free42-nologo-1.4.77/000755 000765 000024 00000000000 12110237251 014624 5ustar00thomasstaff000000 000000 free42-nologo-1.4.77/build-gtk000644 000765 000024 00000000540 12072214253 016434 0ustar00thomasstaff000000 000000 #!/bin/sh -e unset BCD_MATH cd gtk make cleaner make -e AUDIO_ALSA=1 make clean make -e BCD_MATH=1 AUDIO_ALSA=1 cd .. rm -rf Free42Linux mkdir Free42Linux cp gtk/README Free42Linux cp gtk/free42bin Free42Linux cp gtk/free42dec Free42Linux strip Free42Linux/free42bin strip Free42Linux/free42dec tar cvfz Free42Linux.tgz Free42Linux rm -rf Free42Linux free42-nologo-1.4.77/common/000755 000765 000024 00000000000 12110237247 016121 5ustar00thomasstaff000000 000000 free42-nologo-1.4.77/COPYING000644 000765 000024 00000043254 12110237250 015666 0ustar00thomasstaff000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. free42-nologo-1.4.77/CREDITS000644 000765 000024 00000004042 12110237247 015651 0ustar00thomasstaff000000 000000 Free42 is (C) 2004-2013 by Thomas Okken, except: Decimal math: BCD20 library by Hugh Steers. INTEG: Romberg integration implementation based on code by Hugh Steers. LU Decomposition and back-substitution code, and Ridders' root-refinement algorithm, based on sample code from "Numerical Recipes in C". Binary implementations of floating-point math routines, used in all binary builds, developed at SunPro, a Sun Microsystems business. Skins designed by, or based on original work of: Andy Hong, Christoph Giesselink, cjw, Curtis Cameron, David Geiger, Denis Cesar, Egan Ford, Erik Ehrling, Guenny, Jeff O., Javier Goizueta, Jerrod Hofferth, Keith Carangelo, Klaus Reckermann, Michael Khor, Michael Vogel, Miguel Toro, Noe Ramos, Sebastien Carlier, Sven from Vienna, and Thomas Lienhard. MacOS X Dashboard version by D. Jeff Dionne. The iPhone version's embedded HTTP server uses Minizip 1.01h, a zlib-based zip creation and extraction package. Minizip is (C) by Gilles Vollant. Testing: Vini Matangrano, Dave Johnson, Walter Bonin, Andres Rodriguez, Jeff O., Miguel Toro, Les Wright, and many others. Free42 has benefited tremendously from, and probably wouldn't even exist today if it weren't for, these excellent pieces of Free Software: PRC-Tools by D. Jeff Dionne, with multi-segment debugging support by Ton van Overbeek; Peal by Greg Parker; Vim by Bram Moolenaar; DDD by Dorothea Luetkehaus and Andreas Zeller; NetPBM by Jef Poskanzer and others; gcc/g++ by Richard Stallman and others; gdb, binutils, and too many other fine tools to name, by the Free Software Foundation and its contributors; and the Linux kernel by Linus Torvalds and others. Many thanks to all concerned for the help and advice I received from many contributors to several Usenet newsgroups and various mailing lists. And of course, kudos to the inspiration for it all, the excellent HP-42S calculator by Hewlett-Packard. A big and continuing Thank You to all the many people whose names I did not record, who have sent me bug reports, words of encouragement, and donations. free42-nologo-1.4.77/gtk/000755 000765 000024 00000000000 12110237251 015411 5ustar00thomasstaff000000 000000 free42-nologo-1.4.77/HISTORY000644 000765 000024 00000275645 12110237250 015732 0ustar00thomasstaff000000 000000 17-02-2013: release 1.4.77 * iPhone version: the links in the About view now actually work. * iPhone version: implemented 'New directory' and 'Delete' in the HTTP Server. * iPhone version: implemented zip file upload and download in the HTTP Server. * iPhone version: implemented local program import/export. * iPhone version: implemented print-out window. 06-01-2013: release 1.4.77 * All versions except iPhone: the links in the About boxes now actually work. 01-01-2013: release 1.4.76 * Mac version: implemented print-out window. 17-12-2012: release 1.4.75b * iPhone version: fixed three issues with the HTTP Server (displaying the wrong IP address, binding to the network incorrectly, and a crash when writing to the on-screen log). 04-12-2012: release 1.4.75a * iPhone version: clicking Done on the HTTP Server window could cause the app to freeze under certain circumstances. Fixed. 24-11-2012: release 1.4.75 * Android version: implemented haptic feedback option (vibrate on keypress). 27-10-2012: release 1.4.75 * iPhone version: in the HTTP Server view, tapping on the server URL switches between the DNS name and the IP address. * GTK version: now uses ALSA to play BEEP and TONE sounds (but not when running on remote displays). 29-07-2012: release 1.4.74b * Android version: now writes state file when moved to background. 06-05-2012: release 1.4.74a * Android version: fixed a couple of bugs that could cause crashes while printing. 05-05-2012: release 1.4.74 * Decimal version: the overflow fix in 1.4.73 was incorrect. This one works. 05-05-2012: release 1.4.73 * iPhone version: now writes state file when moved to background. * OFF now refuses to shut down Free42 if there have been no keystrokes since the application was started. It will stop program execution and display the message "Suspicious OFF" instead. This prevents code like LBL "OOPS" SF 11 OFF GTO "OOPS" from locking the user out. * Decimal version: overflows would return zero in some cases, i.e. 9E9999 ENTER +. Fixed. 15-04-2012: release 1.4.72 * Android version: added "skin smoothing" and "display smoothing" check boxes in the Preferences view. 14-04-2012: release 1.4.71 * HMS+ and HMS- would return results with the wrong sign under certain circumstances: result < 0 and |result| < 59 seconds, or result < 0 and |result| > maximum integer. Fixed. 09-04-2012: release 1.4.70b * Android version: fixed app manifest so the app no longer obtains the READ_PHONE_STATE permission, which it never needed anyway. Some background: Apps targeted at Android 1.5 automatically get READ_PHONE_STATE and WRITE_EXTERNAL_STORAGE when installed in Android 1.6 or higher, without the user being told, and this tends to look under-handed to users who aren't aware of what's going on. Free42 now targets 1.6, which means it doesn't get any permissions silently. It does now request WRITE_EXTERNAL_STORAGE, because it needs it for printing to files and for exporting programs. Note that the app still runs on 1.5 and later; the requirements for running it have not changed. 12-02-2012: release 1.4.70a * Android version: fixed app manifest to make the app work with screens other than 320x480. This should fix the problem of skins not filling the whole screen on some devices. 18-09-2011: release 1.4.70 * Fixed several bugs in complex ASIN, ASINH, ACOS, and ACOSH. * Android version: better printer icon in the main menu, by Günter Schink. * Mac version: OFF didn't work when invoked from a program. Fixed. 11-06-2011: (no new code release) * Added two new skins to the Android skins package, designed for the Samsung Galaxy S 9000, which has an 800x480 screen. Contributed by Günter Schink. 22-05-2011: (no new code release) * Added Free42AndroidSkins.zip package, for skins designed for Android-based devices; added HTC4800 skin for HTC Desire HD or other Android devices with 480x800 screens. Skin contributed by Michael Vogel, based on an iPhone skin by Jerrod Hofferth. 22-04-2011: release 1.4.69 (Android only) * Added "orientation" option in Preferences, allowing the user to lock the app in landscape or portrait mode. * Improved the layout of the file selection and Preferences dialogs. * The file selection dialog no longer disables files that don't match the filter; it now hides them. 20-04-2011: release 1.4.68 * INVRT did not perform any type checks, leading to badness when it was applied to anything that wasn't a matrix. Fixed. 10-04-2011: (no new code release) * Added six new skins to the iPhone skins package. Contributed by Javier Goizueta. 13-03-2011: release 1.4.67 * ASIN returned incorrect results for large complex arguments. Fixed. * iPhone version: added support for 640x920 skins. * Added Silver And Blue skin to Free42PocketPCSkins.zip package. Contributed by David Geiger. * The SST and BST key labels were switched in these skins: ppcskins/Andy480x800.gif ppcskins/Ramos240x400.gif ppcskins/Ramos480x640.gif ppcskins/SilverAndBlue.gif ppcskins/StandardPPC.gif ppcskins/StandardPPCsm.gif skins/Andy480x800.gif Fixed. 08-04-2010: release 1.4.66 * Removed the "Not Yet Implemented" Time functions; I decided not to implement them. They're also gone from the FCN catalog. The ADATE, ATIME, ATIME24, CLK12, CLK24, DATE, DATE+, DDAYS, DMY, DOW, MDY, and TIME functions remain; those are the time/date-related functions from the HP-41 Time Module that are actually useful in Free42. The functions that I decided not to implement after all are the following: Live clock display: CLKT CLKTD CLOCK Stopwatch: RCLSW RUNSW SETSW STOPSW SW Alarms: ALMCAT ALMNOW XYZALM Clock control: CORRECT RCLAF SETAF SETDATE SETIME T+X * In DMY mode, DATE would display dates as DD:MM:YYYY, but that should be DD.MM.YYYY. Fixed. 03-04-2010: release 1.4.65 * Implemented some more Time Module functions: ADATE, ATIME, ATIME24, CORRECT, RCLAF, SETAF, SETDATE, SETIME, T+X. 30-03-2010: release 1.4.64 * Implemented some more Time Module functions: DATE+, DDAYS, and DOW. 28-03-2010: release 1.4.63 * iPhone version: implemented ACCEL, LOCAT, and HEADING functions, for querying the device's accelerometer, location services (GPS on the 3G and 3GS; WiFi- based on all other models), and compass (3GS only). * All versions except Mac Dashboard: for skins, increased the maximum macro length from 31 to 63. The old limit was too small to allow macros that step forward all the way to the end of the FCN catalog, and supporting such macros is necessary now that the length of the FCN catalog is no longer fixed at 42 rows. * Also, changed several macros in the HP-41 and HP42CY skins where the FCN catalog was being traversed backwards, replacing N repetitions of "up" with 42-N repetitions of "down", so they will work correctly in the presence of an extended FCN catalog. (None of the other skins currently in the Free42 repository traverse the FCN catalog backwards, so they don't need updating.) * PalmOS version: turning off printing did not flush the GIF file, so you'd end up with a truncated image. Fixed. * When COMPLEX is executed with a complex number or complex matrix in X, and POLAR mode is active, it is possible for range errors to occur. Free42 would simply substitute POS_HUGE_VAL in such cases, but that was incorrect; it should report Out of Range or substitute POS_HUGE_VAL depending on the setting of flag 24. Fixed. * R/S can now be pressed using the PC keyboard even when ALPHA mode is active. 07-03-2010: release 1.4.62 * Windows version: now has an option in the Preferences screen to map the Calculator key, found on many newer keyboards, to launch Free42. * iPhone version: the hostname lookup for the HTTP Server window is now done in a background thread, so that a slow or malfunctioning DNS server no longer causes the app to freeze on startup. * When EDITN was active, you could overwrite the edited matrix with a scalar using STO, or delete it using CLV -- and then you would be stuck in the Matrix Editor, and even CLALL couldn't get you out. Fixed; you will now get the Restricted Operation message when you try to change or delete the active matrix behind EDITN's back. 29-12-2009: (no new code release) * Added HP-41 skin to the Free42Skins.zip package. This skin mimics the HP-41 layout, and includes many macros to map additional functions to the keyboard. Contributed by Guenny. 25-11-2009: release 1.4.61 * Decimal version: ->OCT (decimal-to-octal conversion) was broken; it actually converted octal to decimal. Fixed. 11-11-2009: (no new code release) * Added HP42CY skin to the Free42Skins.zip package. This skin includes many macros to map additional functions to the keyboard. Contributed by Guenny. * Added kacskin and kacskin_yellow skins to the Free42Skins.zip package. These are slightly modified versions of Michaels HP, and may be easier to read on some screens. Contributed by Keith Carangelo. 08-11-2009: release 1.4.60 * Decimal version: SQRT could be off by 1 in the final digit. It now computes one additional digit and rounds it into the mantissa, which should make it accurate in all cases. * Decimal version: The BCDFloat add and subtract functions could return denormalized results when one of the arguments was zero. Free42 would not display these denormalized numbers correctly, leading to strange results like 1.0001 LOG returning something that looked like zero but wasn't. Fixed. 31-10-2009: release 1.4.59 * MacDashboard version: changed the background from green to gray, and changed the transparency to fully opaque. * PalmOS Decimal version builds & works again; fixed the SQRT crash and the divide bug (caused by 16-bit integer overflow, oops). * Decimal version: re-fixed the subtract bug which had originally been fixed in 1.4.53, but reappeared in the great BCDFloat/BCDFloat2 refactoring. * Decimal version: 1e-10000 LOG and 1e-10000 LN returned . Fixed. * CLV and PRV didn't work from the CUSTOM menu, potentially even causing a crash. Fixed. 24-10-2009: release 1.4.58 * Removed Byron's BIGSTACK and BIGLCD hacks; these will be maintained in a separate fork from now on. This protects the other Free42 versions from bugs in the 42S/iPhone code, and right now, it fixes some display glitches in the other versions. 19-10-2009: release 1.4.57 * New LOG code (again), for exact results when the argument is a power of 10. The bug from version 1.4.55 is fixed. * Subtract would lose one digit of precision for edge cases like 1 - 0.9999999999999999999999999; fixed. 12-10-2009: release 1.4.56 * The new LOG implementation introduced in Decimal version 1.4.55 was broken; rolling back to 1.4.54 version. (Note that this means that powers of 10 do not yield exact results, but at least everything *else* is OK again now.) 10-10-2009: release 1.4.55 * Some improvements in the new BCDFloat code: . LOG (common log) is now precise for powers of 10. . MOD function now works for large arguments. . rounding fix in multiply now gets 1/3*3-1 and 10/3*3-10 the same answer, 10/7*7-10 is not the same, but this is correct (it depends on where you cut off the number). . optimisations to subtract routine. * Mac version: now uses $HOME/Library/Application Support/Free42 instead of $HOME/.free42 for persistent state, keymap, and skins. If this directory does not exist, but $HOME/.free42 does, the latter is renamed to the former. * Switching from a version using the old BCDFloat code (1.4.51 or earlier) to a version using the new code (1.4.52 or later) wasn't handled properly; it should handle the change in how Infinities and NaNs are represented, but it didn't. This is related to the problem fixed in 1.4.54. Starting with this version, all state files created by versions 1.4.51 and older are assumed to contain old-style BCDFloat, and state files created by versions 1.4.52 and newer are assumed to contain new-style BCDFloat. This means that BCDFloat numbers created by all previous versions of Free42 Decimal will now be interpreted correctly, however, any state file that has become damaged by upgrading from <= 1.4.51 Decimal to 1.4.52-54 will still be damaged, and the only way to make sure no such damage remains is to do CLALL and reload all programs and data. to| <=51| <=51|52-53|52-53| 54 | 54 | >=55| >=55 from | dec | bin | dec | bin | dec | bin | dec | bin ---------+-----+-----+-----+-----+-----+-----+-----+----- <=51 dec| + | + | - | + | - | - | + | + <=51 bin| + | + | - | + | + | + | + | + 52-54 dec| c | c | + | - | + | + | + | + 52-54 bin| c | c | - | + | + | + | + | + >=55 dec| c | c | c | c | c | c | + | + >=55 bin| c | c | c | c | c | c | + | + LEGEND: This chart shows compatibility between Free42 state file versions. To check if version A will correctly handle state files produced by version B, check the cell at column A, row B. The numbers 51..55 in the chart refer to Free42 releases 1.4.51 through 1.4.55. A 'c' means you will get a "State File Corrupt" message; "+" means the state file will be read correctly, and "-" means the state file will be read, but some numbers will be converted incorrectly. Note that once the Free42 state has become damaged by an upgrade marked with "-" in this chart, it will stay damaged; so, for example, upgrading from 1.4.51 Decimal to 1.4.52 Decimal, and then to 1.4.55 Decimal, may result in a damaged state, while upgrading from 1.4.51 Decimal to 1.4.55 Decimal directly will not. 28-08-2009: release 1.4.54 * The new BCDFloat code, introduced in version 1.4.52, uses a different convention for representing NaN and infinities: instead of using special values in the exponent, it now uses dedicated flags, and has a narrower exponent field. The code that converts between decimal and binary, when switching between Free42 Decimal and Free42 Binary while keeping the same state file, did not deal with this new convention, and, as a result, switching between those versions could cause numbers to become NaN or Infinity when they shouldn't. This release fixes this. It is only relevant to those who switch between Free42 Decimal and Free42 Binary. 17-08-2009: release 1.4.53 * Decimal version: fixed a bug in the subtract routine that could cause incorrect, malformed results in some cases, e.g. 4 ENTER 3 / 1 - 3 * 1 - returned -:.E-25. 08-08-2009: release 1.4.52 * New: Mac version. This is a native application, not a Dashboard widget; it is functionally identical to the PC versions (Windows, GTK, and Motif). The print-out window doesn't work yet, though printing to files does; everything else is working. The print-out window will be finished in a two or three weeks, barring the unforeseen. I'm releasing the app in this incomplete state because I feel it is stable and usable, and should be a big improvement for those currently using the Dashboard widget. This is a Universal application, so it should run on PowerPC and Intel- based Macs. It requires Mac OS X 10.5 or later. * Decimal version: Merged Hugh Steers' latest BCD code. This provides more accurate transcendentals and some bug fixes. * Also, Hugh improved the error estimate for the Romberg integration code; it now converges more quickly in many cases. * MacDashboard version: now recognizes '*' and '+' on the numeric keypad. 10-07-2009: release 1.4.51 * Changed the homepage URL to http://thomasokken.com/free42/. 29-06-2009: release 1.4.50 * Adding or subtracting two complex matrices, and adding or subtracting a complex matrix in X to or from a complex scalar in Y, would cause memory corruption, resulting in a crash soon after. Fixed. 13-06-2009: (no new code release) * Added "Realistic42s240x400" skin for 240x400 Pocket PC devices (Free42PocketPCSkins.zip package). Contributed by Denis Cesar. 07-06-2009: release 1.4.49 * BASE/ tried to catch division by zero by looking at Y instead of X. Oops! The result was that 0/1 would return "Divide by 0" and 1/0 would cause Free42 to crash. Fixed. * PalmOS version: tall skins can now be selected on all devices, regardless of whether they have tall screens or not. On most devices, this isn't useful, but the tallness check had the effect of suppressing the 320x450 Ehrling42sm skin on the Tapwave Zodiac, which has a 480x320 screen; the result was that you couldn't use that skin even if you switched to portrait mode. 05-05-2009: (no new code release) * Added "Ramos240x400" skin for 240x400 Pocket PC devices (Free42PocketPCSkins.zip package). Contributed by Miguel Toro; based on Ramos480x640 by Noe Ramos. 04-04-2009: release 1.4.48 * Fixed PSE; it no longer freezes the UI for one second, but uses a proper timeout, so event processing can continue, and keystrokes during PSE have the proper effect (of stopping program execution and then being handled normally). 04-03-2009: release 1.4.47 * Another UVEC bug: it should return Invalid Data when the magnitude of its argument (|z| for complex scalars, FNRM for real matrices) is zero. It returned its argument unchanged instead. Fixed. 22-02-2009: release 1.4.46 * UVEC returned Invalid Type for complex scalar arguments. Fixed; it now returns z/|z| (same as the SIGN function for such arguments), like the real HP-42S. * When functions with arguments, e.g. STO, FIX, etc., were entered into a program from the CUSTOM menu, they would get inserted before the current program line instead of after. Fixed. 28-01-2009: release 1.4.45 * ASTO IND ST X was broken; instead of storing the first 6 characters of the ALPHA register into the register or variable pointed to by X, it stored them into the variable pointed to by the first six characters of ALPHA itself. Fixed. 14-01-2009: (no new code release) * Added "Lienhard480x800" skin for 480x800 Pocket PC devices (Free42PocketPCSkins.zip package). Contributed by Thomas Lienhard. * Added "Sven320x240mini" skin for 320x240 Pocket PC devices (Free42PocketPCSkins.zip package). This skin only shows the keys for basic arithmetic and stack manipulation, making the keys large enough to use easily without a stylus. Contributed by Sven from Vienna. * Added "Ramos480x640" skin for 480x640 Pocket PC devices (Free42PocketPCSkins.zip package). It has a distinctive, sparse look. Contributed by Noe Ramos. 16-08-2008: release 1.4.44 * Made auto-repeat for number entry and ALPHA modes optional. The auto-repeat feature, introduced in release 1.4.11, is not found on the real HP-42S, and can cause problems if you don't expect this kind of behavior in a calculator. It can now be turned off in the Preferences dialog, though the default is still for it to be on. Note that this is unrelated to the auto-repeat for the 'up' and 'down' keys, which is authentic HP-42S behavior, and cannot be turned off. 12-04-2008: release 1.4.43 * MacDashboard version: fixed event handling so that program execution now works. There's still plenty of room for improvement: key timeouts (so pressing and holding a key behaves like it does on the real HP-42S and on the other Free42 versions) and auto-repeat come to mind... But at least programs, the solver, the integrator, and interruptible matrix functions should all be usable now. 10-03-2008: release 1.4.42 * Pocket PC version: I added a HI_RES_AWARE resource so that Free42 will now be able to take advantage of high-resolution displays without the help of Tweaks2k2 or similar utilities. 11-01-2008: (no new code release) * Added "Khor" skin for Windows and Unix versions (Free42Skins.zip package). Contributed by Michael Khor. 01-01-2008: release 1.4.41 * PalmOS version: when opening the print-out database fails with a 0x0209 error (database corrupt), it now deletes the database, creates a new one, and tries again. 15-12-2007: release 1.4.40 * The 'Paste' command now handles binary, octal, and hexadecimal values when the calculator is in the respective base modes. Note that 'Copy' has supported non-decimal bases since times immemorial, so this simply makes clipboard handling consistent at last. 15-12-2007: release 1.4.39 * Windows version: the single-instance option did not work when the previous instance was minimized. Fixed. 14-12-2007: release 1.4.38 * Windows, GTK and Motif versions: implemented single-instance option. This is on by default in the Windows version, but off by default in the GTK and Motif versions, since many X11 window managers won't let clients raise themselves. (PalmOS is single-tasking, and Pocket PC enforces single-instance behavior for all applications, so this option does not apply to those environments.) 08-12-2007: release 1.4.37 * Changed the homepage URL to http://free42.sourceforge.net/. 15-08-2007: (no new code release) * The Motif version did not compile under Ubuntu 64-bit; its version of g++ considers the loss of precision that occurs when casting a void* to an int to be a fatal error, and motif/shell_main.cc used such casts in two places (those casts were legit -- not a bug!). I fixed shell_main.cc so that it first casts to a long, then to an int: the loss of precision when casting between integer types is not frowned upon, so this should compile anywhere with no errors or warnings. I'm only updating the free42.tgz package, and not releasing any new executables, since this code change does not actually affect any of the existing builds. 28-07-2007: (no new code release) * Added "Ehrling42ss240sq" and "SemiReal240sq" skins for 240x240 Pocket PC devices (Free42PocketPCSkins.zip package). * Added information about keyboard mapping to the README files for Pocket PC, Windows, Motif, and GTK. 12-05-2007: release 1.4.36 * PalmOS 68k version: under PalmOS < 3.2, importing and exporting programs would cause a crash, because I was using a Progress Manager API that did not yet exist in those versions. Fixed now; as it turns out, I never needed that newer API in the first place; the PalmOS 3.0 version does the job just fine. 06-05-2007: release 1.4.35 * Binary version: AIP would mangle some numbers; the underlying function to extract the digit in a number's units position was broken. Fixed. * Decimal version: Modified BCDFloat::_usub() so that it performs the subtraction using an extended (8-digit) mantissa. This fixes the behavior of borderline cases like 0.9999999999999999999999999 - 1 (used to yield 0, now -1e-25) and 1 - 1e-25 (used to yield 1, now 0.9999999999999999999999999). 04-04-2007: (no new code release) * The "Standard" skin for the PalmOS version (Free42PalmOSSkins.zip package) had the labels for SST and BST switched. Fixed. 01-04-2007: release 1.4.34 * SOLVER: when the Ridders algorithm made no further progress, because the interpolated value coincided with one of the interval's endpoints, it would use the interpolated value as the final estimate of the root, without checking if it was in fact the best estimate; the best estimate can sometimes be the opposite endpoint. Added a check to make sure the best estimate is returned. * GTK and Motif versions: now set the WM_WINDOW_ROLE property on all top-level windows, to allow window managers to use different policies on the various windows depending on their role. This property is set to "Free42 Calculator" for the main window, "Free42 Print-Out" for the print-out window, and to "Free42 Dialog" for all dialogs. 23-03-2007: release 1.4.33 * PalmOS ARM version: would not run on some Palm Tungsten T models, because they report an incorrect ROM version number. Made the version number check a bit more lenient so that it will accept "Development" versions as well as "Release" versions. 20-03-2007: (no new code release) * Added "StandardPPCsm" skin for 240x240 Pocket PC devices (Free42PocketPCSkins.zip package). 27-02-2007: release 1.4.32 * When the BASE application was active, the arithmetic keys and +/- would be reassigned the BASE+, BASE-, BASE*, BASE/, and BASE+/- functions -- which is good, but these assignments would stay in effect even if a non-application menu was active on top of BASE, and that was wrong. Fixed now. 18-02-2007: release 1.4.31 * Decimal version: Floating-point addition would return an incorrectly rounded result under certain circumstances. Fixed. 14-02-2007: release 1.4.30 * INTEG reported the difference between the final iteration and the one before in the Y register (the error estimate), but without taking the absolute value first. As a result, the reported accuracy could be negative. Fixed. 27-01-2007: release 1.4.29 * Decimal version: did not handle numbers without mantissa correctly while importing programs (i.e. lines like "E4"; see the notes for release 1.3.5). The binary version has handled this kind of thing correctly since release 1.3.5, but the decimal version did not. Fixed. 06-01-2007: release 1.4.28 * INTEG used to treat ACC as an absolute error bound, but that is not consistent with the behavior of the real HP-42S. Fixed; it is now treated as a relative error. 11-11-2006: (no new code release) * Added "Realism" skin for high-density (320x320 and higher) PalmOS devices (Free42PalmOSSkins.zip package). Contributed by Curtis Cameron. 21-08-2006: release 1.4.27 * The int2string() function did not handle numbers with more than 9 digits correctly. This caused MEM to display gibberish on machines with more than 999,999,999 bytes of available physical memory. Fixed; also, the code now interprets free memory as an unsigned number, rather than signed, so that free memory up to 4,294,967,295 bytes should now be reported correctly. 11-07-2006: release 1.4.26 * PalmOS ARM version: Paste didn't work; it caused a Fatal Exception instead. Fixed. 21-06-2006: release 1.4.25 * Motif version: now gets the selected items from the Export Program dialog using the XmListGetSelectedPos() function, rather than using the selectedPositionCount and selectedPositions resources; those resources are a Motif 2.0 feature, and are not supported by the Lesstif library. With this change, Export Program now works with Lesstif as well as with OpenMotif. 19-06-2006: release 1.4.24 * Under certain conditions, SOLVE would return not the final value in X, but the previous one (which is also returned in Y). Fixed. * Pocket PC version: now building the CAB files using the Cabwiz.exe from the PPC 2002 SDK, instead of the PPC 2003 SDK. Hopefully this will fix the problem of Free42 not installing properly on some PPC 2002 devices. 09-06-2006: release 1.4.23 * PalmOS version: fixed a bug that would cause reading the state file to fail if there were no variables. * GTK version: the position of the print-out window would not be remembered if it was closed when the application exited. It appears that the gtk_window_get_position() function returns the original postition (from the window's geometry hints) if it is called while the window is not mapped. I'm now working around this by making sure I get the window position while the print-out window is actually mapped. * Windows version: Show Print-Out did not work when the Print-Out window was minimized. Fixed. * Decimal version: changed the definition of POS_HUGE_PHLOAT and NEG_HUGE_PHLOAT to have a mantissa with 25 nines, rather than 28. (These are the numbers that are returned when a range error occurs while flag 24 is set.) * CLALL did not clear the CUSTOM menu and the programmable menu. Fixed. * STO+, STO-, and STO/, where the destination was the indexed matrix, would reset I and J, but that was wrong (only STO and STO* should do that, since only they can actually cause the destination's dimensions to change). This caused the MLR program from the HP-42S Programming Examples and Techniques book to return incorrect results. Fixed. * In HEXM and OCTM modes, SHOW should display X in decimal, but it showed it in the selected base instead. On the real HP-42S, SHOW only shows the number in base in BINM mode, and in decimal in all other modes. Fixed. 22-05-2006: release 1.4.22 * Decimal version: numbers are always rounded to 25 decimal digits now. The 7- digit base-10000 representation, which Free42 uses internally, is equivalent to 25 to 28 decimal digits, but the extra digits tend to introduce noise which can interfere with the convergence tests of certain iterative numerical algorithms. Rounding to 25 decimal digits makes Free42 behave like a regular decimal calculator, preventing such problems. * GTK version: explicitly realizes the print widget now, which prevents GTK warnings being written to the console when print instructions are performed before the print-out window is first opened. 20-05-2006: release 1.4.21 * Decimal version: for values < 1e-8, SQRT returned results that were a factor 10000 too large. This was due to a bug in the underlying BCDFloat::sqrt() method, which probably affected other functions as well. Fixed. 19-05-2006: (no new code release) * Added "HP-42C" voyager-style skins for Windows and Unix (Free42Skins.zip package). Contributed by Egan Ford. 11-05-2006: release 1.4.20 * GTK version: added periodic battery check (once per minute). The other versions already had this. Note that the GTK and Motif versions use /proc/apm to inspect battery status; on systems without /proc/apm, this check does not work, and the battery is assumed to always be OK. /proc/acpi support is not implemented yet. * All versions except PalmOS: the keyboard handling changes in release 1.4.17 broke auto-repeat from the PC keyboard. Fixed. 08-05-2006: (no new code release) * HP-41 to HP-42S character code conversion in the rom2raw program did not work; alpha LBL, GTO, XEQ, and string program lines were not converted. Fixed, and updated the free42.tgz and rom2raw.zip packages. 07-05-2006: release 1.4.19 * GTK version: auto-start (flag 11) did not work. Fixed. * Auto-start did not work if the program counter was at line 00. Fixed. * When auto-start causes program execution to begin while PRGM mode is active, any pending command should be cancelled and PRGM mode should be exited before program execution starts. This wasn't handled properly; fixed now. 01-05-2006: release 1.4.18 * Decimal version: fixed a bug in exp() that caused it to return wrong results for very large negative parameters. This affects E^X, SINH, COSH, and TANH. * Decimal version: fixed a bug in TANH that caused it to return NaN or other nonsense for very large parameters. * Decimal version: fixed a bug in the BCD20 library that caused -Infinity not to be recognized as a negative number. 01-05-2006: release 1.4.17 * Pocket PC version: fixed a couple of cosmetic bugs in menu key highlighting. * Added support for CShift mappings. These are keyboard mappings that take effect if the Calculator's Shift annunciator is on. This makes it possible for [CShift] [Key] to activate a different keystroke or macro than the unshifted [Key], so you can, for example, make CShift SQRT be something other than X^2. * Display repainting is temporarily disabled during macro playback, to prevent distracting "flashing" for complex macros. 23-04-2006: (no new code release) * Added 160x160 version of the Standard skin to the Free42PalmOSSkins.zip package. 22-04-2006: release 1.4.16 * Complex matrices took up twice as much memory as they actually needed, due to an erroneous extra multiplication by 2 while allocating the memory chunk for the matrix elements. Fixed. * Shared matrices now remain shared even after application exit and restart. * PalmOS version: removed the 64 kilobyte matrix size limit. Matrix size is now limited only by available application heap space. (On PalmOS < 3.5, the 64k limit still exists.) 13-04-2006: release 1.4.15 * Windows and Pocket PC versions: when print commands were executed before the print-out window was created, the scroll-back buffer would not grow properly. Fixed. 11-04-2006: release 1.4.14 * PalmOS version: tweaked the layout of the Preferences dialog a bit. The version for PalmOS < 3.5 was particularly bad. * PalmOS ARM version: Export Program would only export the first program in memory, and write garbage (or even freeze) for any others that were selected. Fixed. 10-04-2006: release 1.4.13 * New: ARM-native PalmOS version. It's not for everyone -- it's big, it starts up much slower than the 68k version, and of course you need a Palm with an ARM CPU -- but once it gets going, it's pretty fast! Note that the ARM state file is not compatible with the 68K version, so whenever you switch from the 68K version to the ARM version or back, you will get the "State File Corrupt" message. Be sure to export all important programs before switching! * Implemented instance caching for scalars and strings. This eliminates the memory management overhead from most simple instructions and speeds up program execution. 04-04-2006: release 1.4.12 * Pocket PC version: implemented proper beeper. 04-04-2006: release 1.4.11 * Implemented auto-repeat for number entry and ALPHA modes. * In the previous release, I broke the annunciators in the Standard skin for the Pocket PC. Fixed. 03-04-2006: release 1.4.10 * Pocket PC version: made orange (shifted) legends in the Standard skin a bit larger. The original 5x4 characters were way too small; hopefully the new 7x5 characters will be more to people's liking. * Tweaked the PalmOS version of the Ehrling42sm skin a little: I increased the vertical key spacing by 4 pixels. It has a more balanced appearance now; should be nicer to use, too. * PalmOS version: changed the display background color in the Ehrling42sm, SemiReal42, SemiReal42HD, and SemiAuto42HD skins, from LCD greenish to gray. The reason is that the standard PalmOS 8-bit palette does not contain the right shade of green, so in the skin bitmaps, the color ended up becoming gray anyway; displaying the actual LCD contents on top of it (which happens in true color on devices that support it) then gives an ugly appearance. I modified skin2prc so that it can now also generate true-color (16-bit) skin bitmaps, but those tend to get pretty big, so I'm not using that functionality yet while building the Free42PalmOSSkins.zip package; maybe later, I'll create separate true-color versions of Ehrling42sm etc. to keep the fans of green happy... 02-04-2006: release 1.4.9 * PalmOS version: optimized display updating code. For high-density skins, display updates are now about 3 times faster than before; this should make HD skins much more usable (more responsive). * PalmOS version: added support for tall (320x450) skins. * Added new "realistic" 320x450 skin to Free42PalmOSSkins.zip package. It's not great; just a quick-and-dirty, proof-of-concept adaptation of Ehrling42sm from the Windows/Unix skin collection. 01-04-2006: release 1.4.8 * Changed implementations of X<=Y? and X>=Y? so they now actually return x<=y and x>=y, respectively; formerly, they returned !(x>y) and !(x NaN, etc. Fixed. * Fixed BCDFloat::sub() so that it will not return -0. It used to do that when computing 0 - 0. I think this was harmless, but I fixed it anyway. * Added new Pocket PC skin with single-click activation of shifted functions to Free42PocketPCSkins.zip package. Contributed by Jeff O. 26-03-2006: release 1.4.6 * New: Pocket PC version. * Added a new voyager-style skin to the Free42Skins.zip package (Windows and Unix), and created a new package, Free42PocketPCSkins.zip, with skins specifically designed for the Pocket PC. * ->POL now detects the case that the real and/or imaginary parts are zero, and returns exact results for those cases. * Canceling a partially-entered DEL, LIST, or PRP command in PRGM mode could cause the instruction pointer to be set to an invalid location. Fixed. 19-03-2006: release 1.4.5 * Decimal version: 1 ACOS returned instead of 0. Fixed. 13-03-2006: release 1.4.4 * GTK version: when initially presented, the print-out window was not automatically scrolled to the bottom sometimes. Fixed. * GTK version: when the print-out window's scroll-back buffer was full, and the window was not scrolled to the bottom, and print-out took place, the window would get updated wrong. Fixed. * In TRACE mode, while running a program, clearing flag 21 should not suppress auto-print, but it did. Fixed. * The "Paste" command no longer uses sscanf() to parse real or complex numbers; instead, it now uses the same string-to-number conversion code that is used when the user enters a number on the keyboard. This means that numbers like 0.1 can now be pasted in the Decimal version with no binary round-off error. 12-03-2006: release 1.4.3 * GTK version: refused to close the print-out window. Fixed. 11-03-2006: release 1.4.2 * New: GTK+ version. For Unix users who think Motif is not cool enough. ;-) * Fixed handling of large complex arguments in ABS and SIGN. * Decimal version: fixed a bug in BCDFloat addition and subtraction that could cause the result to be rounded wrong under certain circumstances. * Some minor maintenance stuff: changed the type of BASE (in bcdfloat.h) from 'long' back to 'int' in order to make the PalmOS code slightly more efficient; removed the -O2 option from the MinGW makefile, because the optimizer was breaking the solver. * Fixed a bug that could cause the PalmOS version to fail while converting programs from decimal to binary. 03-03-2006: release 1.4.1 * Decimal version: fixed a bug in BCDFloat addition and subtraction that could cause the result to have the wrong sign under certain circumstances. This seems to have fixed the poor SOLVE performance; apparently that was *not* caused by transcendentals not being evaluated to the full 28 digits. * PalmOS version: when entering text using Graffiti, case switching is now performed even on Graffiti 2. 27-02-2006: release 1.4 * Added decimal floating point, implemented using BCDFloat code by Hugh Steers. The decimal code is a compile-time option; I can (and will!) still build Free42 with binary floating point as well. * Removed "IP Hack" option. It never really did the job anyway, and with the introduction of the Decimal version, it is now obsolete. * VIEW, AVIEW, and ADV now talk to the printer when invoked from the keyboard, even if flag 21 is clear. * In TRACE mode, when the X register is printed automatically (not because of an explicit PRX), and it contains a matrix, print only the matrix descriptor, not its whole contents. * Fixed handling of large complex arguments in LOG and LN. * Windows version: the default location for the Free42 directory is once again the executable's directory. This change only affects new installations; the Free42 directory location is still stored in the Registry, and any already existing setting will be honored. * On hard reset, the printer is enabled now (flags 21 and 55 are set instead of cleared). This is not what the real HP-42S does, but, then again, the real HP-42S does not have a built-in printer, while Free42 does; this new behavior should be more intuitive. (Note: flags 21 and 55, and the PRON and PROFF commands, still work as before; the only change is their initial setting.) * Fixed a crash that could occur when ridiculously large numbers were used for indirect addressing (meaning numbers >= 2147483648 absolute). 10-02-2006: release 1.3.17 * Clicking in the display now only activates softkeys if a menu is active. This means that programs that "fake" menus by painting in the display and calling GETKEY are no longer supported -- but then again, you can still use the top six keys of the keyboard in that case, and for all I know there is only one program on the whole planet that actually does a fake menu (hi Erik :-) ). * Code import now converts "" (empty string, a synthetic HP-41 construct) to |-"", instead of dropping it on the floor. This is consistent with the behavior of "" on the 41: it is a NOP, sometimes used after DSE or ISG, in cases where the 'skipping' behavior of those functions is not wanted. * In lowercase print mode (flag 13 set), functions with hidden characters, e.g. COMPLEX/CPLX, were printed wrong ("cOMplEx" instead of "complex"). Fixed. * Added rom2raw.c to the source tree. This is a program that extracts user code from *.ROM format HP-41 module images; it converts XROM calls to user code within the same module to XEQ, and warns about all other XROMs (except the ones that correspond to HP-42S instructions). At some point it would be nice to integrate module support into Free42 itself; meanwhile here's hoping this will also be useful. I'll put it on my web page as a separate download, complete with a ready-to-run Windows executable, once I iron out the wrinkles. * Added KR and Widgi42 skins (for the Motif & Windows versions). Contributed by Klaus Reckermann. 07-02-2006: release 1.3.16 * A few obscure printing fixes (PRX now prints complex scalars left-justified if they do not fit on one line; the same now happens for command printing in NORM and TRACE modes). * Modified most skins so all the sensitive rectangles are now as large as possible, making keys easier to hit with the mouse. (The sensitive rectangle for a key is the area on the screen where a mouse click will cause the key to be pressed; in several skins this area was the size of the keys themselves, but after this modification, the area extends several pixels beyond the keys, meaning you can be a bit sloppier now, which is good because it reduces strain on the arm muscles and tendons.) I did not change the Semi-Auto skins yet, because my automatic rectangle- stretching program currently only handles 37-key layouts. Fortunately, the Semi-Auto skins already have large sensitive rectangles, which cannot be enlarged much further anyway. (I know, lame excuse!) * In skins/SemiReal42.gif and palmskins/SemiReal42HD.gif, moved the Rv key one pixel to the left. It's been annoying me forever -- can't think why I didn't fix this earlier. :-) 06-02-2006: release 1.3.15 * The PRX command no longer prints three asterisks when printing a complex number or a matrix. Also, when printing a matrix, in addition to printing the descriptor line (as shown in the display), it now prints the matrix contents as well. These changes bring the Free42 PRX command in line with the HP-42S. * Windows & Motif versions: fixed handling of PC keyboard key rollover (i.e. pressing a key before the previous key is released). This should fix the problem of keystrokes being missed when typing rapidly. * Windows & Motif versions: when the A..F menu in the BASE application is active, the A through F keys are now mapped to the corresponding menu keys, regardless of the keyboard map -- so you can now enter a number like FCE2 without having to use the mouse or the F1-F6 keys. * Added MacDashboard version to the source distribution. No binary distribution, alas, because I don't have a MacOS X build environment. MacDashboard code written by D. Jeff Dionne. 01-02-2006: release 1.3.14 * Windows version: added always-on-top feature. * Windows version: in the previous release, when I fixed the code that saves the window positions when exiting using the OFF command, I broke the code that saves the window positions when exiting using the close box and the Quit command. Fixed, so now window positions should be remembered correctly regardless of how Free42 is exited. 29-01-2006: release 1.3.13 * Fixed behavior of Y^X for certain special cases (namely, 0^(0,0); (0,0)^0; (0,0)^(0,0); 0^(1,0); (0,0)^(1,0). Note that the first three of those now return (1,0); this is not consistent with the real HP-42S, which returns "Invalid Data", but it is consistent with the way Free42 already treated 0^0). * SIN, COS, P->R, and COMPLEX, when used in DEG and GRAD modes, now return exact results for multiples of 90 degrees (100 grads). Also, the TAN function now returns exact results for multiples of 45 degrees (50 grads). * Added range checks to the complex versions of SIN and COS. * In PRGM mode, the A..F command in the BASE menu now says "Restricted Operation". * Windows version: when exiting the emulator using the OFF command (shift-EXIT or XEQ "OFF"), the positions of the main and print-out windows would not get updated in the state file. 20-06-2005: release 1.3.12 * Motif version: closing the print-out window did not work in the Cygwin build. The problem appears to be in the Cygwin Lesstif library: the XmUNMAP setting for the deleteResponse resource in TopLevelShell does not work. I now work around this by using the XmDO_NOTHING setting instead, and calling XWithdrawWindow() explicitly in the WM_DELETE_WINDOW protocol handler. 19-06-2005: release 1.3.11 * DOT did not drop the stack; it placed the result in X but left Y intact. Fixed. 14-06-2005: release 1.3.10 * PalmOS version: in the file selection dialog, sorting was broken. It sorted the volume list, which it shouldn't, and when sorting directory listings, it sorted the names but not the types, and it did not force directories to the top of the list. Oops! So many bugs in such a trivial enhancement... Well, it's fixed now, and I'm taking a vacation from this project. :-) 14-06-2005: release 1.3.9 * PalmOS version: in the file selection dialog, file names are now sorted alphabetically. 09-06-2005: release 1.3.8 * PalmOS version: Added Copy and Paste icons to the command bar for the calculator form. * PalmOS version: The Edit and Skin menus were switched in the print-out form. Fixed. 12-05-2005: release 1.3.7 * PalmOS version: hardware Up and Down keys now scroll the print-out window. 12-05-2005: release 1.3.6 * Windows version: fixed computation of scroll bar width, so Windows 2000 users who use "Large Fonts" (or otherwise wider-than-normal scroll bars) don't lose pixels at the right edge of the print-out window. * PalmOS version: pressing and holding the 5-way navigator's center button now exits to the Application Launcher (this is an automatic PalmOS behavior, which I had unintentionally sabotaged before by mis-handling auto-repeat events). 11-05-2005: release 1.3.5 * Improved import of HP-41 programs: numbers with exponent but no mantissa are now handled correctly (e.g. 'e4' becomes '1e4', rather than '0'); XROM instructions that do not correspond to HP-42S built-in functions are now imported as XROM instructions, rather than being skipped. Note that XROM instructions always return the "Nonexistent" error message, so HP-41 programs that rely on non-HP-42S extensions still won't actually work in Free42, but at least now you will be able to see more easily *why* such programs fail. * PalmOS version: PalmOS 5.0 (and 5.1?) has a bug in FileControl(): the fileOpGetOpenDbRef command returns a bogus value; when using this value in subsequent Data Manager calls, a crash results. This was noticed on a Palm Tungsten T and on PalmOS Simulator 5. This bug caused Free42 to crash on exit on some devices (it would crash while trying to set the "backup" bit on the Free42State file). I changed my code so that it obtains the necessary information another way. * Motif version: fixed a bug that could cause a crash whenever a file selection dialog was created. I never noticed it causing a problem in Linux, but it did crash the Cygwin build, and it was incorrect code anyway. Fixed now. 21-04-2005: release 1.3.4 * PalmOS version: yet another 'program import' bug, this time due to a difference of opinion between gcc and g++ about sign extension. Fixed. 21-04-2005: release 1.3.3 * PalmOS version: program import and HP-41 synonym lookup were broken, again apparently because the PalmOS version of g++ does not handle const globals properly. I removed the 'const' qualifier from a few lookup tables and that seems to have solved the problem. 21-04-2005: release 1.3.2 * PalmOS version: added key click sound, to give the user audible feedback when Free42 receives a key press (pen tap) on the emulated keyboard. * Added skins with single-click activation for shifted keys (Windows/Motif and high-density PalmOS versions). Contributed by Jeff O. * Added compact version of Erik Ehrling's realistic skin (Windows/Motif only). Contributed by Michael Vogel. 06-04-2005: release 1.3.1 * In the previous release, I broke the FCN catalog in the PalmOS version. Those pesky 'const' globals again! Oops... 06-04-2005: release 1.3 * Converted the project to C++ in preparation for implementing BCD support. 23-03-2005: release 1.2.10 * The code in ->HMS, HMS+, and HMS-, that was supposed to prevent results such as ->HMS(61/60) = 1.006 (by adding 0.4 to fix the minutes, or 0.004 to fix the seconds, to get 1.01 in this example) was incorrect; it would apply the correction in cases where the intermediate result was rounded down due to binary round-off; this would cause ->HMS(119/60) to return 1.594 instead of 1.59. Fixed by making the test for when to perform the adjustment more accurate. 21-03-2005: release 1.2.9 * PalmOS version: fixed a couple of problems with the hardware Up and Down keys: auto-repeat events are now ignored (Free42 handles that on its own), and SST in RUN mode now shows the instruction to be executed, and goes to NULL, if held down for 1/4 second and 2 seconds, respectively. 21-03-2005: release 1.2.8 * Motif version: file selection dialogs now use option menus for file type selection. The only remaining difference between these, and the Windows and PalmOS versions, is that the Motif version uses case-sensitive matching, so the "*.raw" filter won't match "foo.RAW". You'd think this would be a simple matter of using a custom file search procedure, but there turn out to be weird side effects (with Open Motif 2.2.2, though not with Lesstif, interestingly enough). That'll require some more research. * Motif & Windows versions: did not repaint the skins after skin switching in certain circumstances. I added code to force a full repaint. * Windows version: the message and title on several message boxes were switched. I guess nobody ever saw those messages anyway. :-) * Did some serious spring cleaning in my TODO list. There were a lot of goals there, which, to my mind, were just too much effort for too little gain. At this moment, BCD and Pocket PC support are my only *serious* remaining objectives, apart from continuing to fix bugs of course. Maybe I will reconsider some of the deleted objectives eventually, but that's unlikely to happen before the gloomy days and long nights of winter return (oh, how conducive they are to the coding mind-set!). I'll keep them off the TODO list, so as to avoid giving the impression that stuff like a larger display or algebraic equations are actually planned and likely to arrive sometime soon, and also to give myself the satisfied feeling of "not much left to do". :-) 20-03-2005: release 1.2.7 * Windows version: could not write the registry value to store the Free42 directory location (HKEY_CURRENT_USER\Software\Thomas Okken Software\Free42 \HomeDir) unless the parent key existed already -- which, unless you have the Free42 Conduit for the PalmOS version installed, it won't! Oops! Fixed now. 20-03-2005: release 1.2.6 * Windows version: with the ALPHA menu active, some PC keys that generate printable ASCII codes (e.g., shift-equals for '+'; also backslash, period, comma) were not handled properly and went through the regular non-ALPHA mapping, meaning the corresponding characters could not be typed directly. Fixed. * PalmOS version: the hardware "Up" and "Down" keys are now mapped to the emulated "Up" and "Down" keys. * 'Paste' didn't enable stack lift. It does now. * 'Paste' now accepts numbers in the same format that 'Copy' produces, which is the format used on the calculator's display -- so flag 28 controls whether '.' or ',' is the decimal. Thousands separators are ignored regardless of the setting of flag 29. * 61 ENTER 60 / ->HMS now returns 1.01 instead of 1.006. I added a check to see if round-off after the final computation step caused the seconds or minutes to reach 60. I also added this same check to HMS+ and HMS-. * Clicking in the bottom half of the display now activates "soft" (menu) keys. This actually means you now simply have another way of clicking the six keys in the top row of the keyboard, but the visual feedback is different, and some may find it more intuitive than clicking the keyboard keys when working with menus. * Windows version: now allows 'Alt' to be used in keyboard mappings. I'm a bit uneasy about my keyboard message handling code; it took some trial and error before I got to the point where Free42's mappings were coexisting with the standard Windows Ctrl/Alt behavior in the menu bar, and I don't fully understand *why* my code works. Maybe that's just what programming Windows is supposed to be like . Anyway, if it turns out that this code breaks on more recent versions of Windows (I use 98SE myself), I'm sure I'll hear about it, and if so, I'll just rip it out. The code that deals only with Ctrl and Shift seems to have been fine, so I can always fall back on that if need be. * Windows version: now allows the user to choose the location of the Free42 directory (meaning, the directory where the state, print-out, keymap, and skin files are stored). Also, skin files are now looked for in the Free42 directory *and* in the directory containing the executable, so a shared installation of Free42 can also have a shared set of skins. 18-03-2005: release 1.2.5 * Skins now handle multiple keys with the same key code properly: the key that is clicked is the one that is highlighted, instead of the last matching one in the layout description; and when a PC keyboard key is pressed that is mapped to a calculator key for which multiple keys exist in the skin description, the first one is highlighted, instead of the last one. * Macro keys: in the *.layout files, it is now possible to define keys with keycodes in the range 38..255; these keys are mapped using "Macro:" definitions in the same *.layout file. The macro is a sequence of zero or more standard key codes (1..37). * Windows & Motif versions: key mappings can now be defined in the skin *.layout file. The syntax is identical to that used in the keymap.txt/keymap file. When the skin maps a key that is also mapped in the default keymap file, the skin's mapping takes precedence. Note that such customized *.layout files are OS-dependent, since the way PC keyboard keys are specified is different between Windows and Motif: Windows uses numeric key codes, while Motif uses X11 KeySyms. 18-03-2005: release 1.2.4 * PalmOS version: the Delete Skin feature would crash when there were no external skins installed. Fixed. 16-03-2005: release 1.2.3 * DELR, with 'I' pointing to the bottom row, would leave 'I' pointing past the bottom of the matrix, and, when used in EDIT or EDITN mode, display a bogus value in X. When writing a new value to the matrix after this happens, memory corruption could result. Fixed now. 15-03-2005: release 1.2.2 * PalmOS version: added a feature to let the user remove external skins (i.e. skins that aren't built into Free42, but that were installed as separate PRC files). So, now you don't need to use Filez any more to accomplish this task, never mind having to uninstall everything and start all over. * PalmOS version: on devices with Graffiti 1, upper- and lower-case characters are now switched, so that uppercase characters are easier to enter. This was done because uppercase is much more common than lowercase in Free42. On devices with Graffiti 2, case switching is not performed, since there uppercase requires no extra gestures, but can be entered simply by jotting in the center of the input area. * Added two new skins to the Free42PalmOSSkins.zip package, and another to the Free42Skins.zip package. Thanks to Jeff O. for contributing these! 15-03-2005: release 1.2.1 * Windows version: in 1.2, I broke the Skin menu so switching skins didn't work any more. Fixed now. 14-03-2005: release 1.2 * Implemented simple Copy and Paste: 'Copy' puts a text representation of the X register on the clipboard (identical to what you see in the display, except the exponent 'E' in numbers is changed to a plain lowercase 'e'); 'Paste' tries to parse the clipboard contents as a complex number (formatted as "a ib", "a + bi", or "(a, b)", or if that fails, as a real number; if it can't parse the clipboard, it just pastes it as a string. 13-03-2005: release 1.1.19 * Using the keyboard to enter the first character of a LBL name or number caused a crash (all versions). Fixed. * Windows and Motif versions: the 0-9 keys on the PC keyboard now act like the number keys on the virtual keyboard *even* at the beginning of a LBL. This allows the keyboard to be used to enter numeric labels. 13-03-2005: release 1.1.18 * PalmOS version: improved support for PalmOS < 3.5: now has a sound volume control in the Preferences dialog (ugly but functional); and now allows skin switching (using a dialog instead of a dynamic menu). * Motif and Windows versions: implemented keyboard mapping. When Free42 is run, it looks for $HOME/.free42/keymap (Motif) or "My Documents"\Free42\keymap.txt (Windows), and if it doesn't exist, it creates a new one, which provides mappings for all calculator keys and also contains comments explaining the keymap file format. * PalmOS version: now accepts Graffiti input when the ALPHA menu is active. Printable ASCII characters (codes in the range 32..126), and Backspace and Enter, can be entered this way. * Motif and Windows versions: now accept keyboard input when the ALPHA menu is active. Keystrokes that do not correspond to printable ASCII characters (codes in the range 32..126) are handled according to the keyboard mapping (see above). 08-03-2005: release 1.1.17 * PalmOS version: relaxed system requirements to PalmOS 3.0 or later (it used to require 3.5 or later). Note that the oldest PalmOS version this has actually been tested with is 3.3 (so far). Also note that pre-3.5, there are a couple of restrictions: the volume control in the Preferences dialog is missing, because pre-3.5 does not support sliders; the Skin menu only shows one item named "Default", because pre-3.5 does not support dynamic menus (and I have been too lazy to work around that using a dialog box; since pre-3.5 does not have color, I figure there's not that much need for changing skins anyway), and there may be other miscellaneous bits of flakiness (e.g. pen events on menus getting reported to the underlying form as well -- this can lead to surprising behavior). So, bottom line, you can now use Free42 on your trusty old Palm V etc., but I still *recommend* PalmOS 3.5 or later. 07-03-2005: release 1.1.16 * PalmOS version: implemented pluggable skin support. Skins are PRC files that are generated from the same type of *.layout and *.gif files that the Windows and Motif versions use directly. 02-03-2005: release 1.1.15 * SOLVE: When the secant extrapolation makes no more progress (the extrapolated value coincides with one of the previous two 'x' values), we assume we've found a root -- but the code would not necessarily return the right 'x' to the X register. This means that when the iteration was started with one starting value being a root, and the other starting value being way off, the way-off value could be returned in X. Oops! Fixed now. * SOLVE: when the search for a sign change reaches infinity, it would report the final value of X to be POS_HUGE_DOUBLE or NEG_HUGE_DOUBLE. That wasn't really correct, since it would return that value without actually having evaluated the function at that point. I changed it so that it returns the last 'x' that was actually used. * Windows version: the Free42State.bin and Free42Print.bin files are now called state.bin and print.bin, respectively, and they're no longer stored in the directory containing Free42.exe, but rather in "My Documents"\Free42 (this directory is automagically created if necessary). Storing the state under My Documents means multiple users sharing one PC no longer share one Free42 state. * Windows version: implemented pluggable skin support. Each skin consists of a pair of files, named .layout and .gif. Skins are loaded from the "My Documents"\Free42 directory; this is also the directory where Free42 stores its persistent state, starting with this release. * In the "Standard" skin (Standard.gif), 10^X and E^X were swapped. Fixed now. * Motif version: three XtVaSetValues() calls were missing the final NULL parameter, which could cause segmentation faults. Miraculously, this never seems to have done any harm in my Linux/x86 builds, but it did cause a crash under MacOS X, and it's wrong anyway. Fixed now. 27-02-2005: release 1.1.14 * Removed "f42" program file format support. The f42 format wasn't portable: it was just a dump of Free42's in-memory representation of programs, and the way noninteger number literals are stored (as native 'double') was a potential source of trouble, because of endianness and because of format differences (in case anyone is running Free42 on a non-IEEE-754 platform). At present, the format isn't needed anyway, because the Free42 instruction set is identical to that of the HP-42S, so all Free42 programs can be unambiguously encoded using the "raw" format; by the time I start extending the instruction set, I'll have to design a new format (without f42's portability problems!) or just define extensions to "raw", using the same mechanisms that were used to add the HP-42S instructions to the HP-41C instruction set (XROM and special strings). * Because programs are now only stored in HP-42S-compatible "raw" files, there is no longer any point in showing program sizes in Free42 units, so the "HP-42S byte counts" option has been removed from the Preferences dialog, and program sizes are now always shown in HP-42S units. * Pluggable skin support (Motif version only at the moment; this will be added to the PalmOS and Windows versions in the next week or so). 11-02-2005: release 1.1.13 * It wasn't possible to enter FIX/SCI/ENG IND nn when nn > 11; in that case, nn would be changed to 11 -- which is the right thing to do for FIX/SCI/ENG nn, but not for the IND case. Fixed. * In the previous release, I was a bit too zealous about deleting the code that handles ARGTYPE_COMMAND for FCN catalog assignments: I mistakenly removed the code that displays and executes this type of assignment in a program. Oops! It's restored now. Note: it has not been possible to *create* such program lines since release 1.1.5 (see the first item in the release 1.1.5 notes, below), but this code is needed for compatibility with programs created with Free42 release 1.1.4 and earlier. 11-02-2005: release 1.1.12 * SOLVE: if the search for two points with function values with opposite signs reaches infinity suspiciously quickly (in less than 10 steps), and only one initial guess was given, retry with initial guesses of 0 and 1. This can help in some cases where the left-over value in the variable, and the previous starting value, are identical (and misleading), and no new starting value was given before invoking SOLVE. * When entering a LBL command, it wasn't possible to specify the label name by selecting it from a catalog -- the top-level catalog could be brought up, but pressing any of its keys would take you back to the ALPHA menu. * It wasn't possible to enter commands with IND "string" arguments by selecting the argument from the FCN catalog; the IND would disappear in that case. * Fixed a couple of extremely obscure Free42/HP-42S incompatibilities: 1) When a program line like LBL "ENTER" is created by selecting ENTER from the FCN catalog, the label now contains the command name with "meta" characters for the letters that are suppressed in the FCN catalog (e.g., the second E in ENTER) -- so the string ENTER in this example is encoded as 0x45, 0x4E, 0x54, 0xC5, 0x52. This means that, with a label as above, GTO "ENTER", with the word ENTER typed using the ALPHA menu, now results in "Label Not Found", since in that case, the string ENTER is encoded as 0x45, 0x4E, 0x54, 0x45, 0x52, which does not match the label. Note that this does not just apply to LBL and GTO, but in *all* situations where the FCN catalog is used to create a string argument. 2) In ASSIGN, when the command to be assigned is selected from the FCN catalog, it is now assigned by name, and no longer by command ID. This means that such assignments can now change meaning if a label or variable with a matching name is created (as was already the case when the command name was entered by any means *other* than the FCN catalog). 10-02-2005: release 1.1.11 * SOLVE: if the search for two points with function values with opposite signs reaches infinity suspiciously quickly (in less than 10 steps), assume that the larger of the initial guesses is bad, and retry with just the smaller. This can help in some cases where a left-over value in the variable to be solved for can send SOLVE in the wrong direction. 02-02-2005: release 1.1.10 * Another DET problem: the fix in 1.1.9 was itself broken; it would set 'det' to 1 each time the real-valued LU decomposition worker procedure was entered, instead of only doing that the first time; this would not cause a problem if the matrix in question was small enough to be decomposed in only 1 invocation of the worker (12x12 or less), but result in bogus determinants otherwise. Fixed. * Yet another couple of DET bugs: singular matrices weren't handled properly: in "Singular Matrix" mode, they would return the "Singular Matrix" error, instead of 0, and when "Singular Matrix" mode was off, they would return a small value that resulted from the HP-42S-compatible zero-pivot fudging that takes place in that mode (it shouldn't do that fudging when computing only the determinant, regardless of whether "Singular Matrix" mode is on or off). Both of those bugs applied to both the real and complex cases; they're all fixed now. * The "left arrow", "right arrow", "up arrow", and "down arrow" commands for matrix editing (EDIT/EDITN/INDEX) did not work correctly when editing a 1x1 matrix. Fixed. 02-02-2005: release 1.1.9 * DET returned bogus results for real nonsingular matrices -- nasty bug that I introduced when I made the LU decomposition code interruptible in release 1.0.17 (the 'det' field in the LU decomposition state was not initialized to 1 at the start of the decomposition). The complex case did work correctly; the real case does so too, again, now. 31-01-2005: release 1.1.8 * It was possible to enter and leave PRGM mode while in the middle of command entry. Fixed. * It wasn't possible to enter the number -2147483648 as a program line. Fixed. * Flag 47 was set when VARMENU was active, *and* when the variable menus in the SOLVE and INTEG applications were active. It should *only* be set when the regular VARMENU is active. Fixed. * Flags 76 and 77 (matrix edge wrap, and matrix end wrap, respectively) were set wrong by the I+, I-, "up-arrow", and "down-arrow" commands: when the top/bottom edge was crossed, flag 77 would be set (should have been 76), and when it jumped from the top-left corner to the bottom-right (or vice versa), flag 76 would be set (should have been 77). Fixed. Note: J+, J-, "left-arrow", and "right-arrow" did the right thing already. * GETKEY no longer causes the display to be refreshed. * GETKEY now turns off the 'running' annunciator while it is waiting. * If Free42 is exited while GETKEY is waiting (or if the power was cycled (PalmOS only)), it returns code 70 (OFF) and stops program execution, just as if OFF had been pressed twice on a real 42S: once to get out of GETKEY and stop the program, and again to actually switch the calculator off. * PalmOS version: if the system requests permission to perform auto-poweroff, and Free42 refuses while a GETKEY is pending, GETKEY returns 70, program execution continues (if applicable), and the power stays on. (This is how it works on the real HP-42S, too; presumably, the idea is to allow GETKEY-using programs to implement battery-friendly behavior.) 29-01-2005: release 1.1.7 * When XEQ was performed in a program, while the return stack was already full, the previous top-of-stack would be lost (really silly off-by-one error in the code that shifts the stack for this case). I noticed this while playing Erik Ehrling's Okie Dokie: it would start to act weird if you played 8 fields in a row. Oops! 29-01-2005: release 1.1.6 * In store_var(), added a check to see if the target variable is the indexed matrix; if it is, I and J need to be reset to 1 (top-left corner). Without this, the code was not HP-42S compatible, and what's worse, it could leave I and J pointing outside the new array, so that a subsequent STOEL would cause memory corruption. 27-01-2005: release 1.1.5 * When storing ASSIGN in a program, and the function to be assigned was selected from the FCN catalog, the function's opcode would be stored with the instruction, rather than its string representation. As far as Free42 itself is concerned, this is fine, but it is not how the HP-42S works, and what's worse, the "raw" mode export code and the HP-42S-compatible byte counter both couldn't handle it, and generated bogus code and nonsense byte counts. I decided to kill all birds with one stone by forcing the FCN-to-string conversion in store_command(); for backward compatibility, export_hp42s() and hp42s_size() now handle the FCN version correctly as well. * It wasn't possible to enter DIM IND, EDITN IND, INDEX IND, INTEG IND ST, and SOLVE IND ST. Fixed. * Thanks to Vini Matangrano for stress testing Free42's command entry and thus finding these bugs & bringing them to my attention! 26-01-2005: release 1.1.4 * PalmOS version: when printing to GIF, and the GIF file name field in the preferences dialog was blank, the resulting files would be named .0000.gif etc., which is OK, but the path that was stored in the file's data records would be //.0000.gif -- and that extra slash would make the file impossible to delete. This is now fixed, but if you have a Free42 filesystem with such undeletable files, you will have to download the entire filesystem to a PC, then erase it, and then upload it back to the Palm. This will get rid of all bad pathnames. Simply resetting the Palm while Free42 is running will *not* fix this problem; resetting the Palm with Free42 active causes the directory to be rebuilt using information from the data record headers, but in this case, the problem isn't the directory at all, it's the data record headers, and fixing *them* requires the download-erase-upload method. * PalmOS version: no longer allows open files to be deleted from the Free42 filesystem. * Motif version: the .free42rc and .free42print files are now called .free42/state and .free42/print, respectively; creating the .free42 directory was necessary anyway in preparation for pluggable-skin support (gotta put those skins somewhere!), and so I moved the state and print-out files there, too, in order to clean up the home directory a little. * CLP would not update the current_prgm index. This meant that if the current program was after the deleted program, the pc could end up pointing at an invalid location, and if the current program was the last program, the current_prgm index would end up pointing beyond the end of the prgms array; this could lead to memory corruption or a crash. Now fixed. 21-01-2005: release 1.1.3 * Multi-row VARMENUs could cause a crash when switching rows. Fixed. * When a command is completed by making a selection from the PGM catalog, the selected label or END is now echoed on the screen, even if the key isn't held down for 1/4 a second. This is necessary for commands that take a long time to execute: when printing a program, you should not be seeing PRP "_ while the program is being printed; the display should show the full PRP "name", which it now does. * PalmOS version: MEM now reports the number of free bytes in the dynamic (application) heap, rather than the total number of free bytes in the system. This is better because the dynamic heap is where Free42 actually stores programs and variables, and it can get full even though the system as a whole still has lots of free space. 19-01-2005: release 1.1.2 * PRP & LIST: in strings and in string arguments, translate character code 10 (LF) to 138, so that it shows up properly in text-mode program listings, rather than becoming an actual line break. In "raw text" listings, this means it becomes Ctrl-Meta-J, but that's the best I can do, I guess. In "normal" mode listings, it is now rendered as "\LF". Graphics-mode listings (GIF and on-screen) already did the right thing. * It was possible to do SST and BST while in the middle of command entry; this made it possible to go to line 00 and have an instruction inserted in the program at pc = -1, which caused memory corruption because it would write a byte before the start of the program block. (Line 01 corresponds with pc = 0, in other words, the pc is a byte offset from the beginnig of the program; pc = -1 is a special value used to indicate line 00, which isn't a physical program line at all.) I plugged the SST/BST hole and for good measure, I also added a check to store_command() to make sure that it never inserts anything at pc = -1. * PalmOS version: when a file was deleted from the Free42 filesystem, and the directory entry immediately preceding it was a directory, that directory would be resized just as if the deleted file had been in it, resulting in a bogus directory length and a corrupted directory structure (which can be repaired by resetting the Palm while Free42 is running; when this happens, Free42 will rebuild the directory the next time it starts up). Fixed now. * Windows & Motif versions: GIF file names looked like foobar0000.gif, should be foobar.0000.gif, like in the PalmOS version. Fixed. * Motif version: when the print window scrolls because of a print command, while it is partially obscured, the region that is scrolled out from under the obscuring window did not get painted. I added a GraphicsExpose event handler that schedules a full repaint whenever that happens. Not pretty, but unless I can find a way to handle those events synchronously (as they are caused by the scroll operation), it's the best I can do. 17-01-2005: release 1.1.1 * Free42 Conduit: fixed a bug that would cause it to write past the end of a memory block, causing it to crash (and take down HotSync Manager with it). * PalmOS version: fixed a bug in the file selection dialog, that would cause the file list to be initially empty under certain circumstances. * The auto-repeat feature on the 'up' and 'down' keys would leave the 'repeating' mode flag set, even after the key was released. This would cause the next key pressed to act like 'up' or 'down', even if it was a different key -- provided it was held down long enough for the auto-repeat timeout to expire. * SOLVE: in the situation where f(x1) and f(x2) have the same sign but different values, x3 is computed by the secant method; if f(x3) is worse than f(x1) and f(x2), the interval is bisected -- but this code had a bug: if f((x1+x2)/2) was *also* worse than f(x1) and f(x2), it would repeat the bisection step without changing x1 and x2 first, and so the algorithm could get stuck. I fixed this by *always* assigning x3 to the worse of x1 and x2 after bisection, thus making sure that there is always change (if not necessarily progress, but that's another story). 15-01-2005: release 1.1 * Implemented program import and export, both in original HP-42S format (which is also the format used by Emu42), and in Free42's own internal format. * PalmOS version: implemented a simple filesystem (using a PalmOS database underneath) which makes it possible to support program import and export, and printing to text and GIF files, on PalmOS devices that lack VFS support. A HotSync Conduit is provided to mirror this filesystem on a PC (Windows only, for now; maybe I'll do a Java version later). * Added an option to display the byte count on line 00 assuming HP-42S format. Note: program lines containing numbers are stored in binary in Free42; this means that 1e3 and 1000 end up being the same -- unlike on the HP-42S, where those lines are different, and have different numbers of bytes. When computing the HP-42S byte count, Free42 always assumes the most compact form of a number -- but this means that if you import a program containing numbers that are *not* maximally compact, the byte count given by Free42 will be lower than that on the HP-42S. * Changed many of the special character substitutions that are made when sending printer output to text files or PalmOS memos, hopefully making them more readable; also added an option to suppress those substitutions altogether ("Raw text" in the Preferences dialog). * PalmOS version: can now activate the menu bar by tapping in the top part of the screen. * The BCD conversion table (37 kilobytes) is no longer stored in the state file. The Windows and Motif versions now rebuild it each time they start up (PCs are fast enough to do this) and the PalmOS version now stores it in a database. Windows and Motif users will probably not notice any difference (apart from a smaller state file); PalmOS users may notice that they now have more application heap space to play with, startup and shutdown are faster, and HotSyncing the state file is faster. * PalmOS version: added a sound volume control to the Preferences dialog. * PalmOS version: fixed a bug that would cause the state file to become corrupted when writing large matrices (real matrices with >= 4096 elements, complex matrices with >= 2048 elements). * Windows version: when the print-out window is partially obscured, no longer scrolls the obscuring window into the print-out. 20-12-2004: release 1.0.17 * NORM and TRACE print modes: numbers entered by the user are now printed as entered, not formatted according to the active display mode -- except for one detail: if the display mode is FIX n, and the entered number does not contain an exponent, and the fractional part has fewer than n digits, it is padded with zeroes to n digits, so that decimal points will line up. (I got this from the HP-97 manual; here's hoping that the HP-42S/HP-82240A are similar.) * All O(n^3) matrix functions (INVRT, DET, SIMQ (MATX), matrix-matrix division, and matrix-matrix multiplication) are now interruptible (by EXIT). * LIST, PRP, PRUSR, and PRV are now interruptible (by EXIT and R/S). * Changed the Makefile for the Motif version slightly, so that the mask bitmap that it generates is called Mask1.pbm (and Mask1.xbm); this way the name does not clash with the unscaled mask.pbm on case-insensitive filesystems. * Windows version: the print-out window scrolling code now actually scrolls the window, instead of invalidating and repainting everything all the time. This makes programs that print a lot of output run faster. (If LIST, PRP, PRUSR, and PRV seem *slower* than in the previous release, that's because they're interruptible now: after each line, Free42 returns to the message loop, and during an interruptible print operation, each time there will be a WM_PAINT message to paint the most recent line -- so each line is actually painted on the screen *as it is generated*; the previous release didn't render anything on the screen until the whole printout was finished, and that was obviously faster, but not very pretty.) 17-12-2004: release 1.0.16 * NORM and TRACE print modes implemented, including the different formats used by PRP and LIST (right-justified and compact, respectively). I'm sure many differences remain between Free42 printer emulation and the real HP-42S + HP-82240A, but hopefully this NORM and TRACE implementation is close enough to be useful. (If you spot a discrepancy between Free42 and the original, please feel free to report it to the author so he can fix it. It will be appreciated!) * Motif Version: the print-out window scroll bar now has its increment (i.e., the amount by which it scrolls when the arrows are clicked) set to 18 pixels. This corresponds to one text-mode output line. (The Windows version already behaved like this; the PalmOS version will, too, as soon as I figure out how to control this aspect of scroll bar behavior on that platform.) * PRP now prints a highlight ("goose" character) in front of LBLs. * PRP now allows the argument to be an END from the PGM catalog when in program mode (it already did the right thing when in normal mode). * Flag 21 now controls PRA, PRLCD, PRX, PRSIGMA, PRSTK, PRUSR, and PRV during program execution. It is still ignored when these commands are executed interactively, and it still affects VIEW, AVIEW, ADV, and PROMPT regardless of whether a program is running or not (note that PROMPT prints only in NORM and TRACE modes). * GTO . now allows the argument to be an END from the PGM catalog. * Power cycles (exit/restart, and also power off/on on PalmOS) now cause program execution to stop if flag 11 is clear. * CROSS with two matrix arguments did not drop the stack; it placed the result in X but left Y intact. Fixed. * SST and BST now work properly even if invoked in unusual ways (XEQ, ASSIGN, the FCN catalog). 14-12-2004: release 1.0.15 * FIX and ALL mode now round to at most 12 digits before deciding how to display. This fixes two bugs: (1) in ALL mode, 0.00009999999999999999 is now displayed as 0.0001 instead of 1.e-4, because it now understands that the former does not hide any information; (2) in FIX 01 mode, 123456123456.9 is now displayed as 123456123457 instead of 123456123456. * PRA now prints an empty line if the alpha register is empty; the previous release printed nothing in this case. * PRSIGMA could print numbers instead of strings and vice versa (it checked the wrong location of the REGS matrix' is_string array). Fixed. * Windows version: takes display resolution ("Small/Large Fonts" desktop setting) into account when calculating scroll bar width, instead of assuming 96 dpi. * Motif version: now remembers window positions between sessions. 13-12-2004: release 1.0.14 * Windows version: when using saved window geometry information, don't trust the saved width and height for the main window, and don't trust the saved width of the print-out window: the decor dimensions and menu bar height may have changed, and since we don't allow the user to resize the window in those directions, the result is a bit embarrassing. Also, instead of assuming that scroll bars are 16 pixels wide, get the value from the desktop settings in the registry. * PalmOS version: limit size of generated memos to 4096 bytes; the previous release had a 32767 byte limit, which turns out to cause problems with old versions of Memo Pad and with PACE, and to make things worse, the check for that limit wasn't even implemented properly (I used a 16-bit signed int. Oops.). * IP hack: in addition to IP, FP, and AIP, this now applies to all functions that treat arguments as integers by truncating them: AROT, DIM, NEWMAT, POSA, R<>R, STOIJ, XTOA; BASE functions (AND, BASE+, BASE-, BASE*, BASE/, BASE+/-, BIT?, NOT, OR, ROTXY, XOR); and indirect addressing when the target is a numbered register. AGRAPH and PIXEL are not affected: they don't truncate their arguments, but round them to the nearest integer. * Indirect address resolution: IND nn, where Rnn contains a string, did not work. Oops! Fixed now. 10-12-2004: release 1.0.13 * Printer emulation -- on-screen, and also prints to plain text and GIF files, and on PalmOS, also prints to memos. Still needs to be verified against some more actual HP-82240 output; also, NORM and TRACE modes not implemented yet. * The user can now control whether or not singular matrices during matrix division/inversion, and range errors during matrix multiplication/division/ inversion, generate error messages or not. * Added "IP hack" option; this causes IP, FP, and AIP to use the value in X rounded to 8 decimal digits. This enables some programs that rely on BCD behavior to work correctly on Free42. (For example, code that stores two 2-digit numbers in one number as AA.BB: to extract AA, it does IP, and to extract BB it does FP 100 x IP. In binary math, this is not reliable, since the representation of the BB part of AA.BB is not exact; by rounding off a few digits internally before doing IP/FP/AIP, the binary round-off error can be compensated for in many cases.) * PalmOS version: set "backup" flag on Free42State file, so that it is backed up during HotSync operations. * PalmOS version: fixed annoying bug that caused a beep after executing any menu command (the pull-down menus, not the emulated HP-42S menus!). * Windows version: now remembers window positions between sessions. * Fixed menu behavior of MVAR, SOLVE, and INTEG in the case where no real variables exist. * Fixed PRA in the case where it should print multiple lines (because of LF characters or because it doesn't fit on a line). Also affects AVIEW. * DOT didn't accept the case where X and Y are complex scalars. Fixed. 23-11-2004: release 1.0.12 * Windows version: added better definitions of asinh(), acosh(), atanh(), log1p(), and expm1(), all taken from the GNU C library, version 2.2.5. 20-11-2004: release 1.0.11 * Printer support skeleton. Doesn't actually print yet, except for the Motif version which can send plain text to standard output -- but now all printer- related functions at least behave like they would on a real HP-42S with no printer. This means: no more "Not Yet Implemented" error messages; PRP, LIST, and PRV accept the appropriate argument types, and VIEW/AVIEW stop program execution if flag 21 is set while flag 55 is clear. 18-11-2004: release 1.0.10 * Error handling while SOLVE and/or INTEG are active: INTEG no longer affects error handling (previously INTEG was treated like SOLVE, but that was wrong); SOLVE now traps Stat Math Error (in addition to Out of Range, Divide by 0, and Invalid Data, which were already being trapped), and the same set of errors is now trapped while RUNning as while SSTing (previously *all* errors were trapped while RUNning, but that was wrong). 18-11-2004: release 1.0.9 * When performing a hard reset, initialize the random number generator using shell_random_seed() instead of setting it to 0. * While in command entry mode, entering a numeric argument, and the argument length is still 0 (e.g. just press STO or XEQ), shift-ENTER should activate alpha argument entry (like ENTER without shift) and not beep. Fixed. * When used in INDEX mode, INSR would store X at IJ before inserting the new row, and replace X with the new contents of IJ after having inserted the new row (always 0). In other words, it behaved like you would expect in EDIT or EDITN mode. DELR had a similar bug although it was less noticeable (it would leave the wrong value in X but at least it did not store an incorrect value in the matrix). Both fixed. * Fixed a couple of bugs that would cause INSR and DELR to update a matrix incorrectly if its data was shared. 13-11-2004: release 1.0.8 * CLALL would crash if invoked using XEQ or ASSIGN while spelling out the name (selecting it from the FCN catalog did work). * Variable catalogs would show the up/down (multi-row menu) annunciator, even if they contained just one row of items. 10-11-2004: release 1.0.7 * Changed the LU decomposition code so that it will keep going when it finds a zero pivot, or even a whole zero column. Also added range checking to the back-substitution code; it replaces infinities and NaNs with (POS|NEG)_HUGE_DOUBLE. (NaNs should never be caused by divisions by 0, since I think I'm checking for that wherever it could occur, but they can also result from adding two infinities of opposite sign.) There is no check for flag 24, because the 42S ignores it in this situation, too; I also removed the flag-24 checks from matrix-matrix multiplication for compatibility. * AVIEW, when displaying one line, now clears flag 51, instead of leaving it unchanged. * A couple of "goose" fixes: clear row 0 before painting the goose after a PSE, and restart the goose at its last position when re-enabling it after a PSE or CLD. * After selecting a program in the INTEG application and entering the variable menu, pressing a menu key immediately no longer performs a STO operation, but instead selects the corresponding variable as the integration variable (and moves on to the integration parameter menu (LLIM/ULIM/ACC/INTEG)). Previously, you needed two keystrokes to achieve the same effect, which was consistent with the other variable menus (VARMENU and SOLVER), but not with the real HP-42S. 09-11-2004: release 1.0.6 * Matrix-matrix division computed Y * inv(X); the HP-42S does inv(X) * Y. Most embarrassing Free42 bug to date! And it also means that I never needed the _trn_ versions of the LU decomposition and back-substitution functions to begin with -- so they're gone again now. * With the INTEG Parameter menu (LLIM/ULIM/ACC/INTEG) active, STO and RCL, followed by one of the right 3 menu keys, caused a beep (good), but also caused the commands to be completed, with a bogus argument (bad). 08-11-2004: release 1.0.5 * Yet another AVIEW bug: the new line breaking logic introduced in release 1.0.1 still does not handle LF in position 23 properly. If an LF occurs when the current line is full (i.e., at position 23, but that can change when I start supporting bigger display sizes), the result should be just one line break, not two. * [MIN] and [MAX] should search column J from row I on down. My original implementation searched the entire column. Fixed. 08-11-2004: release 1.0.4 * SOLVE performance improvement: for the case when f(x1) and f(x2) have opposite signs, compute the next guess using Ridders' method instead of the secant method. (The secant method is still used if f(x1) and f(x2) have the same sign.) For well-behaved functions, this improves performance to about as good as the HP-42S. * SOLVE now displays the two most recent guesses, and the signs of their results (or a question mark if an error was trapped while evaluating the function). This display is updated at most 4 times per second -- display operations are MUCH more expensive in the emulator than in the original, so you have to be careful not to throw away most of your CPU cycles on animating a progress indicator (especially if said progress indicator is likely to turn into an unreadable blur on faster machines). (This is similar to the "goose" situation.) * SOLVE bug fixes: now returns not just the root in X, but also: the previous guess in Y, f(x) in Z, and the result code (0=root, 1=sign reversal, 2=extremum, 3=bad guesses, 4=constant) in T. Finally, "Bad Guess(es)" is not an error, just a message. * INTEG performance improvement: switched from using Simpson's Rule to the Romberg method. Still not quite as good as the HP-42S, but getting pretty close. It converges much faster than Simpson's Rule and seems quite stable. * INTEG bug fix: now returns not just the integral in X, but also the error estimate in Y. * Thank You to Hugh Steers for the Romberg implementation, and for reminding of the Numerical Recipes book, where I got the Ridders code. 06-11-2004: release 1.0.3 * CLALL caused memory corruption because clear_all_prgms() didn't clean up the 'prgms' and 'labels' arrays properly. How on Earth did this manage to not crash when I tested it three weeks ago?!? * When the calculator is switched off in the middle of command entry, with flag 11 set, command entry should be cancelled on the next power-up. * While entering a LBL command, if the argument length is still 0, and one of the alpha submenus is active, interpret the number keys as alphanumeric rather than numeric. This allows labels like LBL "123" to be created easily. 06-11-2004: release 1.0.2 * In number entry mode, the MODES menu does not come up (the code does not recognize the difference between +/- and SHIFT +/- in this case). Fixed. 06-11-2004: release 1.0.1 * Reduce arguments to SIN, COS, TAN, ->REC, and COMPLEX (in POLAR mode) by applying fmod(x, 360) (in DEG mode) or fmod(x, 400) (in GRAD mode). In RAD mode, the behavior is unchanged. See angle_to_rad(). * Fixed a bug in keydown_command_entry() that made it impossible to create alpha LBLs where the first character was a shifted alpha menu key (e.g. LBL a, LBL "foo"). * Fixed bugs in keydown_normal_mode() that caused program instructions generated by several menus to be inserted before the current line, instead of after. The affected menus were CATALOG (except FCN), VARMENU (including variable menus displayed by the SOLVE and INTEG applications), INTEG parameter (LLIM/ULIM/ACC/INTEG), and CUSTOM (LCLBL mode only). * Fixed the behavior of the XEQ (and GTO) key when the CUSTOM menu is active in LCLBL mode while in program entry mode. It now starts the command by calling do_interactive() instead of start_incomplete_command(), and the former takes care of the additional bookkeeping necessary in program mode, which fixes problems that would occur on line 00, or when cancelling the command (returning to the wrong line). * Flags 47 (variable menu), 53 (input), 65 (matrix editor), and 75 (programmable menu) did not work, because they were missing from the virtual_flags list in core_commands2.c. Fixed. * A linefeed character at position 23 in the alpha register would be displayed as 'LF' by AVIEW. The check for LF should not be skipped immediately after a line wrap. Fixed, and also made the code a bit more general (not so tied to exactly two display lines). * The BINM, OCTM, DECM, and HEXM commands, when executed from a program, should activate the BASE application, and they didn't. Fixed, and also fixed a menu highlighting bug which I noticed while stepping through the test case for the BASE bug (highlights would disappear while SST was held down). * When entering number entry mode while in normal mode AND a menu is active, redraw the display to force the menu to be repainted; it could be invisible due to a recent two-line AVIEW. * Added [MIN], [MAX], and [FIND] functions. See README for more information about these. * PalmOS version: reduced the beep volume from full volume to half. It really was a bit loud on some devices. (Of course now other people will complain that it's too quiet! There should be a sound volume control, but that will have to wait until release 1.1.) 04-11-2004: release 1.0 free42-nologo-1.4.77/README000644 000765 000024 00000022677 12110237247 015527 0ustar00thomasstaff000000 000000 ------------------------------------------------------------------------------- Free42 is a software clone of the Hewlett-Packard 42S calculator. If you know how to use an HP-42S, you know how to use Free42. If you're new to the HP-42S and its clones, you may want to take a peek at the manual. You can get a scan of the original manual, wrapped in a rather large PDF file, by ordering the HP Museum CD/DVD-ROM set (highly recommended for any HP calculator enthusiast). Go to http://hpmuseum.org/ to learn more. Alternatively, you can get a free manual for the HP-42S and Free42 at http://joselauro.com/42s.pdf. The remainder of this README file covers some miscellaneous tidbits that I had no other decent place to put. ------------------------------------------------------------------------------- Binary vs. Decimal Starting with release 1.4, Free42 can be built with binary or with decimal floating point. Building the binary versions works as always (just say "make"); to build the decimal versions, set the BCD_MATH environment variable, or use "make -e BCD_MATH=1". When switching between building the decimal and binary version (or vice versa), be sure to do "make clean", to avoid linking the wrong objects. When building the Windows version using Visual C++, choose the Free42Binary or Free42Decimal projects; these projects are set up so that all output files are written to separate directories (ReleaseBinary, ReleaseDecimal, DebugBinary, DebugDecimal), so you can switch between these projects without having to worry about cleaning up object files. ------------------------------------------------------------------------------- Building the Unix version: Different operating systems require different compiler and linker flags. One day I would like to use autoconf or something similar to make it easier to provide a Free42 source package that builds easily on all sorts of different flavors of Unix; until then, here are a few hints that may help in case simply saying 'make' doesn't to the job for you. One known source of confusion is the LIBS line in the Makefile. Depending on your system's specifics, you may have to list more libraries there than I have in my Makefile. Apparently, different linkers have different abilities to automatically include required libs: on my system, -lXm (Motif) automatically includes -lXp -lXmu -lXt -lX11, but on others, some or all of these may have to be specified explicitly. Additional libraries may also be needed. If your build fails due to a missing sincos(), add -DNO_SINCOS to the CXXFLAGS in the Makefile. The Makefiles were all written for GNU make. On Linux, this is usually the default anyway, but if you're using FreeBSD or other BSD derivatives, you may have to use 'gmake' instead of 'make'. ------------- Fedora Core 5 and Cygwin: this is what I use for development; the Unix version builds with no changes. ------- MacOS X: Needs LIBS = -lm -lXm -lXpm -lXp -lXmu -lXt -lX11 Needs -DNO_SINCOS. ------------------------------------------------------------------------------- About the character sets: ('bigchars' and 'smallchars' arrays in core_display.c) The top half of the 256 character codes are like the bottom half, with these exceptions: 0x80: thin colon (':') (looks different than regular colon), while 0x00 is a calculator-style divide sign (superposition of ':' and '-') 0x81: small 'Y'; 0x01: multiply sign 0x8a: displays 'LF' character (small 'L' and small 'F', slightly offset, squeezed into one character cell); 0x0a: actual linefeed (move to next line of display). The bigchars array contains 130 characters, in 5x8 pixel cells, corresponding to codes 0-129. Char 10 is the 'LF' ligature. Note: the 'x:' and 'y:' legends for the standard X and Y register display are built from 0x78 (lowercase x), 0x81 (small 'Y'), and 0x80 (thin ':'). Note: 0x01 (multiply) is different than 0x78 (lowercase x) (it is 1 pixel higher). ------------------------------------------------------------------------------- HP-42S bugs that I found: If you activate the CUSTOM menu in KEYASN mode, go to its third row, and then switch to LCLBL mode, you get a menu row with labels "L", "M", "N", "O", "@", "XEQ"; these keys activate the commands XEQ ST Z, XEQ ST Y, XEQ ST X, XEQ ST L, XEQ 117, XEQ 118. These commands do actually work, but entering the corresponding labels into a program is a bit tricky... ->POL and ->REC with a real number in X reject a string in Y (good), accept a real number in Y (good), accept a complex number in Y but only use its real part (undocumented and not very useful), and accept real and complex matrices in Y with weird results. I'd say the complex and matrix cases are all bugs, resulting from a missing parameter type check. HP-42S bugs that others have found: When COMB or PERM are invoked with bad arguments (e.g. X > Y), "Invalid Data" is returned (good), but all subsequent invocations of those two functions also yield that message, even if the parameters are now correct (bad). This condition can be cleared up by performing an operation that affects LASTx (but not STO ST L). Apparently some rev. B models do not have this bug; I have read that the original problem was a hardware bug, for which rev. B introduced a software workaround; somewhere in the rev. B production run the hardware bug was fixed, but now the software workaround would *cause* the bug. The bug also exists in all rev. C models, apparently (it does in mine, and in Emu42 when run with a rev. C ROM!). I'm curious as to why a couple of basic, straightforward instructions like COMB and PERM would be sensitive to a hardware bug (and *only* they, apparently), but who am I to question what I've heard others report? I'm not about to spend weeks poring over ROM disassemblies to make sure... In LINSigma mode, summing a matrix is buggy. I have seen it sum only the first row, and report a bogus value back in X (8.47216900137e-489), and also seen it say Insufficient Memory when that clearly wasn't true; I have heard a report from someone else about the machine even locking up to the point where it needs a hard reset. In some versions, LINSigma mode, Sigma+ and Sigma- do not update LASTx. I read about this in a manual addendum from HP; however, I cannot reproduce this, neither on my real HP-42S nor on Emu42 (both rev. C ROM). I have also heard a report that FCSTX is buggy in PWRF mode; I have not been able to confirm this yet but there appears to be more I have to read about it. Probably depends on your ROM version; I tested with rev. C but it seemed fine. Not sure if this is a bug or not: When KEYX or KEYG are used to reprogram the UP and DOWN keys, it would be nice if the 'updown' annunciator in the display got turned on to tip the user off. I'm not sure if the manual mentions this behavior (TODO - check), but the Programming Examples and Techniques book does (page 34, bottom). I've seen a discussion about this on the hpmuseum.org forum, and all were agreed it would be a nice feature, but it doesn't actually seem to work. At least it doesn't on my HP-42S, and I haven't heard anyone claim that it does work on theirs, either. Note: Free42 does turn on the 'updown' annunciator. Although my general rule is to mimic the HP-42S as closely as possible, I felt that this was a nice feature and that it's pretty unlikely to break anything. :-) ------------------------------------------------------------------------------- [MIN], [MAX], and [FIND]: undocumented HP-42S matrix functions [MIN] finds the lowest element of the current column, starting at the current row, of the indexed matrix, and returns the element to X and the row where it was found to Y; [MAX] is like [MIN] except it finds the highest element; if the minimum or maximum is not unique (it is found in more than one row), the highest matching row is returned. [MIN] and [MAX] require the indexed matrix to be a real matrix, and they do not allow string elements in the column being searched. [FIND] locates a specific value, searching the indexed matrix left to right and top to bottom. The function works as a conditional: when a program is running, the following instruction is executed if a match is found, and skipped if a match is not found; when executed interactively, the display shows "Yes" if a match is found and "No" if not. Also, if a match is found, I and J are set to point to it. The indexed matrix may be real or complex, and the search value may be real, complex, or string; real or string values are only found in real matrices, and complex values are only found in complex matrices; in other words, 5 is not considered equal to 5 + 0i -- mathematically speaking this is wrong, but on the other hand it is consistent with the behavior of the X=Y? and X!=Y? functions. ------------------------------------------------------------------------------- Differences between the Free42 printer emulation and the HP-42S/82240 Free42 prints programs differently in NORM or TRACE modes: in NORM mode, the listing is right-justified, and in TRACE mode, the listing is compact (multiple commands per line). The HP42S does not do this; Free42 "inherited" this behavior from the HP-82143 printer, which the author remembers fondly. :-) When the HP-42S has to print a line that is too long to fit on one physical printer line (i.e., more than 24 characters), it takes no special action. Whether the printer prints the overflow left- or right-justified remains to be found out. I should check the 82240 specs to see how it behaves. Free42 usually prints the overflow left-justified; the only exceptions are PRP and LIST in NORM mode. free42-nologo-1.4.77/skins/000755 000765 000024 00000000000 12110237251 015753 5ustar00thomasstaff000000 000000 free42-nologo-1.4.77/TODO000644 000765 000024 00000005044 12110237250 015316 0ustar00thomasstaff000000 000000 iPhone version: * Add option to disable key click. * Haptic feedback option. * The pop-up keyboard handling in the Preferences and File Selection views is broken. The screen often doesn't scroll up when the keyboard appears. That should be fixed, and while you're at it, put those views inside scroll views, so you can scroll them when the keyboard is visible. Android version: * The key click is flaky on some devices. Why? * Add android:installLocation property to the manifest, to support installing the app on external memory (SD card) under Android 2.2 or later. See http://developer.android.com/guide/appendix/install-location.html * Add an option to display skins scaled to their maximum size while maintaining aspect ratio, or to display skins without any scaling. * ACCEL, LOCAT, and HEADING don't work on a lot of devices. I'll have to rewrite them using the new APIs (ugh). * Add support for physical keyboards. * Implement pop-up keyboard for ALPHA mode text entry. * Scale print-out (the current 2:1 scaling is a bit small on large screens) Windows version: the "Calculator Key" option should probably set \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AppKey\18\ShellExecute, not \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AppKey\18\ShellExecute. Easy enough to fix, but make sure that the new code restores the HKEY_LOCAL_MACHINE key so that people won't have to use regedit to get rid of the Free42 mapping! Another Windows "Calculator Key" issue: it doesn't seem to work in multi- language installs. Some other registry key overriding the obvious one, it seems. Mac version: Modify the file selection dialogs so they have a file type menu, and add extensions to file names automagically when appropriate. Implement pop-up keyboard for ALPHA mode text entry. Implement copy & paste. Implement URL decoding for HTTP requests (filenames with spaces, etc.) iPhone version: it is possible to disable the sleep timer using [UIApplication sharedApplication].idleTimerDisabled = YES; This would be a good thing to use to prevent auto-poweroff while a program is running. Add a feature for importing and exporting variables from and to CSV files. It would be nice if we would handle the shell_milliseconds() wrap-around. This function is only used for flying the goose and updating the progress report when the solver is running interactively; all it would take is for those two places in the code to detect unlikely values of (startTime - shell_milliseconds()) and assume the nearest likely alternative, a la Y2K. free42-nologo-1.4.77/VERSION000644 000765 000024 00000000007 12110237246 015675 0ustar00thomasstaff000000 000000 1.4.77 free42-nologo-1.4.77/skins/Andy480x800.gif000644 000765 000024 00000324675 12110237246 020236 0ustar00thomasstaff000000 000000 GIF89ad~ Wfu2GGրUQzߍzLƴVVV+NWN?c̫û)3;\gt1!~n1r~%exKKKŚAVٓc]8zrcEfմ%G* gjYPrHf"o;Z!˜pppbYJyV]{^m03FBG!!!9 *#III,T\&0%:$Sdmmm !-|wuSn| ˠ8,{KXi7;4LZ!򰳈á4:_d2*AJ-9='JWbbbr^|: (]j0F![q1dV:()=u|x"9J䁌_m|JbmFv[{m/X bpqL@:%"MMM$RXr=+L "]Q>XC/;~xnªVOI000ZSMWPJ\UO[TNXQKYRLriZLA.===mi¿fff, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٔ]˶۷pʝKݻx˷߿ L]X̸ǐ#KL˘3k̹ϠCMi^ͺװc˞MvwNk N >֨3}УKNسkνËOӧ\u˟O5|h& V_gb$f6hf=YMYh(hh4h۱x8)D6cfΏ 3n O<;o:K<[%k1{O!#.~goǏ?;L;c_&o~E)p# : k`AYpR=Ѝ0U% iͭW-d6FPW Beq=Dp 1qE% 8 kɢ.z`| V2hL%pHAq_ȣ> IBL"F:򑐌$'IBҁ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0Ib$H2f:Ќ4IjZ̦6nz 8IrZL:v~ @Jz£<:z,T=A{L =1ztDу Iqz8T=Mh=*y, m)Nm:ޣ\z鷧\B5ݮ^#yVϋ UumG Z'h渢vs:\ulek:4nzEfU` Q.W_j"v=n{l֙z =)s=՝ޞrz t]QӢ7Ve7TǷF~YT&܄;ɆU_\]tDŽV Uh~yW:nJW\_Yo |)jTjTځl+Iu꤭cy:ijc05Z%QjY:y>w`z6ceuw Ws  en'`e4_kuJ:gYaVRd'uw[W^ֲmuJjutzs'Yp's;xdjv_@[U%r'[MŌ5fU0zH\h uTb*Y^{osUUj<f_gUǚ1 ZhT2dflᕓɓWK)G~k~^HYJkAّMoQZSi[UY_WغqCte v]ז`Ǜ^ y%[k^j0bىp2``AXBj Glݔy[]5ؕ Xu6ֽ[; [˖7ߍMW _e/ފނYe ߪ)E&ƘS댒X9WZƙrDT6tHImd\H&=ia]p[i_XMIk]a0=Lc_jx"吀Uo+dlFײ՟Wy܊xeg^Ek]ي[^=M,o%IiU͒\Wwt`ˌȯ\CCߎ߅~Δ)UlUlN&i٦>mf]Zݒkfڴ5jUgNtL~MYuzO䴗lS_Nʜ.hR|㔂š`^EpN f`M$l+<6Sɴ ?4w]WOw u?D#t',>N.J95;`jy(! ] @̍ X tB($d/APYD!vG|J'_Ibeta[ -X.T'@$-1cl}6I:|EMjʣ0C^Gul\0yBr4QR d-mZ$Gf~XVK\egUߝ2.͉)\@|֜|OΒm+M y!&N6#XZH&1AʟJJKLvj2XQV 1EZ>BqAr¥%M"VaA#KdȳX.u.$}ڏӰegBg<\$`N8\ FҚ]lTi,!J`^6WL#k%E,7e_qh+B3&;ɌRUT ^fx(T-L1lfؾ& rqRHyFܹu%=AA^* sw&?MT)WGz.D =G6\Zc!7 viBHpC'@c .*WLTl,Ad3\&*FKjc@gkiC>wb1R VGUoT@O{(1ljCc|S_r;aXj1<H76r0B;!0Z^Xֺ-B0dʪۨ-[2-.UAZ܏} &ȃB  ;3%\Sg;& ߐl;p']ٗ6ff1ɇf$mi+B5I4Ͻz}'B^.h\TVI9oB`#9w2CT8$ %xC ˹=b15p"H!/&bs'Wx* P4Y?+0!nc& 0iBڎ i?zvj/~# Ⱥ01 s*P*8#IZڽpÆ;@J>)x >?~$Wi0`20)%뭝a;:"k"̵( 6:@ jAbiHk*,qr 8ء4,kʅ<8~%xb,::K$R,F2*s7y nB'tlLE")}C3`&c2&≀@8E ~dȕ;3  򡠅+K!J#svv C"ź$ s e ʥ3v\\*J3Y3DA *b*PǴ7܋)tG A@FD2<-0  O, Js{"g˥(9) t)ѝB 5y=V364&h!MR(8^" u1 YM!Itƕj;Xm1sy$xp,-A $&8퀳 3B!V۾#;Px3XUo#qul?ϼLN?<(і- o)  Dy^y }kJ)2v鍨M Ju3یp+$`յ9ึ1p 8C]iB&$PQ`5Uʖa٘Vll} 1+%ظ3Y` Fxyz{|x O9wׂ5؃W~#jXE؈X|U؂ PqWx؎؊BɀX5YX鏒=ٗה-W*׃U=%~hxٟٞڠڡ%ڞ=~ _:[-ڧڨڣ#(ڭڡZ(ڮ5[Z;HYٳۭb ! ۼگ;#3 ڹ=R"}[ą\EOIװ >Ǎ̍]h\ܹͅݼU\=ܯͬKH3 ]U]P8@x]ńVP^[%~B\UhpV`f^]VUULگ.ua n^M㍇]؄MW5]EuE}Gs8=[H@FѝܱCA[=px=V%jeV]V_ %)~ZH_UL^`=@.<%=\v=X=_epaMb]V`U]=_-BBWC]6ncyظP [XU&"~"Z򵁞Y`b@&ƶYccH]pD(xa`miZF\cHΘx !V`7փ^\nc6&e 3;"K@;-゠\`U^YV`aNa` 5 %V^M0dH?59e%䞥^`d=_K d+{5ߞ] vgEƨJ a$NtN`UP^8Ճ:amݹ2 ZV=g=; x^.] VVp&n(0ٓT,n.jV^c@QBe(^=@ݏU"_paMpZ'k ubj\%8gx 벾@qӬʶe=Yx^ٳMDm]O~ZUpDVbwf2PԈeهUb^-*3QnVlc *vk m~`M ),cfV d~oXxeNgY+O)P_2R \t=  "Iv}h.kUٖl<2϶vM@M[:H Ourf?.gPU[IYUjn+~Fnt U^h׌!C*BuIK_ׄ{fv\`6WMޓe}9$XC]zAbߩW^ה+Wm=&\z˕s*o<׳ooI_*E.=CUUox`{S -ux"8! B6C =z5PxMw߇) 2L C-6P6#=#AX@K4b4OB:$@=*=%]:9%=C[zy&M_!yWlL9gSJUa97]t)$:RYJ 7@:(J:)ӐA|U)Bz)~ui*K5s[xκn":,-)PfI]DA{ ^bmٶނsZbfsE.akViO5'ֱ{ESRU)BU6dUBiSL]A7e._OqB.! j%&d("A!TC @#vq͉fC_՝F7fީ$qXJ"H&5¤eꢛb]YZyrW+Ħdvu$5ì.ݱ9SbaJ"u1;ZWU[L5iJ.iZy&*#2TL%b,2 fk; 'VK)2!B ;،>Hmq {_Kcpp]yJYkcnu:ua y ̡?R=hcn1^sx(:#a6~ `R &Fa4taD 8K7&-q1Wt!PcRr.,ݑ oFDÀg/ك9uW’ lÒzfǰ 'e48$G@x#vh p0aH<!Ua7qy ^C-Ne+':()d$ݥa6Xa(&_ ȒbYцNfNi(dl#O*rF.ܾl[ArkBb^*zq5س ;SS֖Vg[JԜBBO`4Se *HM`U*G]&|dQ&)h@>juEh \"E wDy gbbeV/rϮ1Clbb1I,a©V6CJuhv>IA'tPr8ȒZ k86ή %*+vn3MDDodԦBpj13.n_Ieibv-T6kwC]@eSB׾DQI4[Dx$r)jHn]Ru+V.bpZg81HQ h%*p$NMr7NDK+ў|w7ܗ8;iF?WJdtw#L9P`5 r7aw }$`&$3 Ԏ,ᡉmK|#֢BgIf)GЗѢAi&/(flƩ+橡ZAN3bv{i[T;˷Ag2-N a.4Lt1ߙz-eUP<X]i.I$lmC\wgV[댽nOv\xu#qi6-{6Fbj~. .36=ZSe߻n$R7@ll#TZC'}V&Jo}] N/ X5٭5Z^dڈέG$i܇4uK w,txAb1` Bu1CApv,XY%M]P)T`Cb!U ^63&na;/HG#0eܭ5ѽ ʪۚzL ] ! P*{ ٠݁ Z4GRF.p`s'k(Ud◸wgT/J2dα[ C߃z|ܜhtF Iа\ *T޸ q^\J(5 FB Rץ`fM kX\ !`nΦ,d`G T55Lb-LY}،s1f\Mєݜ 7uGVUN͌\Z M! =ZJEq_R_]3 ``iIl-`Y\ȌLqtOimͨJt 0댠ݙ`ÌO TK`ދ aJ6LI! ^F՞$:]2Xwؤ%M1\}GҚG_fdIqO Y ƈHM-RGuP?\͌ )O:=qWҩ ΘJ)%DXFY6U4+Uy5taJ])Qˁ]M*n[UTKG*zEގ*9θ0R%T!?H-V0RL2y6!LIلLZvYeHM7ֽ8hT(͟[=XSxO!\M18P^[N!U\V5MGXM!"ynԽHX$ Ӝ wIQZ((y.Zˁ$Mg (i5%zDUY.fiS^00FT,Ad eTMR XOPAT%?ͣRSETtQj0eW-JQĖYZ M$͕X(aJ mFδlFaaI$zbEdQ1pf 5u\ ]]dO1Q\/i`d]H߅d ќgIVݜ,IuH0S\M厀Pt:H]#8OyeVUh}OieVOlQSMuiJ'p%W9wtamvv5Uw!N@H0\ Y%b AG\u PآR嶤Ld᥎Z-PxFcZݒFːOFa Yje'mb)ه UX}ܤRمZ՝)XEu iKa2A'UUgʦ W˱ }JX_u!9Y6dlɄMܑ]arqC]Yx}&,iFḺEMV%mq̛]i !]h)ԈM8]@qʦMmG%uPαʔqG}a̒gS(v^[I+5}GR jvZ˷ܫcNX=V'C ̴H?0[K)yM D r!>0HȰϊJ`\Sާ1\%qd%O" 0O1W_1gWq Do1gq/iÈ1q /q3@ qZF"˱T=k0?H1 \2&g&o2'w'2& A< %*2+s(ԲŇ D*-r'IHO̠--3.0.[, 0/*벫qRhI%33W'rzZw-B?83993:8(驃|:3=9@bH2=3@3>SIW?B+@YhkLJNTdHvh3XJiOvJt}6Bv=3Xbwt47aoöo tV{WH2¶ttvVd?o7G761r@xl)^;8\WIŵKzsaϨ/˶}uCwX:GuE77Ă{uGׅ_La0vG8c{q셵yS>R3uC x_xQ8*ZO;/UM75NzxGkc{Y8:wLHH97A d9x OD<yk$48$apoy@tYL(v0Aw@sH¤+X9W:uw{dZ99:OA tzK?vkty_89gS@9 o)v$4B?HO;7;9 T{ tt2e 7:W{G@sD ndS; L9K'z;e- 9 S{  s糬Qtu{s@s3iuFK/O{×s9?|ćė8tNV4Y+4 t$ Ё7Ag/}s 4{.ly=D'S_9{B.4ˏ+0{ B3A0Gu;7{G#9;V2]ѳ+<;ЛͯyfУڃ9ۻ8 =@8'>΋ј%SIKg{ 83s 8> ;LFX87={33;ŏsN8:Y7ۇs79w{/HI}򋳺@Ws}Z{}?>ȼ??7@ܳw^Axc_CAC8B$С㱡+,vOD͛wo=xͫpJtN|DȌOUiq㔏-#^z Jǵx2\)PD AFG+{;PXd-a:̞M!T7G/2z`2E!-%c{jʃ7==n äYT|ؔ㛓),:JÈ#/:1~-̪9+.T6֎}U]ϵtʻg.^(g*JhyN&/3@s(єc,4s,&ɏ~0|(BP@jg:Xk@ bˡ 53F$ rS1!KAKL"W6t?ln*&pJphdD #P(,REoNrP41Z&^3h%F47{Ȥˆ3 yK4l`JQӧ>K)V}rL]w%T5Tt6 J9a%izȱЫP &z/Ն"鄣Ԣ0kUxKZZ!!Ѱ5T(\TxrEA 5__!aEؐ;[kL +T(~vAC8E##Bɏ:5cvl-$yP%KC8?1ڒG]tKL2|k tmL%yfr1jh.!*6e}u8$lslή1}aPcJjlƘJ{b\w+~R}@FkΝ e,$E+\+Ҏ 鉋},uWו56ʦt=uPӥST}5A<%'qO>ՠ_#ꪩq:}W 2 Qh& ?~4=kmCJ+~}`*YI/&Jz.P좇f&Ȃ Krv%&W:YfdB6**_jEz %΅PH 7+}0ԣȘ.jloM+ z7f2 i&x2re+j Nʞͦ Mj lW=,Pdʪ 𽰐Hj>piBIH%D!1%q)1`/dA1Eq1Qk(A>qY)9ˍf5$o]oC 'q1GL8*Aq+} utkD q1q!|DZi011qyL>  j 1 !!b>$M"1>f]6j"3$P&# I$]QRFl+Z%]Nn'KR'أ%2(Q&'"u'*ʿ(e8r (*Q m!#1lHRFr r,!S.B/)Rr5q"OJ+.QR +lԲ/#n rpf>Zr @qv2-SYVa;3; 47S%4IT4A4RNJ|r- =$r&/ $l.4?U0v3F,5]S:h2w.N815@3Q23;q2)1* y0߱57:U9s:4[sTxoRkJ>Y!AAA]`q 745Ai>7 A7EM4VZ!6l~6Q\`G]`31Gy1M A.es8)2 eMT7CqRAIBM4;E%c tTNE{J7A;i E44Eiqɢ>M CBKJ7EL I d.|-K iJSZM5V*35O4JTvDOCU>{>dUFͱ=r~OQKWaE]K3;Qj΅xINY㳺q[A\MhR=5QuUQ JY+u\DE-Um@DEA2YGtQɰc.<MS5T3[aG{PߔCKd g2rG .TtBYPQLUCIMAEU3Qdu\r5j!7:a9@O'#nʴ;vi6MNI C[Z7EqbAKC]0H'C>6d=N4ac6=47hp~5/]^TB X_j VT[kO2*HlP%"m6bDiH^WVTE>^V2 .%o1t]%\BmY{5PS=V@PTwEMElk}qg;ͱ|=wZWAKM{qR5^{iWSW(,6wzytygTMr;uocz֩ń.+w45:a4HWt13\]V;5U&GEdq`ݑT[aM3SV c r@/./CVq~xWV9.pjcUtszcbf0c1v~'mMf1\QTBZUu_Jss{8h@T7dz֢ d+"[X4C^Itt]u5GC.p&C`!:TVd@vE\`Mbǵjz /}Xc4t'zM]{xbWlӗLXXi aBԘx1E_j7WpTfAWL7XYq#eMF4 )#)_6M@BYaA6;+8y6VS"B.w [NeW3Y1?t,i0{^p7BaVq-dCtNSCE7f?z4};rhJk՚ѢSpugewl}LY~qHKQ47YuSUt[UWʆÑdg:ĉѡm8xl6Q~34CoC8oSE97uP1nSVUsuU5w\QCVLuGڨCMHRCsZ7am٪xt!|N*r6#zURIg N*:Ǖ+* '`U23aś:2E?tz܄H,I3tE!Rm@X^U|s2`~SC\>xgӱŵECXC5DuhaHҥ!LvL:LwwԌM@#zd#V)&ϏǕd \r`3a!B-C9KYC۽4ш7EaSGSt\9f)/Ǘ YIp^ΏdΉd.;c|8{2l:ݱG>!=)\;: GhE~I=R]a~Q^,χc>ug^cx킛r牾NHgA>{pv=[鱾{VcF>!ɾ>~ϾbihĞ~^/NK?>﹩AIډ>?->qN6>kl6l"UY]a?՗ 9RBq?uq ?{w?q%2?} N?/iF?|JDѳe? '2`e hȊn?UDP|={ͳGϟ… :|1ĉճ"=x՛7텄=y<{fs̚?I3c͎+q#(oePqœGz} JL:aD#Ȗ[k&͍IS*U>XOM5359*{#ť/]V((Ja:0Xq]2^ΆDc(FƐ8g l%F?jbŐmێ8Ю@rJw.|֠b˚8L Ggf5Sa8R+qw[Mu$K%dkg]WJ DU`хlͦamE_GVP^ITeESFmD|"Y%"yREc-K}klMXhdt4qUU*yّM}5YkZ_'Ar %A82gG]eVBSMUyVg_8ƖNѵXSHa"HR&l!%-^|mLvQE୵GdݭTӎ*T`T]X@ęJU|׍i+f% cjW䝊 qUwRW%a@Ut֕vf QEFUQEU4G}"k^y_.&0'LZSFRGi!r@?)i, 9`y[1uLMV8%˄ RVc^\DE%\ORi9"Og SԀ%SQWp31:ud(|Z5 'FZ'7ʑ2jD7*Y+'͐V) wYP0ɲmH 5Ζ5߀;W XD"T E[Ѿs5J| ɣPJN]RslQd1v*)9JJ>/xpӶ"3=gaSN증Ж'ÈOTB QM[Hc`$)oNqI腮ʎ7UXZe.%)⺇6Ef)* uTrMݍ_u$GώJh)~9jf֩dU (ьVEKVd;93!-d)fD,aǖ"^TZ96dRKCU.;". i9AWTQU|@D-eBn;eK?ẖQ;..\+1zɮGn*%&>ON:qG}۱ $\ 4|%џ)֗7QǫΌmYj3hlv難⣝U_9`i!T4hSWh5Fe *L~cKI9@Rbzr_S`W5 ]%u D[;{;[6q{&C{X{{nxjI '$Y)r#46> $a[ԇZs}Wj}:)6;;B2d~FS.@9S'hKS_MG!n"ThёveBobB6 "+2}B]evp 0^N6^D!%co2D?8JPU&,fc;z%"."E5JlCR^%XSRs5s%d+l$8B/C}@6n5DʱM$LSo2R#dJBBED'6iCvÉ/QB3EC5EQa< >DeHcsA4D1.5s#*TS!caQU_d.6fD"45c]29GE͡' D/&[Y[B1R\$'!PPR%xX_Be<{t%ZE~B~JV_3't-h?f=1.Acaix#b|^s,1Bc1B@!mx4c;޲K9x@(4 D5(A4.hZddRbf(q'd[)% <44kMr[sT=VQ!>wpOc#&,lA{1c(GQqD,jA3$3S6A3s}pv Mn"Ma,MUNK956Di9DlNOl'Z6PsbKPsPH6;)"%J1)1b`Q&<]cR\.fIDM@*@t6vAAR%9BT4,T0q1iSC͈CgҸaSͧ9{XJF$PX (T7E05D^Yd)M1<]EFM1VNVsBi*x4p5*X9n"Mygp?~&@ +EHqdEHH}4cD$BUᨏ *JOaʩ 6Uk*>1jG\M.~'z\uRzrrmŚ>g&@"fIKUkFKcR{_ H:t'^icN2@q% `wy{˷} +Kf sKೀ +Kk "0X39Ը+KM[뺖{ <B)ztKKkNjwk +F$KDcҫe0KܫR"˾2*#'b5'›~@-:K+EE e1 /C#ø\LZLߖN׳P u)+<|"!Wi!3ܺ5Qr:<;\TA,ĢKw^")#SLUܿ%&̃I|6y([Ŧ۽2e#c<;b M#?~ _2]ӄz!IWu@l('o^@ro*,Jo_ڦE@`J޴\_Lד '+pԭSO(v&NPo OL#n|ȣ'[>LJKL)I=dz|UMU};5Qyד>O呋:_ι+D~ȿ=OUh !Ʈ, $XA.dpxѫGozewO{'yKRK1eB̖5\@]lSfQ n QDGGx#)V 9$%a LQJ ܸ%fcE{ŽΉ14)ء^ֳRF{7^Kg熐+z7޼zY3 ڕb~m="ARoT0kĩestzHqjyhrThd{5oĄE r1&sNa0dx~y,G>;,@舀6 ӊv3i!\3 ?#{p;-@x2tLdSq !F(5*7Tb)o8+)J@dKC/~DMk=ЀGrֹ=ïBx8$zC" 3 3DRk({07?Lx8U>HM];kV7̶sʛQ( 08HU =l 06TI=] Ӄ{@97؃+2Jz h>ۯ>3:s::7 +˚4k@ S V=h8 ۄp 7 @>?=ƃF />Di$xxnB>:YC7&D9aG 3G,@I. 㼉UFR̽YUGkBX4ł3s!VWĻGEC ! `<ƂdƝd~{=fȐ.)[J< +w/?hGa;qľ>ܓ:oJJB30H?u Vx?s:da< &ӈ ;[tH CL>8lλ8 @4ȉ#,XAKIghl?H;у]42 Q콞tCjK̸+zܖ^A?nY+w?G3=3GtTG?Gܯe++zŌEh[;›1 U⚿L \腹Lǔɴ06s3QIӣ0cL<\9Oг;SM,)H8>[:66Ծk;wZ6: !,Ȉl;dI E=@ҠXVTˆ=[]FH|<=QİU( 10%Q;~ўMMܼx0=kOH.Ct(pt4'30Ny{O7A UCh,d"'=V07[ wU ]HH-#; \SH28JI+7K ]+MQykITcGB)+N봯(3t5RQ6<Ӄ"N=:WBIZcqc;|LH kH{uɔ2<3m#;f] čM7Lp(<ɪ {FH 7sA&W2EWC5:.$ Hxڮ]<3َɈkLӃ+a+#-SP%JpuK7:Suxn=CDkY}<2?Q =F(o* aDi4B&֨Y5_%1Va*Ӵƍa.abdJ(N)V@ .!3Xc6nD C9,  / (dC>dDNdE^dFndG~dHdI U kCdOdPeQeR.eO& L5IS~eXQF) hk0Ye^.eZ `jhec>f c9&D&ŃJfjfkflfmfnfofprѸl@eu^gvngw~gxv8Dtg~ggzNQ`}gNxgU}Vhhuh 9 QAudhii.i>iNi^i.i`Idri6i=ih_yj5P"jNi^5jƍ:/j+j ֲj]9#%@`0 1 C">$: 9FEyV)&!ÒЍNQ芓1EMm\2mJu vō셹&@FNRUhW;ƹny٤%Z1N 8geza>JRU[JRC : :9>'iﳂ+[doQao I5bnJj nMN/ YsѳBO ?"#`o%pX׃<\Fk :  oiBج JHB>/T d% ֤)+XFwR>22tr@ &m9?`vJ 2Ӝ%[7f sG vHrg3d䲡,+==qg36Tbœ- ю5&&H/2uX5b ;2 jT#Vk_D˱ &~aOiÊyˎ&8ST F];"I{/pݘLHuF$:mV'͹x'Hb<+&>b.CBmM6>mPQmLō̆(-12~pgUVpHkJ$ q q֤owvK=9ミooO>V oQR2&G(p-x1Țnw 1:sP43:*4Ya+L7iJU|$r3ЬA zٜKOJ.灅1OŢZҔAkjA?+ELS7!}jkgnڙ(Ya޼yѫwo!AzɃО2.Z)D(y9:ѡUP>w5!ҊXjUX.LZ0%R/*3mՍR )R$F;~PAzAHUKWaՌ1i%r+[G֞.) LS1cAU:'Uň/͘˖xŅd&4A4fr(j-UjDg}ds2XC)E-wh7}C$H4~4n@F\W^ YtRB1Xdg[Y)u&(GTLU!m5Z"uԀ]to6TPTTaTKr1_LuXz H=FhruH`Oa!%L&dYE<7mќgE ՘RL!TS}Y_`ZtpU]9[ޓ29f=M!EVCeJ`W 1gh%rNvgEa+)'ky DmEU+V-6bs^HFog$xQ4䲆UFQkkQt_|.kkYZK[/Օ~i_)ftZƕSRTV?G䑦\¬PTYHn VHMci];n!4gz ZxHuPQU!{t[1ɒA8|IUbF9hqJgG㙑ia)eUg[ T)IE D? f*ywqȜ7g&25,f!S|$gIHLZ,+s035ALN+T62ihbB!XCWBB؞Δ`uQK^w02ܲKp1MNC"!$x&A@4_Z@2=oX:ǶX)*Ydb+Qo2 ~+Xvl,2{%1b" Kz&2$pN! q&(e| .4ms@6 &=! f jXqT0.$nO0](qBLU*XH~B$>e6[ f8ydD(iF4d/i#Π&ιCP4=Sц ɠHmWjbW򖜼@Qъ~Ͽ&pgѶ+Er!)Xӛع bi,S| JWi+tsO=Y!ΦRX#D,SQ@CURe͟~"Jwy˖vƹD($YY+7ɬS! 5Iy{q xrͪTO L+O.JeFTH+#MfӼP-)K.#*S锁 pX物>MD$(X8ܜ nlN/, '|" ?,UX"ĦTKX*)6X%?jo(I Ytۢj%_z8[XYy%z G \u6.^ %?2yM)]O.|]K$z!jE&<5?!\j4S&3e^4E!]8Ȏ~W̝IūyҤir%aU߁-/{z̢BAHNg dlq";WR(W%{؃y3݌9 m]= #ZBpwZ dALNAz4N в[Q4)rK'~PKA79CZĘB |cOI(3!\TsNݭ ,~"*Q~"#y~dʑ H] L<[)]s$ef+&,Ci8T + 2i[Z" zșWѤ3e<֦[yVoO lwU j-MM)m63CBh?LvLmvsIcҧp4Ks{R14d; am_kSr{wCKPjra=7-ncbFIbJuƦ3YޢWuI|ZeVyaW!u G0GY_QN)Em=Uh ]E6qJYڎ\ uhX QemLI Za Ȝ`xmɓII0ҙ8k [YIH F0,Iš;ųձ9 AX~ EL4YpHfDшlIdLmx]I`Gі%ȉא` Ej U`żLd<H}X`ѧ4MGcHyÉZϽEf9̈e1 I[eZlP^JGY dzN^(! X^TGXm"\fiaNmo8޹gI7 IN\#as9Ig& vf״ڍJZ|VQ\ژ1g~fF((ځnHAI LQZD^ GQkڅ Lر&qoWE:T.VĕqXQ'nH-Od K^MMdĤ̤g$_$Q ObP6e!JPR^[4V`WXYq<aPWmh`!%p8.Zhvm1Laevx^cmz^%F`ași,cDfeʇe0^G{1FJoXh$E ,Л-39Pg5]tQ6H7<mF90DS<MS4n+{Na-N \D}YsTQP^ؚFGG5jHzp ǫQtٸIfxjCUlMQS #I#nSj#?x'r((r))'{E+r,˲('r,r.';m:=.s0T 1#';ZBa$s4࢔H5Nas72UnUz82^s:šNâkM'k s==s>>s?xrr8_*'A! tB+B3tC;CCtD?x^AKFstG{BS?H0c=hGtJtHڤ'_LtMMtNNx2D1vnnvoowp6ulԴm r3ws;wnw@ȨT(scwvcwt6E.g?@@yywzzw{z'f6Jz,Avkwk4,R]v#x6w ݁+x+8wyH@kv]x{xw|qrJ2~ ?$PNSox/7sx/xq]#yCwq?&*Eb8[yjxht;ysD ?u۹n1EsByS8#P9VeNyKzf)Pj69w#K>O׹By bGxz t:KȲ[x7z K*nSEvVn( c0C{C08e2{vAw;s779ĀwkytVŰhj߸D;óT{qhz;@K8asӁ9 \˗O]E|Àöo߿r;GAok 7t{ ;>Aw{ݓ4GB@pnt;x<]A|ܳwoxÀh|ߗr+;ڳzqr`ro8 t{6֣S|Co·v {s'3ƊWy߀L߀ܛ/3I)QR<@8loYDLkA?UGa 6[v IIok<nCk=7C7|;mшGK:}>ݳGN,)Ij) ۋl@:,I?Zk11%=GpƠl`bӟhk(\j389‘,v̊ʏ%3PEM+_3N 4[Ϟ./0 ߓ/XB R7)MPDpH 6|#Q*CŎCY(#V鬸Ǿdtt].<(5!-1\31,A:5 HdXX 6{gsa@nn-_llMVtbC[3-rݴmF/泠T(0ZeLU3,&Y}V[o=2][,ٺU+QY5%Dpў]:O)A)CyBjBEcE7gunc/ٔbpf1~r*ͰkQ;Ó!On D:Ott+;ݺa( +!u:NG6%Sқ\ ' 6ȍKP#%*yIu@_ !|$.&$0k*$ͱ?)%F JAB(ebd+K͓/ Y@Q!_6=k'u dA)&Kd}Vd)-v.V+RZ95si4K4ت}%ANBk(ѹA~¢uNm?a%=MK04P*SKgb!rT4UA)SV2.*,z0q(5 %1DpR/JT$SeՖ{ŕjPzW8|8_R=M{5B|ȶ$&,$LpjJVAwKe=Y_(V.%VvA %`0L?RG?otaiP*&*&I1;,1XyURsDP5,"Qj܋hJ+/Y[;̃[#C )#)Wo39!';X< *A@SIBz+\VtHŤ6352zttiׄq͛]9+UTY0֫]$ZB _LMQFCK4h)'f`BmcI)5Pus%PR;sW˶Vk{ !$6ξJ)'hT. v CG7T0H֒նv* jdf=D8z)^q_x0GՆ! Pr)WYr\ &B/yl|% s> b9 ^^u_YֹuaE1T RK!q^w}xA-{C _xWE_<=-!7_zӗꕽY` wi_{oO`QV‹l|]6ډ~S^,ɿ뭪ߡ_~gwmF31g`9ϫ`CMF”P0^0!TB*P٨o*QkŤد[o0BCM4X)@:p@@B@j\O5o2zAB@A"o ` @\rp 6$$‹|E``Q]^re/'3A)o#G@&P@\  !0 0ݮCze0Fr2Ұ$ !rq ARz IGh@0R f /1 %/n$9R@8ҸD63Q\@4 2p%0&orB\>$Ahs]4. ^o\Lhvqh2~Q vo]dQЮ);Ҥ֮++r, Ǒ" &6&10GQ@B{Rr30@#C"sB:!4̮^F)B3E4/uSs@Ґ54Ro> B!QD` +tF/'9s1+"/-!q*TA})U3Ձ @,1<P5QR!z ^E2u3 BoV-BB11C 5;Ȼ'OoU"?31 /$#!5Q6=T'-vY5pq4h %_0Y%쥃uoAtSfQ@MTw&E\M&+, !fs_`` CYDH/r$q& obB-G%&13v'Sbh$lp/d/e@5?3fHb)!6OYOiJQCKWR5cB[q6ul@AXvlc>hc/VnPUOU.PA P>f9^WHYRuq  D /21KUF fKsI$UVi)c O#S=(8?CW!Yftӄei$jrfkezeo]Ĩ7J>4 wow}~4y>.KVLsBvY%'gG4qSXT/djGoRUvKqZ+7>s)6q7%ms24y%IS#MwXfF6g{N/WW=wLM@zakls]$)bec?mP,á<b1o.5wGwgOoKWIQ zL@ՆA "9!撦ke{:r>R /|t5E~{wڻI;R d;[>hOli\Űn.B:dyj! +~Od{,h' Fƨ|К(m‰M|eA* R==zLQDDAzȆuD&Y6xX!ȺH 䘲oK_;DkvhGƭ:eM&D71GiI"=3n֤M^]n ?CMNHԘyV|$Qi7StQ_z%=envLdICmC}QtQ]-hJE_dmUX}!J׍2dCdVdAyu~twfeQzᇘV1u!9MGP\(bo34RVLMAq$irF`$Fr%e`6vQTW[DVHMX&Vhy}`.qd]DYy6K~mQ5PMvT ҞHAY4_ UԦ\'>E UNHv=!TY]Z4#iiJXkѺ!vj-Ɵ]EՐS&lQMubϝ_:YSl^fl)}dBhŅ[v5tUOeѦzG:DK'LjNtB%V9ߢ^TsN]TZ2u-z5iW8zTYG`UzYDAքςr9|RA}G8ZGX~ 59[Hi՜!j?ͻd`Ovwee&TwYUZUҫv X̞^s^/_E7%Գw hoz:|P^;7{LI^HbA0>QۭS1fAOLdjQGF"R":qIQ"La'\jF>812YJIN*1f~;ѡx6`[4V–  BdC\ػ Ӂ\^%0(.`Y.E]\p "6$WˆfTk9R9'HT$ܬdRC݆.'@Pyȣ"mc FL4'I'=L V7Q7D %TFAƎGFYTOr uܒ̂*(\| &ZWKː|4T j2֔RZY|Ėtj-H$Uty,oܻ&kpO<į-` 6sJi-V+jcCY%k+rN3&s(* . i?)F*/stbX5v3jR%HGX؏?)_n$-/W#j-jbVS4 X.bP6< DW)AQ4=9;$x17j"`8k;`rT111kk7s%鎇afy(a+!26dqO=[RY%*JUkKg\(9Run}aA4askLH8=l0`˓Bps6Ls*LȶY #ebeY[ˌlN4Kpi, H+ SKUUYV,g`ĕhTRw-,IJZ^COe2ljgks׶v=nߣ27D]$h}WgpW ;Ƹ4$͈I9s.-:g9]J8hmJThJ|6C_NI&3EL0Artp#%% ["4E, DΠL><9vUC^Q.Bda;;8sx m& e9P{";Țgg[ 3B|R>"p$C$ 2LA?+T";D;w;}エ}&<W2݂͒y!g!@.tiْg'7nhr8V+4%q2VTe#j 2.j.sr@R\I97uYa A"kڲEnX:H!TsX1vضDE1C#7AªT8MA"L!yOn4ofTd$A ^W)BCrX(+B6U3Ġ@B#`ʬʮʰ˲<˯ ^ "8Ɖ<\| r(<Jy\|͸,aI)lZGV\L=]Ъlˁ|d]ǰ|l la,.`ql:;\uruD=PnWC]Lm⬟)U*KT# 4>u*^/hdubTS]tԆ*6:4tM>{O4 *\ ֊,0ۚxǤm]ٕo1Nd0>C!!MV;<)f =tᇨ".-0)8u~1],-)$a B.>M j'@ͼ:t0ٍMLTn)~@P A,\61lt>8~W/j;=Ctm 5*4-LMX@,p v}tV楽훅A= 5.rH0txSR~A.D^ml'V> cVuW>̛^ھzXDM^|(Zl= tP P t nix<.: .Laua M-@i|[.NMu|̏N[ ft *= G 6ӿtP~JuLt.~WLR@W/!t)Vr )o,?MjhP-[m 7[KVpfV]%- |6?$~tph"\p#5o8;%d5鍮FJbVV]2biX6s? kosuoGt y{IG+4.AG@DPB !{̓{"H @H%M$9M`jd;hy=(ދcM>NUUSO}"j)o޼{bF,Z Љ"@+m}ў=y{8ڻ#xLUr ?Ɨ_={ջg=xBCfkE[k``1w%8>ʁ/uY_AipdB*˜͂_z$4 ؓO:e~mCFnID#Tf*CiOSJhzD] [X0QRDԔFÐF1aXwd8ae+Ĕ͡ m2rInjɡ$.\eC.XqYLEbt+> r|nRÐҬi"N&<1WdphI$iT|'Y`35Jlq' zdV([Z+3eG,r{R PR,uDxØjM$A8mҩR锭fDs-WV23eԿxPD>&o1CP1b4T"d몫Y+u a:p}ޑ8M`~Vdu x0KP{"@Z lYr@',FmnmnMs+g:ߙdEr ; 6 +Oڡ{yerZs $װ̇UA=fPx,f"BNzFp`7Vp0yk awqEgFsռf6jńS$Hg>t=hBz(XE((υt%=i>:"L;̤GSӟS pRXTjVկue=WSB&`,+bU`K3v.d yrr7<ҢVhGr261CYPxȧ%},.rމnO,la!nx/x™ss/@zғs3Qk6Czz#oϸ%pu9\=X5Eۇ 3(уK4}@NkrFv|WѐHWt=}'mrwzjwmL>&X2\W+pW=L/LO, <pΧB=|绠>ӑ-@5w6%} `{\`? {u 6qA {>?FЃF=?sþ>:{>c.H@4#pPs?I5ꉡѴ[L>P[?X5c\5y{ah:H`couopu'0pB¥Ӿ#9O#=klk= =K ţ$Å@3t<=: 4٦)S 4MĿk;P;=CD4"ڨyAa` \;;0gU=Fkl?ڻ=ks[Ӵx ,84>[|ԾiYCOkKC9HE4$?>s˦ r1D+L>4$;Qd>C3 |{͸BE?I?C\pDHAsS;!Bfg^F=X5aS=V3=pGUs!&byY-B(9*Ѓ)D'uG4Ǻ,C9ˌ*I#?[D CU DDcrI3<=#{H$CD:QD?8&ʃ4Z̺l>3A?3GȇS;Zb(kJnT5cʨFP5V BU3ֳA#щ{]aYvK>a'h>K3sKS}Ǧ % þJ:D \,; IA죿BB M@[@ܳ I9͗3Ȟt Sl??ɡZAɿӃLJC-4Qx{hgLpmNԽߓn#u0qOaK3+>3:uD£샂3ڰ3ĺp4=I $OC4FL4;,҄4j t[(RxA(m5p=4Vx{9q.ջ)y>4sKsOd+[]cTJ+H;ԃ?NԃJPIBWѺ\@s U1u?k?xy-X>W̸C>#cJ#=beSeP5gHVJVNs9* 5Ɍ08p=5-3sxpu73r)c)TITD XάOR!NP=?PD-;sJpV%3˼zUV4-JWBXU=:a\>Z?M1Oa 3_UV;JUʬU3̞M4bt;S(4A ,:+ԃ1 :6ם# 1Y$4sJk[MBm$ ϛ=Kp \TIt>93Juc#˲Z8; ĵͬS> (DM_S4Ư0JY`eN]5 U^PcN=нdR|w{9/MLG8u>*hO ˨-8 &+`6 Эs[T_A5˸u?JdJIPp\J}S iM,~>XM>$?`9b˝ νK$a+EG 9 #ӢRc2{+ި% XK<Nn/%:;3eL̓E4;^>f󳬣摵1(In9k( gW{ɩ"R\rx6#O9d%+ e}8jK[˚hn&}#+rh&" 8$ay5SwdjUhr54 U%NimHI蘖iȤ \!Kb_[t|["r:(Q3Qp꧆ꨖꩦꪶ꫞j >O9n[&6FVkVu q[빦뺶knk ]2ڐ2RԣfvdžȖɦlȞt|x2:&6FVVm3!PdF .%|) 6FVN4وC[fV.'a}&6FFo^F`j)O#o>,7Gp&rIo۲ oRK  ږ=)gq ϑnp]D8Vrr*!WrNjag')K<ut\2 XڐZ.s:EU#ً&/o.=2Rޱ$:,}"#nSG|މw7rJK)4$2\ >.$ I-FA۝! /F)\|,9qttѪ󔎙{}qG$O 4 : :YtoVB[ZFO%Z-$ɑ..;x-I8pibaxϜ9<^wfUbtv V':тaBT 1쎾'rk+rb:Ʉ|+y՗)$ޓ;o pR&̕.U/`DG"бj4^-e(HuSP!$tQ7 -Qk^9] {Gb p[)×_O2˻:&s˷RjOe4Gz:Bu9 r U)/%W t)?Z ~򫍧lH] .:<ٴau|"/UuKx(}[砳{#XO ջg=x 98o5,azÈp=ͳG{$H I,q_e#GKyWٴH ICy֓T4fNg`fTJﭸ0RmG)!&g $KTQߧtC)8c>&d`x&^OMz*X8!ve\bvo1SO.Unjю]qCha2㎳qtU~%kmN^T Աyf\NNw`>VeL%҄A=G\miu'-˩dE/g%e&Tz snOۗO$3ۑUj%\yfR,LH+2𒥡'AbeGdO/jɶH䭋D\Ys ({aV7iw:z$ʼn aZ ràl"FLT қΤo=/7Qn\A e7E!R”?/*{JG` Qs>M+ros.! VKfIàǜ[.v 3bIH#à& QRF}1д$R"|71GK`\S6nYDR)kkD% aè.}qPҶq!@}Iv'F$= Y.΍)>s> Pi PxF a AI(Z&Væ(&,tg?ď?ˡ lѬUDqN~ 'dX: YJ$BS1hOLtM:$OcxѠJc:),7XHsg0u;[x!yו˨aH"D2ddSn"˜`)+lt8aq6djY+'(iddTd+qILۛͫWK ?Ģ2#@аծ0I^آ.Ҵl6jߒD%uGQ|Pw(mXEMJ+|k/QHN)A}Nm14m9OJW:yg o9}"`%G:jWV(DFaa]Ce+~ 0'fvdfPaS\IPT͍%LL `~\kٓ~,Ԯr8 I0@_.wҭf(Oo0uv06TAJFZ̑L=J;;UiOԚ%IS3՞THXRڔm_, XOGVL'*6/AXdF٤*Uj]]a SBi6-cPpW1yrRbEEf{ )M<5J{Ѝ/c%Iꐔ\i^6RJ%[5mrdRCMsץ_qNѓG@1PV Gpe.L,#U',Z=Pi 4;@U^u47B+ TǸ 1f;Mz}oiqeI(89jm8B37%7 tzEG[VN kmڠm%][v8 )pB"1쐩qK{dc=T YLeNfRhuc{`KhԍlZ ~m8̰MtZ`rd=N؊P͋1 ȟl`˵Jq,HKaMnɞ}ᘁPц|PpFزԙ5 ͕=Xnɴ ۔ƢMY}QXZݐ")Y-ĹYPm'=R_^+Iۮ=x-Uь.505\<\qƐei^adI\pQϞ\c>9yLȟP\*1aZt  *L \ < {c\^)-g` !(ȯUKx愡AމLɗdRrUotDON4]|8 }dhU;ajJ N[\Jm R ΉLR>`6P\!f !xp )!D6! |GLfeaGlat!X}!YQ[JtIr/ .%M]eh{ Wtu_^4dT.]z;ΝD$i-nUx_P|E ("Ɏ2 ʉfУU iW XXMDB M g QxhH ՍE}D1O֤|z,=MI!eH }oO~rT~Δ!X}m cr8g-O"# ]$!%Eh`h'&JDS@ !ULXḩl,X4\n.v~.膮j3+&.Ʈ.֮춀~6ӯn!/.\(>"o"Tndn/v~/z/5Av w[dh>į/֯//-?ЎC0'/07?CmXrn"@o0wK0){ ?ba&İ 0 װ 0p?؛Q~1'oZC 0Wq09Mwqy,g1^Ĭ? 1q p[Y.X/  7?8ׄ@07i0;%?0$)F%_'+ be=2*2O9rD 2-2Ș1#/# /1t22_ /Iݾq26g pZcB̲--2"GD3?#,B12Uy=w=32cq[Hd\6'Bu2%21EӲ!?[1, X@>r4cGiGp LǴIOuE"J0AL4'B+R*}πT3E_UzNO4,2$MMWw|OP*I)s#OAMٕ[K$R5rp9_b/:YpH[A XO0baD[ A4 д%srQgkgh4hCCq[3` vUJxuU/6s/:I]5'6u^*!Nq1v4nC\H˼6@e0 MCN_~*FMN$vs82ɇ5u_uc7}K4N7 Ly./L[AriF@67۠hŠ3!;M57%?en2;1V"/5 okw*GbE(x]Zbrk8;eg7dr[WC {uu#@|gyuP`žҘﯙOOUhyvw0,4l8kk[04dFYuf#o7(VJ?, S:Uh:{ziI1%LcAIƴ#{u[)m5:k{0)Fo Ĵ[4.i ?'9b;:1O0F<4@w`p/uSoQvIX3oI]]V5/|jm,'ų7 XنWH[7PcQg3`G a˯`y 0;t˗mm{#<Шi/|mzO0`ɼ#m'ۣIkTEc7QuIPzz':—==J5/Ӷ<䂞 |6oy ?W4s;LF/LRc@ȫ'Ϟ=yW(>!F80:tԴFrڛg@{ DaG/f͇Cx<।@G&UiSO2MrG!y9y,П|ЦUm[9.ƕ+7[kiIIk=x$9eC Me=W 7hfď=z|8jOf{ziFֽѩ }i'qOjgFܹu͛X$uGt2WxSzXtr#iۨobﴔfK nM 'H8DʠN!+Ѯꬣ;2+#KF2{)HۯiGz B#h$1m9犢P1'F =c8G9B.:[˟D嚣1 #5+.PJ4xT qȒ= ; UY(LQMOL#K՟0YWL Eҩw7ʑH, sһ, M)bSN[P y:V @2.U`U͙7 QCÓ~bԂZNׄY ]IBȧpSVT鯞聇j:Jjؠ3#A^H gծ LX 58fZP,6;Dě%kHqFB) 8Q:aJp= RȲ"mT$l7T %@^F,4%'X@a0!HE.t#)[TC J6l'AJQ"?l2 d)aKYr lz͕/K[^+bl_8/ t3MiNմf40TYh(sm9љNut;NLrO!ZҦ| A?P5@JO\?yPNgBo:/'?-Rxr8CKKaSΔ5MqJSJh"0( )g0ή/` 2>DB܇ V..!!A"+b,,hnAXY@̜ Ӛq +Onph ќ̬*PhY+r˸f%2b%ZpRVl8*. )!; 8ξ"+@QI@$/RO,TRQ/oП8q mβY 'G8џ\ȫt/o(Y a9F2&Ljqؖ`j0L@z+Np4a`!1Fo@1-SЋ'-#SM64h.h B*f ծE\ +T,dCf䛼?;s$<爈DM.M\>*!0k,L0 F/4W2,*p@!pa&NJCaQA9ͯl$}*`O"3236;P嚋㜋e-Z. e4nM@aFq4x h"@t 9/fH1MD0&q;PlV/(ct2-Z դ[ 5VŬ1S% 5LmPeR$oU\W-RamR Se.׶/-Ө/ŵFu8m ٺFKb/'Pb?#t"||{uAglcICÔERH!RR,5'19q|fWXr4y!iD+HyX?ȂeaDfbyuY( (Q2ī;f}"`g汭]PhS%c)zC:H3QV,{ySgC?w+[HE+0$CEX>OZ[H`Rg6-Q+LCv[|7lp\L@JeS\(Ih#tk՚cC#rSwIT{pu /T?Ce,:7móZc%@.wR[\socy [kl8AvMbc,1w4¡vj'VֈU* 3FҘ2g<ƙ *cd73g lȒ?d_zr)NHF&bD+"@P8⨚Ȼc'e(ݜ^BYVb#ሞȸ@C$CLSFuh Z3_sHqšM;m\>ۢ;pCr}n@:9Bƞ);dF]['zŲgCW 3EUi9=A-`Lct0ևO#GB9Ydñ>vd?tM['V-ワ]0vhfjagQM hЄeU!n'ٿdW$)Xdȶj[55Y]r&|qG_ibfZcAd޼sB%o~"a FcEP5&PzIMh7d H퐫fgs[0ly,#$_D䣓g ~PmԞB!0dFLY9bԢ2Ώ Iq`0Ask&ː6!,.$x ,hS@I(= +|aM*7/ɣXD{#Z BY )&*ZLv|6NuGΠʇ%i&VѥZ)2WШNa"&*!7?›n8-)xud)R U(Č Q,z-qLR=LY/۴2 J1-~:f VE$y K\Z!8 KūDXP 5X]|TT{j nSZ a./ COBHə0M[g914y xLRrRVXhDT3QR?@fZ#R>58{E[$D> RZV$jivZ*'s6LSWVUUZ`jz׭䕒}2.bj[IT&@j>,u+jr4#ʵxVGH"N\q'_QO^#D0=4W9Ads,ZK@+emz_Qf-9`yIk;LH؊eM'ŚN >+bii;KyɈN>9~nПQ (`1<3 Ƈb]TEDg9}GO4MJwFQT?GC NOUmU\x! "ӆ$/جe"1V65qu\XOت1nWB٦cqaݨr, r*\UꤜΦ%UcZY2$qW1 mY,9HmJ#&0~P0tqqnJ"0=l x$fJzcӦl\tjBvɳ*=YnxsT"ugV0|y+)M%fߠp>DWgRP&.F]%'%U WCymuXU;"sIrTdO)3q0#$g07jc:]ƞ,]#'X~25bg1)>X'8"Y@rN6}/7*1gc5747WZ;o=G@?+5te>3,[ǂL,`huEQu UHuU(%U"$A(1t8b5At#S)b/S@UhWY[ȅ]U/saC_hgithQjq(UMie8y]\H$H򰇁(tJ-$$Ȉzxk&o؈R4)4g8腩4sZ<ס<eT @hȋ @4@@PɨȌ(8Th8ȍX`jj騎UE6Tx@hȏK@RQDx@ ɐ  )BwxGsP3:6jp̺]9 $|Fae ɴӠӀL]mw.,+ *{K_!ȌӀ9m %w @ q3 ȝ[><-ە=[́hyx8;lIB ;b9-Q߻}ԫu޽{eC!E ~y NK>.-1%i,q+]jig-d_Q">ӠΏM- _(;׀m^BnHtv|j7RRT>%jgN.amA ^,m4NEۋL3B,<:B>t8 9FSWɬ;@\gD%%#٥$piٰlhIwrmZtXO ^9&5L͗ |ԓ٭~vBr%yu|#a N.a&]qv<.cJ>.*!slt%3OÚ* ԛ:B\0a'W͎~B// 9?OoɷÆP[-c?lWӞIJR3+leKQZ7aQ"ћ@.dC%N|("Ax̓G=yջgP"|)Udҥ̤IK9YBѣBD3q*u#sW^N*e̱K5uLs(ԕ~D[pw\r5q!Ġ{00YO-g5^|w_~W_91jwavހR:U3IO@ҪG6MdSVye[veWc8 iڵB~i&`2Y afh4٨㠏vrAi X !Nc:W_Y4;C X.e;n6E gȹਁ8k{^H+]YJ'΂Toޅv5j?N_t P]pٓm5}klHfBʜAw,(.F[6vWKTb6 Ѡ֥-BVU_Иv>.6ܩL*4u;tW%f{w^{R<Ʊ7IzCS&<0+!I^ЃYغ4;촧! DN:s)qÚ7,#H;)3:6"بb61*?)7'A2Wd*K,@.=`) c#w2kI "㳻;xajB8|3@'8 *8L ³ 2\'R0 L`vx1,Z1҃r2/说A22"( Bɂ'Қ#R(Y6Z mӚn#>+"86["2q^5d*<%g0=S ?9)0%8/"۳2% D ъMe9CdUPG8J|0=@3'(N,EH-Q,#Ŭr>#FkE2CT>p>E'#+.,J#C+IDhFy92 $j0BbTẌvё@RC~ԃN$[ ԥ=kGŻOrd"p%(Kb'wD@ڄhUIJD` z!,h ,B.AD*:EdrIP9B&9c;2#Jv! B* 4L9nN)!%9[&Ź3x@ 0)7&8B!EY] #!j,6k[F0!$.a)3[аQP'0/{N?P< u#4ɛ*d <۹O]9eP!8+R&}1v 0м&/"ٹ5\Q/눠*`Q65"ҬLϠO`׳S9RP=*Q R0} *A h CTOTP UQUR-USUBA[XUYUZU[U\ZUZ]Vb-VcU^ D9VhhMV氭ـ JVnVoVp WqWrI/v*FS 2ʆTWzW{W|W}W}-C 0rU=XMX]XmXmXe3m 8(؇XX=؈eH#6X=ٓ?rlЇsYYYYYX!l3dtW(Z=ZMZ]ZmZ5ڐp yZZZݪ8vZ [-Z![]ۦPl a[ۢ-EپOnZ[[4[ [ Zm\EH q\uZߠ\5ψ I! J*1OPSi w9bϑm0J ^ܨm+ъ=e။L&LtM {>yT AU,#iɠWL쓐%޸:H4ne(uuPE(d,Ő1eŠ]IQ (ĒI Y_xjo_qR!9#T1VI[PY^  eIԪbT>c4e]`±-@ -a v&>>6aOCPj+dPb؀? Ȑ ȃ1+L:6~EuEC$k'*Z(RkP,(0a( ]Ps ,]Ka]=YT94`9̝$Hi!YJ4]Fۦ)n ==6 XwAc _:SXSr޸^=nbMfސgC^iDm TfJSt !b rY,{d(VF)F(Ueab &cd``34vT"}c#99im ِC[&e'Q0k3_L/!sb] < ^"yPX*dƠVcD)u=Dپ[[R w8fE=G! b=5 8 j>ᑃD!#bOٹO13fEI  Yv`ij zbE`@3]>1?!LXdȡږ>5 LN\t6Qޠ6ɕ۱Ϟw<6XK٨yo%i?[l=:%OdM_x& ΐZս0HBa6NJ#՞QQvdfN>d ]xֵFoUq0*[&4}O!km 6sAmRso2`$g2ݹL> P8ʛ@œќRwoR[ђ eCܞ悙(ըn#&j21U ]pWaV^@ϪCE&({ n 6'7_8WtvcFW䛉=Ad\WE[vFt% 6nj ֜St\]D!ŀYov($// tQI,dc_pw2v}\vGqkҳmӯƛ](wErXPruh !F]#]@mbfE_9idV4b 6?c&ᨮTc c?v;aU{@^QDgu g ^'FJ8`HDY^)DRsDvøm9nYodC)+r'Q1N}Dӊ:o\ʙ1*$YϤħ[%ΣS{q2'Dxr3kGy Bdv`EwrDK##Jg3m^;Jt;L\&%82mMS.8H]IMI3ɌD]&ZfsB4HgJ]0A¡>%J\ɧPWdUUQBA7VE(R\2y(TWM:tab~5R4PAvWbQAu$iEɗH+bKa}X6cY)UJWy Y_]r}[&!Hr-DdDUSHP@5j()hyaV!_ !Aᆑ-S IrdA_ XԓʼnT!م%1rLcաDbq4FSd vPCcY 8bBD6tJDPGVJETSQYU4gIeU_b5Z.ŖaV;vWj 5`u}cEeBr0K=aVM|iSjV/^FAAc~/NDaIld,6z)vD"GH*t/zb*/4Nsҧ51 󗅹 FPqЭӑ 1RIl$$Mq26%iw'~MIʡnגda)mHDAx1ps>7r!N~Iiy6w")I#cIFd0b@H5 `7C0'$oNz" t:L;8Mdrٳ6iqVRR2Aex'%AI4"W/E6,D,er粖2lQ*b-e ے$k\]Dt#x^[j/& 0!=4l[l qpO=$x@1e6|>Q)/ (p3lL]aL`U*w&&Pcdx"8#tfp=" DaDIQ}Xw1إZ)՞P+߭,mҪ. Zț,sT#+Z(]ѐe:tHlA\@H$-/2f$)*,VqTi|\MTE #~𼖢)nt6Y":%IV"9O1}Y71>dJ1F18rɐ4͟q9TU:$q 2=Dd٪:Sۺ+yɟ9.̲5@y mfMu]6e̘JĐ,,nB0@9R2l+c9Z2/9b3l39j^3L79r3l;9z3?:Ђ4 mC#:ъ^4GC:Ғ4+mKc:Ӛ4;O:Ԣ5KmS:ժ^5[W:ֲ5km[:׺5{_;6mc#;^6gC;Ҟ6mkc;6o;7ms;^7w;7m{;7<8 n#< _8C<8+nc<8;<"9Kn<*_9[<29kn<:9{=B:ыn#=J_:ӛC=R:իnc=Z:׻=b;n=j_;=r;n=z;>< o#>_<C><+oc><;σ>=Koӣ>_=[>=ko>={?>o#?_>C?ҟ>oc?>?sF??o?_???o??` `"`*2`:B`JR`Zb`jr`z`` ` ` ` ` ``a a"a*2a:BaJRaZbajrazaaaaaaaab b!!"b"*B?#Bb$J$Rb%Z%Jb@Y5"&z'%j?p?8b(b*b()+b "ub,"*-*n"A#0c11"c2*2#7DY) #3J4R#2:/?Dc5rc7&5"-jc xc9 #49#58nc;7>#0j C>>c??d@ c6@"dB*?$6Y0BJDdCAF$EjFZ$4fFBv$<~dHdEAI$J:ddK>$FMCM>=L$LdOOYL IQdRR2F:eAeTRTYUZDbiVdJA%G%}eY dWAZK^N[.$[%]]%^&^%_Y*^f@eI?fbK%a2f^ SBfddReeZf>GnbbfVj@@,V.gcj@Wefkk]&l dt? mfajgfv&IҦk: g>ҁp"+AdrRoYX>'eekzgd'c.M.D{g|gdz#}C$\gBDBw glJxX"{BB~{>t@$Chy {f?*{r"{J(?>g|?6iZ6恢P(E'v6edAdq'$dXcX'Yhgfrh@ق2?Yfhr((>꣝i>Bʙp'\Zt雍Biz)RD @g^堆uJi}*j>.)BV)?{v Y,+t2|xbB'cA)t*?J'0B>›FB,'=ni@gf~Fk&(\ij)C>6@?eZC~)?髶fo>kvJzD.k&{,J+@I'Bgrrcٺ#nk>Z缒)'brtl?֫2?|h黲,@>)c6,gJd*~lF,NjlJ$+K*iuR>ڪ-B^'ꩺ̲g?j*㯺?ϲ)l&%նluNIZ(i6j,-Bfm62FDVlm~gllFun?mb?i:m%Z.>nH~bjo#N雱*?Zg*V~.?g-ۖ")@o֦FVi+b:,TBk?&t\nl>'J-v(H/hk 0 7on?/f'ndžBBbBz 4pW%稫fppn) op/), oDp2A Zj0?VuZ.Oz/?Z;W&W0Ӂ7.o21?+n q檸d0"-8r1 0@jorO&Ǫ%,"n)>0}"$s n,)#F@(k(/#xn3'rB#s$v0.+CrƩ<)^ S>n5,#r?n/6Z @.$Z+#n r桞s$.n6#D3'2?!#}+@S4MWFC*.io@{rf(Do>,H+<);-Y'u'/n9/>jC(V+JOlǩuB.;4lN.5v*.*3:V{u+>IWJ5Q4\/df(zmB:cnDkኀo>(`/v2%&L5vb5@ҳ񛉶sR힭3gKD2@rv²n(vfp,<+4p鮆Y@|7wevJ jq=6Ru7Ki}?<(B~>2MYK5H*wso/vh l4fYJx:/p Px4lg1wܪǶ*{8119ws -af$=t,Ep 218HŠ.l|'Cdgin9qg+d6ɂOJnT8][&LƂb[8 dvh6~kWy+$RM.zY:XLW_8`:Ku.1Cu+7[:%${e5^{@ŠlP6{O>{z:r-禯odjw_;+'#zg B{{"{;dN3|'}9@ <.@ \=BKY3&<֯{A@A={YG.t=Ƚܛ}[c>ӽ韙@D}+}=D# e?/~#H.~4H??> ?G?P~}<~ط?gH{kB? 4xp  * Bp Ѐx2#<aQXE+^YE/~aXF3iTF7qXG;yG? YHCT"HG>$)YIK^&9IO~G HYJST*Yyʓf,iWv'.YyK 咗/Lad1^0j! YMk^6YMn,/O8Yl~R9Nm ;YA34>lj蓟='8K'A UBP> Ac8ψ^uD-j!}(Gу)%i>ORjtlKi јRϠ5H)Ιi8TY5*PZѢ.M)TSn唪GԬFuxWU󍕬"I ֎rmYUv+H ק5}_ӷ|kW X AzuM>,B!nV-e{Vn,aKnTli={vVjluZڪm-bY-qi[&ML*wеd[f.&*HoC v BދDHd 5p|P-oB6D ;h ءbaðLCrXMb(F }DA } Fb& 0&h0^ d2Gx/b+P(G7̤Msmz,  p!n ș\~rCl9 !v ?lF5z @&5 cFs azp@Z 0>B㏞/޺{nQOq/Ո(kpY^wfcdBsЙy ?n&zoOcU/0|3~C 8c^NcMkPnv" P疰TDj Q ˶("DЎP0ɠl Oo mW7-D,G{no$ ES̚Q!=;<-j0u/I%?tЎ`.dsN=:"&D}H,NL1듡pu$vuLʱ͖S$jĔ2#js4u]5wՊXmbQvTcYJ6P5feIf5gfS6azew6ffYKjVjjjj!i kkj6llVllϖm6memVnm*noіicz|o'pgpypsgvwvwrG{ wkpVrr'sgsǦs-2_t7rT#DgtqD_ZCthprPFYd{{:  LugFZuwc R@xz3bslY d_HVRw'fsg6dl Top84U~8V+ER'4!R6EGz.%mR#sB|GJ(%"I4RRQ@ lf%_ .°f &x%sv8[&Ƙ-d|bQd{{ *['Z#Ěa ١M -CdS'Pe;#LtaBt,ë;庉d"tˣp׆Q"U&a9):Bt|@%,|b,YJؾa:n  8(x ]?.:L* |CBƔ_Ĺ7{C_XpNE|=g~>j~r]pG]}5W #=7B[h8rހv^Jn^>[f&~ks(͵>~ꙻUhվ>>A^_ _#_'+/3_7;?C_GI_ `R_W[_c_gko?ຘəzߕv?)x_{S__@I*Kٿ?J'P H*\ȰCGŋ'Ǐ CIGA)b\ɗ0cD9q%KeYJ[JTOHmN$ӧPJ QOBjy4)[Òسb[))ջxuWoŪ 8JFfł-[˘3O{bac, 苤K<j'/9Mfl{L9?5vqKW_̛-6o牗{ qw ]QssGd % ]ɧ~_&LMVaZLQv f /ԢiP丣4#uz6ph$$ѱBL"2;ɖ\raV$L%z׭xH &8a4Yv@@mEއ~a1BG `*)V uDhbI h_҆ !u &j4gZ;ja' U)S$'0I`)/E9e(,Lqĵq,*KRi)tIBᆜT/BTDEAyGtБ3l(R$~@,_NV( iM%u)elY*BlğJ3xfJ>Xt>LaAHӰ> ,mGFU UTr{&U{?.%G{ ʚgZ7׬Pm[ϨY+4铱0 "5Pn4AVܷ&%R,t0"]h&ej7PѺ1k޷Ӝxhﯠ_9 ??#RpE+G+QKl:ILoHv[{Qݺ^&jqt;*ހ5NXGk[@\G Hru,+Bn\kߕޗ6a0F3T 3H ;A 1nE 7EĤ +*)GN8}Dsź8OsVP$RS!#N&ЇPv$aL ŐR1Nz4T΁,P5'c Uȶ {H?nÕf7/&ECH"5 &T$D%FLmYAiJ-o,+UtⰖ$.qYIǗIFq4S`FҀFi&&2Ob} L}T8͵3Xd-Pwc"3ȒG[IS&p I1Y6?ag]*2w|ah*( V5i&3H &q{ZqJjݗUzni%Ǿ)Z*A|W (hƆ6; nqѸx9EJXlV,Z*0_"Pҁ@:Rcu40N ?41*PvNޱsAiR-<@L\͡er?p֋X8^&Q l:Z*Kys:-xs5?O=fD[)HtbhtRԨNWVհgmnHεw^uJlm`sd;ٿ^u jW[)n{MrN}TaMzۙy'w'Z;Ļn<w3q}o u._: W򖓻!C񙗼69s #.W8 o]:сtnL6ћ&2GtP uthO;ڿpwy v]jvi՞.p/dO?}e#O|UO7#Y/yX s[}~u<awa}[5`; S@~J|K~RB}/?x7 ?"[їfy z|y0lW~׀m7|Vz'u7t{f{yP% W|}s|oy@z5xx ѐvw;z[ wg~?hP|| gWyw_x^ 1G{P{p'`du7rpW{ %`}oyw׃)~fHo>t4(n28'{.H+{{g%0؉f^9(o8o;gnuT fX[hvQ8~^|跄h|(orX({q᷄_׋ƎfX8hzzg&Xt'Ynx%v^ )>@z&Ѱs{]Xȏژw0Xxo엃^æGx&97y^8u<Ɂnhtx(z+('f0^/W)9l/Y,y%z%09"9oho( W%9֧fKYI'y|x=yo?yx ؎ o瘍L7u?vؗMǖ&(|j %({ ɐm]iiVtiG> w~I9|ux9lxKg|)Vܙz7<uY{qxy`ؔw Wg)Qifw{)~4פђŊxyXjJ8밖W}؂~o8ԧʋ~%@~gG}@H}ǮG(wfh~ˤLzI|hxچ䘕n:l)YyIoyEfvu׍7(:'G@B P = lφ "=$ 3Pƕ%,.'4]4|0<>@B=D]AmzMLN m2mT]#V\Y=\6סF}hjHMkPrR`-xdZ~]{wׄсf֊؊ p=ג=]״l̓]ؚ}}ٛٞ٠ע تBД۲m_]ڠ}ڭ~}(}ٺCڿڮ԰-=״}m؝ڝĐ׸}%սi-P ޤAp Qi];͝b =ݔTs=߂bn ~]@!thߴ^#!^ M׽tbA6q C@q~3T[iP^P>Π ߞpt%.ᶭHPx-㘝7DC`p`l!>A['^۶mtc棍rN@ѕ~xnytpM K~qSWP}o.x=7 nθҕ~ QMx}!}Ce@ȭQt2.ۆn^Nݿ~>C`ўnxs<.1.7a-~2N-A%na]龞MH~ ҙN>~y >6_~~ޚ~.\] L9 '_/?in@j^0@~J{ρO-/O1//d4M gn@`b<-hIW0_p/+oXSUoMZңҞ.^/ .֕N>ok} T>]+qc_~%Mt-=n7/֢^e\ ˿֐-sSMuNn[|oAG@t3!B >Ȑe`"F g8XP78ARJ-](!G޼y:=}TET'?6Dԍ\uG8&JULj K$ !H "ԵmX`)iv;R'Oz:c@"vTlNWR1/.#ٵV@f?]„ܸwl{2e~.|xlemcխh5v M>bApW^?qʡ'kL:H{/ Ak AP"踋KABA6iNi?8K9203 n;o%IpGx} pϧgy矇>z駧*{x|9}߇?~秿~Onn)|D%Ё\  @ /Y4Av}?@_+B]OG ,)x` K8-$A`xCFʠ8D!Pk"ĝ &2!䮉WdhyЅ S8<c(GF慂3#BGGԱec8=#Zɋݛ@vP/`6tcG~ yb}D(F@ayإ Xj^06# fhA}_-jA=8 n(8ȃP\.w\lױty('2okYx֊&: b k<@ZA ùB6I`4ul42 yma( DL٥Zt&1D?q`1uGc߸}/[y\V.,K9WbluHL s,~s]~ f%&]SZ2WC- [X}&FI(&J]7؁ ^br;؜yy>B=:l#7yrJN%q,yl-zfx'9εNڱ.uz`e^˻oHmCjqM/[(4 /Ό(\YO(p\lc9Λ@^*%*yy wkf<\{>3sd)kɛQgFC;zNoyiV])Y〳^^ j<32"c{j,u(.,5-  %f,37(){:ԑH&'8&)7bB:׋&4[2e=  u+.\}85}>'3r/0B*D;]˯5lK9بB+JQ<[:C:BгN˝nH*[$+[{Iz!!>AtS06$ -a6%$XJYZp5L9_ *Fs d,ʯ;jFnGnܿBqGiůG{4i*3c>S$ B5CEDUEeFM$x_T S'qɊTST -i]ؚJ5 qW}=TZQeRݕS=QբaUYU\ՍUcu$LVe]GH`mabUV9lMVoV׿hi`։oqlsEWQWavmwقX* ׃X؈ 1װ8jor5HUjOxEٔUYu1X ?aZkVhي>}]ِR!4AIiقN7~iX]UX~XY ɇ!H1@%5U pק= :@ٻ%E[ WkygݕHH5ZGC%另uZ[ًYՎ!}khܖHkPȕKʍ[ۺYZZUۛ툼ŐQ]G}]]8eVՊ8ݎ\-^E\W]Iq]m^IMYdE_(_y!ݘ=ݙYߑTܧ^Zm` ^Z14P9_U,2X Reu^ ~ n} ֎m[[8 `! )8[F߆ ظ- %#ZV=M, $8#F+rMZv c븐<3N@#_Y,Iwwx^s dv`0a`:0\JNdܲ}Fe5nRb)8dD~e@ ]cNFb(Jm Y~-adeTVheW8}cHϰ[v |Q`hgevSRy֔gh{ kXmivh ag8]ihU~MfUzgpG.f4֋@fy IO~蕠鶵ihig&h.ZvjiokBy kyhe❦ԗ5޽}j 6j6 a慈eUi0j`]jEea곆kH^!f7NE^(Fc~d ٦#i[~>Ycu @)lf,h ̎N T~Fnl&  3~n^纶hmozy |4ȗ 8f&JkTc=o6pV\qPhsp/ lTq Oq@pTai͎q ׏=&w'б+r,+.sљ҉3G4WsԙGr\:TGGȨsE'yltGD?@tAG3M4$tI?Q'KWFssp$!)K)"+¢\"D?(#G802](Oz,b jr#4>摬aw+F59#cNڤIvcOsck`@ra'vy$Tۊ%rsYg%ABpCbrܱ4e?`ul=w92$c@Rۻ(bsQATxr=_0.灳o(!SHs(nP`yo*/T"R&vZ&8{'ourwɺcJ'l*1\)\W"]orsҮb((#0mGk?+҅ 5bAxMR#Bwê:ҧHdF$Ft> $/'ʀ'[ڼBzۂkrs7ۊ) 7ے)ʎ:W{;.:_C .}h hÎ-cāoڃ*Z T9k*yRoo,s9W}Jg}zk8<( AvsM`Z kYh"F#Ȑ5$ʔ*Wq8)`KL7wJȐua(ҤH :rkSG'#O(҄bMcБc<<jR8:0Pyv+iVS' lڏCpVKyWYrYȑ;j #22tVE5QyɐKʡ^IS ȓ:-ӄ2)}u>ue:0j+>k뭣',?6l5W,nyA[-TK ' 42zҶ)őNi$+e{oĕXRcWX_.ZE؄wK"z)e8R`|3j@V>QP ,B lQ4=l[F}MR EN708 (MmhV_T/RXMakyĦ#xoTԢdoyHXXb%f0E-/ˉ8R8@2={z3l"o18 !D#hYb\u 1Bui6.Bx2wʆ:b Q.h/T0#I C~ӌ>xS|eR z@;6DDd2ƁMB&ȝ "EhALKyRt#/BEmFD+F%CYΰlT'1X\c cX#sHO&B#4ieV-)RD%6.N3 o32g9MӬ&Jryҳ'>}!:q=(BЁt}(D#:gEEer %ϑ&=)JSҕ4܆Gc*S3)GehS42DOPMFU TjN%SԨ:ϤjUUjuFW ְԪY"XVu3[e ׸zԬMWu|_-Ԯ=,b2},d#+R,f3r,hC+ђ=-jSղ}-lc+Ҷ-ns򶷾-p+=.r2}.t+Rֽ.vr.x+񒷼=/zӫ}/|+ҷ/~,>0`aY!", S03a T0C,S20SPc,Ӹ61s|`8 ,!F>2d#O=&Y? )SV2-_y:p!1f1n>s\Hsv3=~s`>4E3ю~gd9C43MsӞ49=/5SU_zjY-YՔV 80]׾5-a1{PhH3~%lĚ־m]Sc-kN.6ӭuط-y[=}"6j[? soxӻoeps ;"?8뇳Ž8b }|;5|ChA @ɉ~9Mae^{ﹷ[@ .p @:•o~CvR|A(CH*v+w:uJ~oq#{ͅ aV:1xlG& k?ͫ{vCq]\?FG w}\@KA֏=逄 |;Olϛ>/tP=_{qo>;P|'~4PObdѼk[Їˏpcڗ<t1aAGpAݟP߸G՝<`-V>z^u\e`V_G8޾ ` !U ! R] \` ^[ t1tD` " [b]vrutD 3aMZG*Xl' H:ŰS;zluFBsvcu2So"/K{SFX뫚S+Ao);Ҹ;?;< d{7_ gTC=DyO<@go>'/>7?>GO>Wji?o>w>臾~W؊gX >~~>Ͼ>h ?'/?7+?7 5W]䙚o?Ǚi?y ڟ?Y3 ?׿? (@8`A&D8?t Fѡ 1fԸQ#CARgcI'z|rĔ9fM7qi,th>G&UiSO r2(Qf՚QeU#n;V`WWɦjK;ƕ;7fϟ^- o_JZ'E{qdžT l<9ab^ռy]xC<4iY.ЅYd٫i so7=&LtqNOUxK[X疸vcaW|= .eϯl Rѣ:CGpRe2[ ,Cu[WU]sM8 5₎: 4@\-N`%..Y]*Z:"ԧLjMDt^rYՍt{-6K7 B!$c[NYm a6Oўx)O`Yj5cu\-"y "5cyNU! v`9F[Gk4ibΑ9N#v㪏r/m[5d:N>Hd #[J ;{ {.HWSUE[@Q:"#1Nï.0m5s.W}s1߈L \})7:xJPd=a{v V(:"~zwٰ ]Q_7h^+:gӞHԨoB؎ 2ֻ($B%=l+E=pa_HAV@~ߺI|% 习@faE1Èz ^b9p!O '"^"پ mt r7k>1Rmq#¢@ Y<)r1h;^ ߥq)9K$FF)x,<>)A$ ҄<&Qq|rEM,#1 0k AJF. "f|ħOe;&)ʬ#A(99˹Kͣ/*Z¾@< l^~LZDY_ ͩ]rs< 5ft=OPC\e)eMH]5ePv&"*HB2tB4]'O'3䈈ȑ5*S 4iX VjUi #"M""{ꘟ2Dv nHCՊίt;˴S3ALݱZJխtNhQ:U,ZfZ'ZH^jKQ `h]׽l]G)le/vlCѶmdOmnh¹ѝnuvqo}=/m/<MqOv}o'rl_;ɡM-_Ϝ3yq@# G }]:v#utadK6ԧlG\]Tӻ~P8ʾCLm>p쵷c7v]UoC{Q/٥lS<ԕl'!2Wwd  }zw v;(})?۟;xP< ?pCA| σ>n3'>w|/η `;B7;"wxgg^8vc"/x j&`F堍/O| '|`P$<dݘaNo ܄-֭ j@!*ӭ#8 | J爮 O .8頎.o 힯j-.L!3P,OĐ:p>L!v/O .n0p=. я MA- O K/- C jЭ21P!Z !dP jj-7nu6!p* { wؼJ Pְm ON6 N1"q Q - PدH1嘱-jѭ' !J@j!ԭ`oz@SQfptN`莯 R;r!qK   P_ 2M#*( 0 8C!r0r-+7o)O1 NP$`%c j J`1e%M%Yr}x2qߢAvPo55R!Dz2Q)q &+ِli6ϒ,)ORk) p+/-׎ &t1렮40kݼM2m;Hr&32 SHzoN35] (bSPٰ.8 C.3ގ) @*1*0*pusk`C/*HOB0S!&-C32 ?5jJKr3SS1 t@OJGvQ /PN_w.n*n8VotrurݷC 8v5P;= XI-M)we-xA8]\؅ eXG؄qXQxU-|88Y8J vb?}d0s٣5JиNx҉ 鸎Xx988 m}!y)-y19=A9EyIMQ9UyY]a9eyimq9uyy}9y9y9y9y9yɹ9yٹ9y鹞9y:z :z!:%z)-1:5z9=A:EzIMQ:UzY]a:ezimq:uzy}:::zߌzb3z*:-8w{m«z:zٺک:Ty YZگ:;;{Z؞zzZ[ڰ ۪#;=[5G;۱Q;U{Y;-Ш:z#zM۳[e5۴[;m[yۯ[ۻ?q{;E;׻ۛ㴻۷;]a۹;[ :ܭ[Ь: {;a{A<GA'ܾu-=\q?:22,/0*GHEA=8"#65014/0.(KGB82-JKH41+:85NNM665FFB:<8GD@8:6@@;2206829:4&#EB==:801/20+89421.44064/0,(00+30.;?;-.)884 8<8362/&!,-(::6DD@642==8.-*-,*SSP54+,+'<>:251:<6MMIWXW$ 1.+9739:7**'=>8343-*'%!#*&#KNL385<=:42/<614.)460>=8KLJ/2,'&"!/+&/,*<74EFD;86AB@?;7240;822/*443($ 896/3/ED?F@;EEA+(#,.*DC?0-*oo"''"DEB2/(>?;AC?++&!!!"""2-(###:=9 DF@040GFA>C?><::>8[\\350D>9LC?QSSB@"#cda[ZZHBF=9*0.πvDD<8UTRSmlgSTUTVW$'%053H@<~|w  #S4%퉇#)'hyhSSUUTV " zhZ &%&'E?:TSQBGDLLHUVUQQO382'$!#!"##"#"$$ SRR !!kkQQLC<8!,Tv\B X '3 %"Hp>Xc f P JT`" M ԱJ +TpB %k4y(ΗQ$aIH[$W#(MɬNQeږXeJ%!S` xXArVꀈδX§ m~@SphH-k$U#q w>3LuǧfՉ1Ӧxf`&a燣G589X౗<5GCpM;15Q15HDuqQX(5'KKH!$3`WAQRAGT!01q+@u:c\m $|LA 3). JQVPR_raA/52>U9hVUW wTw|ܡZ*!B55K t-0c !I 4 %`{ Ak+H;}! XwDAEl*_y{V&A]SGaj%cB{AIQBXc}X2X kBH̨A Fv=-J10 \`PR(|U$` TQ"%HV!j+PIv$:QtX۽* ×,Ƈ%E(iS F(Brˉ Pt3ETE@@I8B1ĭU)zP* -f)i & ֈb#]+b2EnR;t("=(DBე!! 6 B>ȀB0kOjУdU 1S"Ơ\֊Rc8GlU^};yPM@jql PqDGQhFN\8U{2 U8:%pv%/*\$hDQT)8D rCS:܋W$NT4d h@5wH B !D<@(@L/4%-;\I"Ф5Jճ;C#hf5 >\Ìf@Ḽ)#k` ݂MT!%8VTIq438JQ<@h@-=f'N0y hKF䅒0J'"3A"*C sTAB @jT/P&}F ":M(jILK/Iy >$q s%h`LGLOaCԉJQ *j[}x+Pt0J܁B`v !BIsID BR9Md)Ԫ  p H8H9tV1)c (DkaKC'89NXc})kQB69Ň= 8K@WT 8G!0 ԁ( P%DxcJ;J'p 0( {ַ|3f-_0 7]oP, >zo%.8(r/x^am\.-ns_ \Ey_r4߻OΣ omM_.tl }+G/ G]4wэu]aԛ4K,%_60y\&3PGOx!`7#D-eއg@_4v_O:a{ߜ.7u>8~q}? {_RQy=w}|9|/rLgx7tBqqPu^ ( o/xvx{j'{o7(Wo"h~sWo\  y0 %q7w` }@aYpo`0'tPp0y18o@ 0Gp=PG Gp7  @p~p(1'7oNpN y  7x~/x60qv: G `w0 0uPrG TN {/G 0qpWk  ( 6oN0 p0 P P1(PhxlXkpuPNp7xqz]~ =؅k x$ :pURppRpRr.)z3pR`v pp7P ~Ho' :ra ^'{)/`oq0xqRp k7` RPR@1x8I9PNwrPo1x kk :Ѐ'ٙ@~3' Gora 0o~Wx Px7@oh T(  ~i( 'pNq0R$ix#8yk hPh0G kpޖU/IpyR"?M? @ /p' `R ``)`W(Pɞ0x-pH\ R` Up<` 6p )1xJz`\ PE0N`77V~R'` p _jTvfoq(PH xxpЀ1X q0@ 9?J@o  eph pp1h  s PѶ57)`U jUPSP N7 //z P% zPJ0R@Fs0`iFa c7J|6wŐ- G=zt- tkh hpGuд 8qp8x($Pp)G zhO! T@'`kpo ~P)@6UP$ =P)=MpٔS  0tp Wʹ 0t{ Y-Xw0/pG' { Tpa:KI/`8 %@p :u0E:$`}pp[ep+$P 0U-0z NPxhp*K`vysp q .Cr% `/*{TBP/= z+ }Pr#Z (1quzpFp-\|%0GP6pLt ( r@q^ 5`(C 5PL~вh@Zq` @P0 а$0`/`xI eW\` h@Rx} Ne % Gpx Pz! 7KPkoRv-pf b``Ffptp<epz6B` SP=@ P~=%k;ђ)b(p t N5P6ju=Pv s@,p/g)h'@v>@ 0pp $p\X5@ڽX`1p B010+U%CM00o'/0|x¬@0`j(10@.kPSPpJ0oys x0 JkS&p `P5@+@xp-+ J/0`\ q e|ۂ;/ _-@"K-PCB9\ l/1 kEk$PL@seMU/H \5Jt }SS)0zzpr)+x6)p6+)G`(l Bg\H @N`y&L S L`0C = FP>ux]xpP=j`kXtu{=o" H5(>G 6`Sr@<<D\@ `j`Lpgcj )Upppx@ =t- F 7fLL`wN@ pIQP FPMN..h0 kepW)V1`7)Gpt) z X=pC0K S` rp/}<0OZKU `pX>+@^p8Mt x06`e6P ow]k`rpגͅ0+PH``S`Jm,/z/\)(kNj0 `0TPTh/EP Mb7~P@G7L7p<=w]})CC05/P 9 N-5pRJ1 f0S7upً (`1uP-S >1#F&*d‘ jA!%M 0i)-bLy@7Р@`d€)M2p"Sʍu4APDxzPs$7*%}w)6m ѡT^Ȗ Zp΄8/cBxƚ5(8g̀9Ԭi")qPTT&91@&ӊ GOJBɅBL2erLRdƊ)T9`c %U^9H1 &ԸB)8 U%@(tP!+riD=2త)6(L`Bl*XJ`RXl&L JH b )4(w$BRq3B, 1nvQ ip 2P,L X4DbTB JP IHE&.#LA !+4! ! i1 s@J| HA`/BT#{!q2V UBRM  P 98Ku0< WQx@vIJB05 UahBh񠆪B%`f&C$4sb >8@^R 0Pej ȡ,  bp)[H0*T X8pK6@@@M X)&Bm+l0H`.\Gȴ̰1 (Ka HA@P9GX"*`DVP,X  bɣXB $c < P  aY9`S9(4uЏ '=hLJ41 VB@3=b nW/k4AO@M](\ԇv9x:D= S Ï|Ѐ`(.(SX:dC4ML@C> $H!kXL+%^iP 5 0` y&\h, bH(9H$.azx @&~mpKlB (&B+sYp dA@hU5, ;0O뀀7@B!8F \`f *H h@ h9( pz̝pps.ſ`#< 5es$Uމfj5 Q`OTUf"*gV&F(!d._01=tK, (p=L H2!@ ^+!ؚ=Y* '4X<إ%cH_I4(#@9;0:(E႕#OЃ=O(Ȝ)7(h= .#@ 485VK&k+ .4O 6R<0 (#; ЙGHM*()8QX#:hJȥ03%`B>ȀZ(̱BN9k6(#G|U823؅+؁Xk,h\A:@]@3(xQh*@5X %hp9!!-*8;@Ehz0Kb;++8 u,DAH,hЃ>(6g 9 H  59@5,@IX9Mpub XC90`']+3 |`!`C@{+hCZ%  O1x*ȇG Y%J?H> ? BB "6h#:hZ3Hz8(6؁M`G2+xxO30@x=Ȃ,O ?x&8>,؁ 5=~L5`,FPlA%@!5Ⱦ@҉h2 4x3+)3̷m`Bx'9Ъ@ ˣ|уMP 1O* #I' {'5œ08Ap܂f+ Z(O!%P.ȇ&#X+P**،|ࠂ3 pH HHM1,VB%5zX,UX*|HA,, hXHP mSMHA8|(h$5h>6z>R =.@B Q +x>gD Te@+h8&!GP8#F@P0(LY ^(El3A1PI0I@ ?=ȄC@h[]&I֠#i,)#=P `ȗ!]O:OІ8 `0P#Z5Oz181,[+;83PN0Ep'Q0x砇,z,h?IXT(X 3؄(hX:(C1:XT+8k$,&F@9xȂ8L30Ȅ#Т2h&6Z*8ߑ9?i%h/A!; PI؁Y TMC+pLH;#Hp@XC50@ ]k̢̡A_*#@RбP"#[Jrj8>/b=p'ܓC8*$S%i]\BM( 51'o 0' %ZpȀ 1,Ѓ1Fׂ.8A8*(ZɾHH+nC-;,8\ U5R"3@Xj݁{< ,X xs=6MX?Xl|pgG]IGXفB!0+$x0-I(R= b8 嘃0Owc)!`MH:gF9;869 R0`BHV5e@@jG!eK:UN= @R/-0!'bx X%OhD%IUYGڅjDOHSm#uQPhOxML1x4RPȀ&b X1@9ۑFH4E0B]&ȁ965x4^id%h h69>{|ГB:ؒH5xZ.=% <&-;@i%R88".x $?hz?D8/};iNM34K}6H;5Ǝx[Q QŊ>W)lm*6Nة)!ȇ@K<)eRC(4  gG PENr8Ł L@$^Y#VaLdԧ)8 @o+E^Ly1'3+lIq'6xΑ6| F6 hPVVDg %ziB/LjC$S]0Y1$6dj"|:B hF]+*5(1D jiE!#,684H2k^3.z#PB$#qiPDϜLM*91QJE!Si!i(PpPV WĐ)у x@ 5,Ȃ XԠ8CxPeбYg/%drC4u j!I 4a"Et̐p  P NSUذ!k0kP0#]H"pQ$ =k 0HvHA C8M\C7pB$dP(!0BU\V4=X1\F}d2G(4D!dLpb1@lA[{@ C $B|Mxx0s` 0F1@ p~ I l PB `$PA< ڃ5P NP %d`Hd & '|*DMH+ FFC6X6lF!}A80pc x$Do_ QDT%\` 0XR5ؔ/P!TfG|4H~ 6(qCIpa&`?x~(x "nFƓ0FaB!`r Tx LQl҃H =e@&L"F0Bg- &F`  qzrV0Az0R\ 5vቁ$x\) aB)@7(hAxMp`AA!љc80;`oF~* LjǮOCH1 Ƞ( 70TakB!ʠ.a^[! Z A @J@ 03x~a[8+P-R@p'{B @ p; .6h#tap V0%kF4PT!zB& hPlB8֠ #Y@ <8 | A R8Ѐ x*2C Z@M,`'2 M "aʄ]H` 1 !l{AB Z$’9B'vI(C 99 S  \ RP* CC10  8\Ah ajH"F.WU.pLA a&W54!LkL0aP,@= ljfB86CĠ46D HS C0'I Ama%!qI aH"n2c=dHzPԲ{Z0$0\%1.i1@ q\ 6l0!UH &zaTDQ,R  $ D @xHC!'AzJPA H(U J(@JB>@!h x dA4A@x$g @=@ XA!'(mR!d@ T- 'eԀTAB d,=le d\(ATBà`ǖP Gad`M=C>L(x@\U LAU$`Įy@BRA%dLB&!Lla9@@KMnMP@24`&$#8fPAxPA f^ !@xC0~!AK@+0(]S p@ !Cx =n10nA, Lx q@BU"q JL dXՂ"xP`@H# G:XG ^'ِD؀ 8Ar3 )EQ 4B XM@A A @D5@ s)@*` A44$0߃qJ , @dXʁ®0 TA @ I\%\A!RhTna%A %ŭ_ A?st4 t WC@ ƀ) H \sV@I_. 20%0D,}b |6Z p dBQ= %(W(`/ dCx'@B  B'FAdA,'BTHG<ׂm˽ـ =,,H%l)8AVq e` mA€Dm)tlp!Hz#qfW&®!q T".4x &(& c+a!%CtA x@.@UώH#xLd?X $l*zg崷w|Ҽ 8 @Ӵ@ x@ A u&2$5#!A@  D  Qd4BN#BIsU.8p&C-e$'Ã=ȁk&ԁ'h*L|A1 gpK @ϥ@AoBBTA p\" , `7LE $ L4PD@@Î@Xo&E DhUføÉۢL+bA| 0/3$4. pTA@-@p+ ,@I-5!A@)T0P@Gt  Jw܀Hn LA)0 vW@ @@ A2@@1 5)١0`͕"b 1RXqcL %N(Qɋ%z^()DR 9kV yBH97G2 "5/eJG&d!BRPsĨ Uϋ`Vli~Ҿ,DRT̜&j㧆;xheEBV@aʤ vQJENvh=&~FjdBz J1a8Cr0(# O=^`FILa( HPovrBLㅗ(ϊ8c8A x;CȠAȁ9h`XJ h"s@bxc:6i3LҘ Hb92 I |Hz &fTR/UE d  QPCLR@! i Q8$zU Q' cMZ%+,KV)rB0\!*CǪ%)8v0 Q0!`@0Xr@ @ `ԠX`dzhЅ3Ԉ0p`hdj4\eH`0g2A&PoZ!`)5 ^*)A_@ !р`'\!$@4fZBvlT8"!`R(e !3 4G-0B&"xbz+3L 4$Hx[*-^R X4р{`\xpU:HXL*49<0M+4B 0O`q$<=@ &`I 1A)p*`J@E!ILS`B* h Fр3 QP8U@2! = 9B 7 T -k^v$~ 0 Pt81`9BJ0P +P`PA_p 9v`Zp`0Y 4@V@IxDp@(a67'@izS)ѥ/0*RyԦ$"7g%A.XE]P 06-l{U@=d"P!*Xn L/d3g0aqCO0 Id%Cm~P!`` j4B(J A`q $;XC 0!~ɒ(vBSJI~hj!& 2 Hh HA (@3gֲ2zLL9T8n@M@ Xh @b_TXB ׂ9`iv: v@ ``' 2p$`6 TC4Q!/Dp4o ! wkՄq=4hA.mJ$b~BV0vL- Ј!@k(Z45F gNr V  8B]:ML!x6)+ $s 1Tt6j`Z C>HBV|`r E >D' H3eC&L 9KP09h6ecB>@ App988\%?'TrxVMHl!Ft$ 8 9̡O. P/DS`@ijA!` 3U D y4d:5$H!LXH! lv v! ` `Z~HYh` x L #R `` FJP ]&ip8<@: TYb @ AXj\`X P< ` *!,PRC`4g Xg|@ &l@= b zH1 ` @l̈́'@IeZXptvAPEe & a`z b `(@Wt|@@`^{`Hz`B^ lV``u^`@V`  r V֫e 8I?l`^`  &@ &jv ^)n` L帠2,HhV LY ڤJ = >!~@Ja<@  H v V @2`0{NbPw @<(B`bV@ !z ʂ@tJIC@ l`l@`z,8`#;>2$Grz$D*%wHAؤZ8X"cn8` LV z2 N bG H V6 QQg=Vb a Dn$6i @ z v HFa 2n0_@L<0AZ/0&$L `< z2%@$)3 70"zf`a ЛJL憳Jғk x۪ (%K}9 lΤq`EK=h`]#L,+6V&`` ~n@ mΥHxKlǦ@ *Ԡx & }SJ@Hzv擂 :H vVp 8aqCn0 S,! Z7 BB@$ᶧ`\AVjc@C) 4 $g9#4#+p}`2 b8&@ T<`H[?al +ڊ@ zW`)VYK0A, O$BeO)z`ZLp` ΀$` B ja$a /`7*f'Tύ5DpBLzN0aBLAh`N@" &5 4kӆ#:_hcZ>lPPrPhGЋ8F"=+RTA(=}4Eċ/$g ĄJ=ĒF1GAP -X(LKI'8'fkRH*A@ LIaEo& [Av`OpVpʥ'4JXJ؀aA HA   u\B8 lOJPI ް@ 9Itr %"ՠik< !  l=dI 802*6pD2pɁX8d)AЇ*TB :L F@@݄ |;08 s`{ Q#9x%PBPHA 2NL"/p2 LB 813 B& -h!(( v2т*s7dv/pd%=\8@#Lr)(@t1J\xA n2g% 5B!r., @?=z&TQ C6(@ʁ/=/R *8 n@ˡUtt;P` ul -< ~ѡlʁ BMC 2u_p2$yߖ1@&g pX{=ܙB8L {q=7`< `JhA(=!`  |1!|@u)_2//@*d/@PQPxQpzPdfk8̢0'PSzxPdr  r ė rp ('0UW:xQx XP<EP FCP 0 q p@\/Paַhh @=`Uj6plSpB% /weNN0JxGGG(_ xFH8 e  kgb _0 ~@(( pPhzP4tPpP )y@rPbJps#՗_5 Wp %~ =[W }q6@ {'(i/s+'kupu@p'kzax`[xp'pIsvkwp :q ]ȅ{ R0qpRpW PP8:@G(D NphNPI!;胒U( q0{yU{yQi pkptpp p0I p W y \p {R ) КM陊q \(qQo ,K)(Pb=D2^ f_p $~0 P0}y+$ LX0V `@q@.@ .`*` ip 9ij*Yɠٟ i ʟ J IZz 6:&Z)JP'PM*9E;ڢ>1J3j09&F 8PPЉ4}p 1 j/:Оp@HD  !>z>ʩ8P!ʪꪯ *JjD!aBF1%Q u%M;@ uP+0ӌz 骮ʮ !!@6 p8J`8 6~J0}ZPbT@^j˱!0Ys#UH(щwPL[N`Cq` ^P A+CKBKH J)0Y1P\PR v`GG0s 0iRr *PEk˶m붮z8@ MB1@EMp (Lt@6 t6| p ko + Ap MA)$L[$~BtS0 P@%`VKCh kp Ъ X~G@q@ $UPw٫D(i=6@7j `PtSp#uP/J+ޛ-7 [~,K PGP pg@'||[Q;0~}@wP] (VV0;<,s @SD):@0^_  8bRxW+(Is@<`k@^t~" P rN0Ylw x`&:%}0p )rǓLm+G@d!S'*L ƴ}(jL kpx :ʯ ,4Mp0 [0 0")( u *E:?Gq`ĩSF$^L Pr+͌ܫ"0!Li|PZTCQLMzyк{t'ـ@wtl @f$qGMP0E98XHT): tPmHc)S!5pJJ8A@ HdFApͫ*֯zz0% E7PTō&jT!}TݪDPMmԦpHp1 ۫` @1 ۽  Hɭ8 F ِ ŀA0А{]DF`cͿ*/ M0$G!~mW o® `^D`  5@ n @D@ N ϖ+ nFH@ ' /~p\ ߀ݯ@ 1@ L6` nMA @ }ҠF ޠpٸ*J:d 0~J_J dUTpRF@Z[T'0n 5ߐ \ 敞U鉎&'N ]6}kL` FP  /``H,eM WJ PnI ] Ő*7@vB 5`|0L ְ 1R@Z߀pހ(RMJy 55` V]p86 +@YJܘ%O L -D : F@ F NPAH ^n۽lia}Tp6 0^$@ wT6P Ӹ~❠󦎪]0Aƍ0 M/ ə}PF AN1PPn ~@5ĝ[P .@pP@ pM9% l'}@5̀԰hKLH ;V~J?@ 5` [> E}~^֫jJĤe[WpcHc6zz " !i NJWAkr$WJe̘5^֋C9uԉBML6@B!-e >_"icH%F&*/&xYv'W,'zKZzA5p)60%\aĉ9WiX86ȃm fh䛑pVU pK[1ԩ(1&-DJ5aXcx?"F^yf̃JB֢#3!b)iӦGU@E ,g|&i Pȇ3oz+ă|\KpC94-nlh"z z!μB#P'0nFW(\{!i^xH1 4"Qnd(x%@""Cźˤ9Lo8=ࣆ/H xa)V)Ӑ"TJQ@oj@kCPC,ڢ̈lڢ"{e2X)-cb †жI5cczWK2(i^!&~0~xWIk5\Q" ͿzȄa:s- =Ao(DÃPcT12x00⥉]fQ;CRzJI'j ÝBШ,ֈ'Y(؂ oz }臛]N B^j6fI[.X%l5x*"{A7P!z jXc#Qch p hxr#eu!P"ND'@朒3 lq2׵.kG ȐF >r(hPr襄*b&V7|WA i"z<@ J`{p h P S`_@F}ݑуq8l/sޠP;pa K@A@ P3a '?A=_(p )`^<8h, @xE,fQ[bE0Qc$cxF4QU4Kw d׶/\$n`xBp8 I%`#aDC `!=ȃР$4Da$GCD$dN)IAk<09I`t9pҖL$ @$9P%7bN$'9KIi1ofSӨF'Ij`L&%)(g5bL9jsYgg}>NiUX|BVۆs]  4` 1p_&qe @>quհ2+{h{#* RBj{62 z/3+.r6ګBo<+]* zQ//k3f9b@h೶ @A |9Fr:SB{EAk6#=B4DztBڮ b+K FC4(?@M3=~@p;i !R0"^ ȇ;P%2{۹2>c;s**;\t-s'K3,u =(rAK?.s 2$K+qM*8TړID|d.>=;4r3.b`Ti;9ׄ+mT>wk4A4;LGc ~ .?X!'0*!k# ^I9h2؂X`(P%*#򳷺9T6l:\C3s(pJ8Nꭔ/yⴭ+#L $۪bZA* @*- {yj4{Eܫc;CmCFk6u̻8S\ǯ T4;O((.SNe9L+w E>klH=x CYc =x,? аkDMε̷d7|.++({Tj7ƻ=MNZb˷m(u1/h@,+0 S(8;K6SФ"Lʙ19Ҭ{'ĊA ,/;sME*:r4s"D[(]ˉKA8eB,嫆 [(t>H L*zR>LaEG"A7pԞ _[ !>h0 X8 (X!`D /~aa b!b".b#>b$Nb%^b&nb'~b($sP B9'0](0( ,k=H&X`Q] )c=c>c?c@c*O@8#U/@0>.@|MH%8;"@^eVneW~eXb*7 a*`:S@Y@(y/ h[RXfkfl%bi/ECa0"f(%8"c-(0=Ϳ2%fmggABo4HLx: -؂`X<-g#(M8{6S~hNi^i(hLM.R@:@ah@:H8hοfijvA\`;'x/`-0 $>SFgUz꩞kk'H*@&h{ >؂uOo:l/:@fFR]kl/Q趹:؂Q(*X:(:x:ރlmhX.8B@&%þ/($*5x،`$:imofnB[XLPe&"$@ XR(.p7o65^$u^0 hRkѡ_qc^~A=h>0n  - ;jO0/&(=coq(mGn 9 ^a󁲞@Rm:-:R='r9 hiNʇP*U]`snp3/.89ο8sJs;؞2.)#XsF[rJu[o<ω:P*B JP= "gm>.`"Iun'L:|{akkQ:X" (FBnήjpG9P'U>`00OX^#(c>"'xtc+Lu/']ljI@=PG"2թhOк">(X:Rv6y/oOy)㶁Ar>ڂsj:`: :Xjc}OtT0 `G>v>x'o`m0%X؂L`Hl7&W{hw ?-pLY/f I5X*'UFzL"v_z='h9In " k _(:h0|H!Ň}G5<8Ⱦ{% LȆ=0g%8$~?9}~_f/ԠO 0K0|Dqb (qD#t'r$4BA˖._Œ)s&͚6o̩s'Ϟ>ɍ1bҾLBM= /.䳅)>,p w꠩IIAdϞAͫw/߾~-*ۑKHE| $yцU/ (YI n6HGy֮_Î->}Xҋ=b4@䄕e|AI0߶ B R[и=/ /~<:JT7S (4'W[NT RM&hDa$mAfRX_^aB&1P RW5Nx_/A@B}QT0 8AxCXf  =L}|0'Qs$ Q8Ѐ 9ra#Ho[`d[B 4P ؗB YuGB )P 9cm٩T4G!T O NpbGTiD 1pjT F9VaDJ˶- 74}`Nf2a$E7{CF,qviTf[FY7Ps bu 3[x$|PPii2ҩ//A GPٔX=V8GU 7p "+G6-f//L( UtPܐOAJ ww>"ݶ۰9xJQ2Q$ Gg [ƅfl]z9KLn2e+4Ѓ-ÀdEpM 5`|QB J@!&X@ |1\& n TҌҴh (B~ -o!QGPxϏVW&` v Ƈh%A!> /H :Ȁ *HF0e [0.LC:2ETKj?C5HbePL |-o[XA R485µ#iSi]֪/(@ s0PM+_J`t2LP(!}RrV–C?ҟ>oc?/`LǼ7A$p{!5s@b5,]Q ]Ђ<C5,8<8@::B5X`5(8`5Lr 9  A j`188 `N4Ђ.4`:` 8:@54!$!&a *@5h 9, B !Ba`v Ơ8 &! b ~ V ` NN9a9bAV 1#>B d ,DF@ĀA8EAЁ@=MZ`181 `:`16TB2!8a> -#1>` Ja#V# CB!8"22cB!9~ 4<,0.aa4| 6 9L  `j@10L^ H2 *$Ơm"@F`8:҄8^$,)4H)́ 0VAh5  @5$ !#A<`ee:b 3NkF A! >m ,i:26N`# ^&Q p2!5Fab)!@ A IB lAD X|@ PKzz0D@@9C `:(E>c%fmVc1FE e"\:1.`& ZHh a#!!=2F r89 h&rj #G^#Qe"#nd2 Ҏ¡R1(<na7h`*NFFB6f(f'7, %mm$@&l@ `Vq WdziY@\@eI^ޥTF5#," V^!6a £d\ `^\$vgJ>F3Ыhҫ<"_J.a.!.k#KGn$ f!J6#3&6;Va `8b% ~*_aR'ͪ%s&ek36RBf %1PBVe)dI'yB6< Lv))  ((%]ee*cnb`Ͳq r32` t:!a!>. `.5ahj͒`ka .!71ndΣQ`^p҂¥HjޠoO4$ qCCJFK @&]5E%%}4TPA [t #]mZv% %֦*J`fcV #q'> 4[+>oz+$Cjz#W覆cv&c`Ba^$^j'nzQrq:@'&e.r ;j\$N/چx Ay@ HS@CӢr T@m t$>qڡA ;2].o,E*)'ncu6~8X&rLZ# Ul!B!B2%!VJN+*+No7e3$w!`5*hl8N!K$q4 o.iꡞ P SfBB4i7F4,Rl@L$`b1,&Ar"V>N&92 `"K+r`a32-ϊ-NK# & N -B m"&s(viR.5vilv"b38-s@j#`ac q=îF LA Aa.V \hAks@ P@m3;AR E{b"?R+&"r2B1`h$2!r!l'# N(a)*$ u5oRbg2bf3SǤ[@^!34  :` B&x&a%EZq򱌮a?> J:D:gj]&\Chƪ`K0HKS~[c~ks~{ca 4@! 8U@ DA60Ap .~ >A뫗'0@^ H/YRl\)@ͬt6ЏI!AYD? 4xaB 6tbD) D& T#GmH$e /bc`7b 42d*&*hPC5b)܈Q ^Lȗ/90Z\4˜kٶun\I⁊PJR#8bLx[ kGr)W|sl4eEFLJJ8BNX@A5zH̻y`xmLD |Kӄԋ&*xx MZЊ( H^o |7b)Pi <~5@A|#B 'L"M<Eʒ^pbB-b $bnC (JL&)):a!b Oj,;*2x Hb)0"ɞ|8J5` *jb[Lƈ ِR"!iI9%ɤ?X 3m #Hd 4ip#(V2Aa6$u\00pP#XHAZ6bLa4$ْ$>jR:Z*H0"*|3RzPȱBz)Zn8HL"㌍9l(%/x>|dCHa9z{.+)nH&D" &HtHl |x%ՏLjX#%= !4H"8Փo8)b0"WN)rA%@+ЁOڦLxAڽ5;)%` ?)H*kt=i:!mÄਁ7 k%qх&pqt(C9ָI .(hBhAw9h &O[PZ d`L@? z~ ҇ LA 0. $8JGܐFn0TA iHU{a&N@$mLP Ej|&#H`5"b!J0U")P2=/XA&RgqSV&4 t $Pp`F I`!f~A~$)YIK^&9IO~(IYQ&%h 5ϏpP$@A!?#0?6cT2Lg>є4YMk^6$p iA qU #b, HtxAPhb,O@ ZP UBP>CB /8 ^PĒ[0[84 (A&H>fri"T1$8Dt@4[BHpl|0 4dB1T UAӀ)!No|C'@T+y AH `1!a|)t HA? QIJ19@Oo2R T1_!zp,1#Z$X l} J5 /hAhOᱮUf0RL Trb/Ȁ`[65BTY\#:уnЇ#aIp.3^~@0!{^m&ҾHBĐp%<.@=$8_M BL4::c:F6P5U $u?uG䷟"!ϳ3a|ߕOC10!tN?8OAW`B%ba* nxN FLPGE%2!V>>OfA! " 6@tH940 ?!` :V8 m`+#Ԡ) r  DN64L"`Cs>JM#GOKQ5?I !Β6$5?P*cAAPE(!POS>YtSU `^羈Krd B H `Ȋ@I?n\ĕTOf`[1$f 6`n\u>_T`ҕ9 `R,$6^u_5`V\`YUa|U!J6]SUS>7U$!> 5\U\\m!]U]`@b\~!p>qv>1@-f`RzK )`P"H G!3HN6[v\Q[  c?+h $ PvSdo]Yj_˵VSqN rMr-_vssoCW!h`a ^u\5COocYS`! Ho5xqxup5WxGV>?hbcBfI` (W\ wpVxW>hWA,k'0HA0`'ٖԠ %0 HJAR!`t: `~aNaWs]`u~ N$*@p  `u :`f7`lI{L!ma^tXgoz7vjH v ,$8dF38M~886unVWƊoNaN 28Lߵ$ (A* i8: 6BwJ!@'V2$%H H@<j $NSNg~ ]5lVhoDi  88)ח!`zxW Rzg [5axA8]M"YC@f;fg@+jgo!UMJX n#t07a [$$p~!p7gF)[Mh RxV@4F%:%A" Xz`Wg?yyf:BgQHb (_ӆ_8AxڅUWIBv$ ׮]x7@M qoei!`ax9d!i+m6o:iqP Z `4!ҹ[ڎoo?c\M` Ѐd쫌 }HX:T BZ/hNj69څgj3Z8Y{Iw9cN_g[g$z8!9/<3| \@;`8;8:!Zwwu=N TKB%!+a7Nuy_WA cz!>^v]!![gAlax0|ݑL(=Y!UNa$ ^|r?m!lr?n $2~ vn@6 B @^$p `h<RA8ڿ!aJ$ԑVH\ LڐW :e׫V25ˀ X Cϖn 4%qWzFMMj])1oJD"MB}6++SUaz>-8PP9 `F4Rlaa5aQy,0f!-ہ hvi $mA&t5@e`\ b=2%"w-j8! oQ4b[D>yN$nE`ErG z1ڨ}KHri~iQA 1<@J8 \`FVkRۨƂ 0a cŨn h,r@0|uCc#(7ӝ `>nCy 9a>@fፇJŮ@2Ffana [lrTR1dyJ+ɀ;Q9Z7$TH`}H1l 8 L^X?IW) h}|PBLa(pe$ ͔1Jլ^2,HU0,AfT rNЯ%O{$WRa6,bW 0J#Q?eN+lo8Q,%e/t? 11̀u O_$HIm8 1}.zY.amҢ$S]7*!@"!q }0XY^:$(x)0ƇGm P$HC #4pS@+ jQ_|w t)|pJ /00DiJGY1{^a 3 C0Bx`&Rsb ;qaQPQ E%HPws0BFRua|^@5@ w /TFе$V DP:wP 07Kd5bQ RA c} KV<0kKr ;2'3 | !ŠL@\ 3_P T(@5P @D\F|HLC\m(IO̾#"C V\RŰ\^^ YRN|V,LLUlO#bk0P0p0@V!$aG5 PPЉiȌȎȐɒ<ɔ\m\6@<`)dE[@ Cc 7Plɴ\˶|˸˺,P\s@6vjT@@APx0˻ڼtA1Jz[G a< > 8=H0]} =]}P U`)P.|@J0^ 0`PsP! *P>@B=D]F}HJLNPR=G`=6DVw[|P%/G`6p:mt]v}xz|~׀؂=؄]؆}P']_m̭6ja 19٠ڢ=ڤ]ڦ}y홙XMC &(FP_La5$p]}ȝm%@6Btߠ6Jk0˝PP}`1[w {VK> p))CrV wtӍPwPME>%t&&N}P;]= K $Ӎ==⡝<%PPN> D>>[?\ yl̵)P679%  P°KP  w   tP&0E} p ;~C  P pM@~P0eE0CnP @PPP˰ P{~]0֞PPe~0x uP70JGd)6ʤBP P='s   @Ȱ Ȁ  p P ס!K0ѐ 0 x> @ PPٍ   @/ .٘ 9@| {홃$)5.M uP<A ;=@   C ?%ݠ|P 2?Y+ yUOB~~p @f? ' ɐWO@`P w o`z4@Sa5_P`hR@@@5Tr,ff͙fғȗu8#a~(QM0|/@[Ǹd)gţ+ p 葨 T3 U2.@F܂9ghQT0.؀-̱W^1&D#Rdz ȵ2b(|kCU̵"?|0ؠ(9;"*leJAc)fW-!`\-A :Pap2?YPY],_[쵼u9JI"0$TV [^>0Trq J`؀ j ^U^ 8ɅBj Ey;;W` nV,T(?!@` fΞw15',pڽ^6ek? x%B!JR0F%S 2=Q^1xAVCD3%8-*raodwvP/\p_e7c 0@0@\^&kKHLq X (gu.Pg' >,|}WjCa^ tSY@Fo1?wmj(c2w p`l f3pea6TЄ !U5 E5HL`&ԕ0G/܁[pgo"$2AZB Yc/L wys'%`AeA #;, `obWLI\%Pl "5~ R`,.)ce(%G/诿(pJ4pDƠOo^:os[Up nH A^f[lx뀯[L1S8Q[׽2b0# MUHaE ȆA%(Wd5WЅ 0E] d_bHh-!#;0&)x>'І :(/U$,@<%H>N?W:]J۫U~PN֥iYx %xn>xb;^v `n^ O]{PljHG04̅^!PD F y@Do-o(a_p\ɼoЛ}Y& Lu2`JStU SX nXWOq5qk46,dceFoF[k  no0A|fWp|!6fwܼCav%:}<*~d)X@(B˛ : / Wͅo0xh!X1s5i4 'as[PoDZR!̺iW|ۀU'~UO xqy.n-aSfD#nQ0lb7el3KP3WY 6b?:Yhea*N lie6Og7(UW|g[8Vthȹ$8P@S(?/? >(85 ]bEZۅs[o}<Y.`ZxXy]YBb[ jYx[Jq O EI@ kUg7ĻqoXT#@KL[k\"YH'EYFٰ~GVc^ze[^+j Pdb\6nD&4*lPiA>.gK/%x€^$ A?xqD\6mx3FũUQpꔊl !s$1}*d&~$ E顎 |i0c:f/8v,(1&i8(x?E%6`K6wDZo i,&,zQ"ǖ QH 8Ǧ*.H7tuHOkYʕjƘ}^d˜}$  Cļ~c0HA#l )DA~&qqGud/sB0ߘ[ߡ?v6  ݲ2n-IzZTdY/pv HwWb-K&V."_d#<^GG|KRf H:Ds?C 7mpD E^p' pȭ"$5JPX@?9LAfT#^,Ҙw ֌X N j`4vJ L". Kʘ|EXUȇAj\U#w @quFP FB-;(с D?F/ ؠDltcHa`x@F-m $]+W pi\P9 NΘa/J̲h"HaLerU>N+ؒ b))B>ӰX ] f1xȟmTIq׉S!b0Kt.ȗ'gVљ?r  <]W @l'I_8)`CxA:x[Hp2 hEQx,! -hT%0,NEg )7"€" ``2)N`1{@ͳ *&EQG:@8͏.YtMKʲEE*dx;mDš&e)\}XFS6i:`Hk*2d`#@! zD[-a ~ʁ)9:ؖe"9nu oS31P ,D 6pA|jBiTXRBNVN614¥Q60% JJq,\f+hI&16NIRv?!@WbpE}+:Psy~prR@X5/rh _`M{Q /  oXk c-*t1I݂)清 +[Vc`␲~v FW?LgNEY]gCܱm9r ^I0QYp@J@Є8\))J8j7VH X ce58-kx"twpl eٱi2ph8 ( KD7z0.KA ('  ;Ty2~o9?ƞ 7?;knUi L`RaGå|MVs.poBI@.!sG_#y0WF.B/ѓ?[g\~2w)yɓ1r9kշ3/^ɯ7p/`zHd :!JЃUn?M@L)4I?H @^ <v~  dJtȺ 4 E@4! @ !!pAA>\5|tD/A̟%!!1@" 0 A @@N0AxZ 7@   "B+",Ƣ Ca@\C ^. $B7E"5V5!Y!@ &܃J"\сnA 45@ PA@_+գ=#>>#??5"?d?B"A (A1 Lʀ$0AH#́I$JJ$KKdb;M֤M$N\d< $Pd#LQMᴀ4L 1`3؀\NBb.C|ZZ%[eZn % ĥ\*]%]~6e^_2L"A[5p03D@#cF@Xf^ZefpfZbZ~gb&O6&hnX6kf[*@PGX&f~&lr& ^fho%Z&gƤ" AH>ͺA|" T@TĀQ,C|ħ|'}֧} e7'|t8XlC&(8@7lĦ}FΧ5T`VG"0PF08C/3,0B4D@bA heZC!|Ag @7 e>PH^*@!$d ~noFب56h0di70 2tC2h0"ݨlvÖ~) ,(f*0)0@  )q:,.%B&(JKP9DX\CAXAr{焖j}fi3m"C4@ C3*/l@3C6CmC3|VFD/F4BB33DFAD3i4eZB3 2꺶/(k4H5Pz]f}Z/ k34//8)*hB9$"l&6tC3knC|ijTA 4dIVKT X)Tt {rĨ 6C4 H6-*k04і*RILC2 2826Bh383镎k38m4* @4*j74:,/"⫕gk7>+/՚-0@|)oVӪÖX-<.\n/hn3|3'rD6@V 5|A H`pCAA pѾgzpn*/nv$Ԫ/~~mum4-3h+00ަ*05rD#<0.C.@J2D/“F3n;l4T.ߪi&Dn{:+ Cz6lJl)jڦ(C!@ej~A,5@ 4AHJ\ojjӺj.@.鉶2$lz+2#Cs5X6 l7X@b(Z2/t0l~>i6~ 縂K3:vF/}.%n2*̫):€n@T@ A&B]1@@  0-RxoӧRk6C4*n^kǚn:' k@E C:hA"2|0>0Wrzn~oF&Z-l,*C, ;k7tpzB%k+m n-΀΂$2#0엶qDT BЁ(U1A5!@ PA܀9?B*.fF,ZW1/XZ3@CA'?0CFC2+/C,,Ck'f-.JmChZ.g2ooZmRt6.4 Hƫ>5 FA($BVA5WEhge(7@& !Yy% \NC A>)Z~,hvt\~~ &4BtSv"+5A7e"&[-0,0P6Cj+h&0(knxm^8@a %.C33W )8x\V.8 (jvl7 9|~vnl1 A'PaP D 7lA&40 5I B!zXB $P6q@AWzUz#,PT6A?$)y[H1(:C:tzӤC2`C@!\A1@Mả2 C;̞db;0S[z (2>@ LH@ 8@_3h'l5p10|L:G`C" ʽ>?HA.Qh@Ԑ/:H.(ÑQ0l6p@` =ϥW C+%Hȁ XuCVAȀ3| A L!W>L @زB[,0(TȰ!Cs,l]t(_Cs0'P']fM55։\2gN s"fx-"A 9vj\+Ob1[Jo`z *N;6M\v TI*U)T $\~iвNm쀍Mjr#)95JH `$m AA_ /FDlcNEo1ז?s@w;q47S{v+81{1&@Æ9B @J/dv 9X:Ԑff>\S;v(m:Us{ZE0'*+ T&u BSP ,Pt9Fѭl@?W2*0Н160"(VG^3F> a [|-\ p)Bu'@4:CP0!E icRHT"U|-̹&L@ b0:,m?J\l nܡL Lȏ*>GkpPZ<{@00RP)&g THذN!DEvɕP0؀?Va Zb:.Odj&)0Ћ=.틄Ui\i.8q4+䄪LAoSԊV(uU&?엀J-Q6 OsP_ T PB)HA2J;!%#2A4TD v)dAx@,9hHS2 qRTWI6 Ha|Jiih}q:Ul 6_ę$VS3Y`Fh &S`AQL=Ҫk b+XISB bY h*JɾR[e* uM Jc7ڤIŨPІIŇ%a%@Lv j P@ff<-ij <Ӥ}ԘC5y8a)9hNImRPSVWd߉\fDO1P `@ .$ F00 -W*:M>ӪVU c7^AW 0 N9E7 NhmrA0nW&e`R|mt >Fvv6.F@F® #zOįfa粁f͢!Tg+h\HlWKt4l0} N@{V-: > 0 ʫ@Hff6Ra Fi8(N(nA^An^Vˎh~$^A(aG&.n 6^r0 N$G\`#x&T (մHOkVI¼.M&Hմb+jQ D !0RFrT 1fl j {"CK0j *P` A@ ^~4Er irz.& 0(ƴiX*~$ƃxjPldnŬ18Z^T{a/~wkt$,F2 l1=0~,_fPLANFQN*B`ByTFll욂0Ѫs5 ?/ehznb>@ l$x )i b"/ a ` V8BNp`G>LrZ.V!R^谩Ս'7ntSy. d\Q,LNi4za`\,@pNQ`nѮ-o.&/'2xuvfIR&%@,`-3@/:zB5'/ffpk/k, ,eCiEf6S3jB4 {j{((za ^`^@ p8 S@i*9%I gv)5$g:!Ak (R*ql^B򇔃!&e` 茡֖fr&#(lin T Epf884MBWi Qjnri"ta#1@5G&Ϟ:MrPI H u"` @n B`0 JR^ H$U` jl Tg&O;{Ph4H,Tym–4.aI)î ^/AtQHa"q2qO$M&E0|G(2dPgƀta o0$%.DSN.Z{!8H$@ԊhvB&lO@ PV E,J d0 ^R 8i”@*@%&$@naxUzi%+f0Io$F%|Hcn*@vgoh$d&DֲX@q0iN)R3~A@vESvz.Ωjt \@zBLyK7iȞiBANhkn* bS2 L̃4liBnNY0 h@aW!q1(4B9iɀϙSЦ;8N`t6< W$xD`Gz`Pb P`5OXpj@ nlADl7\NR&>l ۢYvyG 2Wk l /eGHObW3Ī8j ĥ-N"[SrEO4Ƴ!@0ޑaJ2(ƪzL#V,!qQf0?3Dzs^ LG`ʩ| xc`[p@(@@CDH#OF,j ?&|@g j2+ րhp ҍhzZPWޣ#B4fE3Z]N -h_RFŌpZx"Vh4(fϹ9Mul` j `%:xV`zA`3L @F&4\J+!޼'HZ`BF^eRxO~`WZ8'fROB&| kq(, `~Bx"XH8a*xNEgdO}Z_ \0|Gr0 .#?WVߤJ+D#*n?3<S_Ih j`r R`P/ gXE3{SE(T;$`*t2dtpTZc݆!!h2*d(z@` t46qO^րC a jMkGE\T( g6Kh>UO"VJ Q%C#H0DC d0 g8ot/`Ph`E _(AKA 2qBDM=}u4T vu5(BiJ>ahF4 sOT[ˋhC(/0|`hio`zԚù/486fLٕiRoThFp0`ԾV15=@3v5Zp/fHa/ٍ pBQ!1(C |otCpo5H@N,ui_a kuИδ­La06 ݆a0\; YԵ.K^* WaO`͠sEK|.U2FO:B30&г0_`VuZ SF_bl+FQ*1(p@ (A!`ЕpXc :L + !WAnP^PLQnv. vDٌ| mA,j^wz}wvRB˔P/ M3`ݫh6~Uvӯ48i02E 8e SkxixK0V2ZHPT:Ԡ ?vN^T$?D}ƖQ+Rh-XwҠҽLsیCc_~p&+phRP9w;C-~W%¢0ˆU.CxQiP&8B46,ы@ zpy s- y~ [ƓQOJ/Fy\ga{no໽%h x\Xaux^ TO]ysgµ~͠_x[SbS7apV`~\6{)İ8(M`rP 0\QtP@ w`|> $2SlNPP ˵D XD(QROPZ5RBE_)P uUѐgPՕLV@ XVG uUUiuz]VX Ea @ W &T4P[ QoGhaE`aRFrZR5UzTTX\4s`++0}}`} 4&%÷>@ 8 TP`DTZu h%@$F $ E9>YThEF_Ŏ@@\fk FF G S ebQ)y@MZH9pD0 E=c3`Ya0$ّp EiwC0Ep197Y)APᓸPCq̐p ɔ)4 4H 7`C 0*a0+p(XV30,*` Ρz p @B%>FH*☐# #31;Vr `0r r/UXpu z $3BX$雿 Eb%$I @$VR9 Y Xx -(P )+75% Q( +8h }*I?P ʀ N,ʡ>@% wڡ$ {' >`4%698w`. _@N%bPhM .@Z, 30pyء.p .`*p @}P_=%Zq P,A3{{& Մ@>ʦp.8z y :>o fkh@Hp&J ^Ц`YRtP Tl0Q'0 w@.*`_Zj~ &_O{*۪yi:K! 9.GK{٥zHѥ} b Pp?~J媦Pu@@ѮpjkZ_J&Pڞ<8t&@ hb(X/ ZyP 0 `3R[A a PPNJHDj*00 wf; u!w 3JE[z `дT U]ʠb{ƪKQOk . [ gZ ~{ƊxiG ;  yqML@k/Z%P58:Ut13dp ` Q; p3pvN {X#Q80+R 0 K; `0{z`p ` smp@@ l|  p' ! 0  KKKl  ' 9| oVP` 5æ0la;?˺' 7JsP:*О?9L. 0!B)10MWD sw`   l J P @ @ '``l 5,S5 \$|}  P| 5§0| FǰQߜ } :\' rl\`  \\ Pk | <γp0X{kUK0YٞwP`C@:·ı,дdqkk;g,  \lp =l 4@l v,  ><gq=p\<  aP,jų [ .o 0 0pK+Φ oΫ-mP҃h`/p/ZCr |R@ /h A{>ߠ W}L̹#-+ ^=`Ep `O{|ؾ ;#] Ѭ wG'` `@A,R{'`l N|  ߹} % ,m5GGP5@h !,t0@k S0.JP/ =0 `mی . \sm  Po| ףW4B,qm Ґ :;;l` P؆m,p Ұ{ P 9l01:A 0 8nک 7%1M!pp<$10)H us@x2 @ $0,ހ Aa߼1 U᧰0/`7o`'p`5~n jm1l6\\|`L F J l ` ]ЩP&B~*  Ì][< ྽N {^7`0+P5GG (J!r= T>B{ `80|+L \r 8 9s>g l } o  #` n 쎿-O 6}US0*/15nѿzU0qժSL)ĉS. B Si@BYu4VйAJ*.`ZŵX%P€n[<`J)rW2'O8 .ިXEyeaOݬP8WTx%S F\eAK 3_v le m2D'@@2Pe5*; TN@HY3wXU B/J߯Q(hPݛ  0J%L "8:*nc:DP( $ J+ *2Q&8PC ز]fGc~ʐ<=S>ryl@耿(R% b&(" R9ef6ZIL9Ŗ`).!",š(:i@I|3'S{e`˴{H/Δ6`[f<3@CԀp b8"%`:M+A>F f,[#c0 J E(PR\b!ZF5@ pY*- $T,5פkf. ߾х*?B( \S[Je\` ҔbUn7 J y6@ c ڛ4Uxe l6;Tb(i  - PPB$jf&`hL0ashqoZEb"|EK3ec (ǜiYxԇrav 2Y_OziSd996!S+s$|@q"n!Ϡ#[f4+X\(a!& DxLfM0 F@T8_;h La>!I)ĸp#@>|| r9>MC1@k#G0P0(yh^ @Qw#XEnщG'XҁP C `3!}1߱dُ07܀ lP^“JR5j-,x43){E|Ӌl8x6pTg?KPALC2I| ~0Xy17[V0p*0%BV0 aIF<[ ]%J0` |ŮHR<Ɂ tAYR/^*!g@oP0% QU )dDR'Cn=,+0@7pJoNY`(40d:P0XxcpX,Pdr©H^6xd7 )p2l`<U0f*"P̤,t0x*63P'br@=q TA @v>9^P" X+"'6N %*Űfh9(`20#6` 'h di€Ykl]H89mNq S\i˕FX@#PM tՔ4IHC&J5ԺG7@Oi~̦J6eӤE@_4-;zf!l纷(-EJn<]@ 40z.eR RxtHr@X)D3&С^^ыY Я 7nf0l.ct`01vX 8az »g0{"MlFx k?%WIݡ"՝7#E P#N46 ,%P P rh(q#E@0D;g_3~@H?7qJ Hگ`ר4ͥT=( $.RH.hu궸_umyQ T>h;+=07g^~V&% ~9 L3@/08 09H.* M \`Uv&0 +pdXO7qSWmGjZ]yŋ Lij͡23WꀦO;25ia.i [#%xi2s<+x*\C@ [#qs>@ Zhl%Ka->+-x!5h%+ G+9 /ٌ*#-.ȅ_#pH $0L!@ Xժ{(J;(!C L FL&v[cXb9J-`5Z)xQ?#X5C3@$X DKL?#ąg@+i(h#8 `m8hHk^8ӈv0(,` D+Cp ' āIGb\r@$9 8/%CHe b>_Y 4@*Лz<!U(: CC2Bx  dA,?58);@`Y,H@~i(9@'@!+f k/0b\&x-R@,,\>P.<%[RY16lS؃\Dd -[Di ~ I9YP2:F*[HC @HM-UwĉĉWJ %ؘ;_(88iؘX!@*K\Ps0YXUH\6 j욽]cEVc55cf E\y>#ldͮij:hvXAeW#| mmVfU`@D} D9$ 0cǡ pA!:G;cP YY Y`%eX99I'<ЃXY,ЃLX6J?O8|+^@  ab".b#>_Kx&((`` nP0xH4P 50$.c3>c4.al=hkZ-&aUMcC>dDNd2&*l֊f>c.L&T%>ENeU^#;@V(F0/(90>@eedNfed9'QJ=n:] hm^.%A%U!^fr.gs4%(0_ k:k؂=8q(>gmh~hh~he/hR=)mR>020މ F``i(`  H\(:(.h:ڡZ(Z((qŅ";(jڂ.h́ k(xW=Y(9Hkj.k`+fpVk>ܢj.袖v\ `챮(jŶ8 h i0 M^0)c*;pfh^f@F4_f:eHn^npHn @ma0H@cXnN*ע(gaagn`hmm0 ?n0o2pk\Nno`?Ȅ@nކކgp h 0o߆eX|nvG^q.d!ԌX&lmnb!ȁHS&#((>_Ѓ``.F0::g=G.nh9:FnhHfhg@_@g0YffBs^ u=>[upu^gHV[sLf.|u\khqXDWtkn0dmV_(tdoofpvk0ԓI K# sj1J00 0P]5`9. (n I@``8gu h'wO7s nhxx\gt߈Ggh0nu Pm_^tYϚ\_}z=d7{i*x6?@o5~b A K4|)c"ǜ```b$ E%4@ܿ oTYuɄ,hȘqlHΦN.Pp! ]3`šAXfœ*S?TY_#E ![fNuFvB3duL3„1Kr0]0S\N WHc/gΚkotv٭ rFuKxlAIĚs2t؃ *JÖƎPB0,ѫD) 8RAMso6dh9j]QRLMUgj`00aL:Dc!\9LeҌ3֍b(@v/"5nc0iƙSV8X(`%/X[b6Uo(Bդ[45L7WJ C3?(u8e' /PB G0j?@3|l{k@ 96V5B?E k^K3L`#d拲|r !/=v3YkUaYbHYeng@X 8* ֐Ɩ[pl۸cW^^8&"hCȱB ((q `y`XzRJ0 &AJ?PD5CGE(TĬ>;XLOC3^= 8k1L7XCd!/EQ13sG;ЀGH38QH !QEhB(_L]BT&L"ԭ@АO> 46A!lc V\ ;6А|@9) RnV^M( QCq(G.WP``R)It$r\`s2+.c#ژe 0hG;4l1cC 5Ƞ l80p&,96w?e0 ?$ 8)hv Ap H? OkDbІ+br*` ~0!`K0 ( Lp5^cmcЀ,cAÌE@%P|.nL=P2A;D~Ny21 `,-Xp s؂BĀ-\`=H"p %0'tL =XdC23YB3̅ [R %)oq .P9TJTR\';1r/axPL=(3>H#mҔߔ9 N`Fm)jp D X[(AH@P#Q0? .q 9QȈKg#7E.6ԫb52|vKh5Wm?^Ճ`b]V_ *cl+YpV4D`)9QP!Pb0Y/PC`>гjJ޼9[3,؀ 2gDU(y֪n/), @=\*PI0L(yXH@l|,A6@}A|-@ɭ,\0%ߺu@ yh@>-Ԟ1dA\ f9]ABB=\0PN`C6C0$_A@U!YYae T)%a h7Dav@] ΂,*`/l!5HfC1ú-]))\)Xa#>y`aGDIC dB A Lnݩ,؀@ e)6?t9\R ,6 4[0B0y™ 6UPy" "U! fruM| 4 [1ei Y G\h BBy"!)bĭ >]é$jVB' `z^CdC_ %YU "RM 3!0CQ4@!́ D2&L^0؂1 <=,4!,W92 6?]t&aaKgBC)2])l5ڂ)7a6c*8Q 8I 55EFV?$0b:B\Jޗ7.)6fad*$Ǔ;5Pzb c2A]V@aҥ mD+bZAh XCHA{" ́ 4@ A$㭅U&p$( @aı)9:6%9dE <@6dC/x$u%]qIe$l@'$B1bDzpja1$%-橀$+BMޛ(hGByl-dÍ!C~c&U@"i-N~@4A4E\@Te  @A N?!"*x驢 0N]Zh|!a a%JY gu^g1d-|<*8BߘC+T@ .w.)(EfNC?-a}_a)5x1x@D !$ H4b-pmrB >F6bN-\a>)qhqJ1A)PC/,!Wm[qf dC1%Hz*zi1p.4nxƀA#DA>yI,P@ L @ #Xçv0$J 8nu^PvnR%$uVt?|qb#(U!+,?\1T@@k?LSf0Bbu I;.t&H4EZ/_f@6Gogi xc-0aiQb A lsG@PB 0 39 k^Ztaj-8$PZ@qq6H?+5`J_?+s¾6\nU.#;Xڶ*}^%Ohg@hé5[mf'mV2G-4UeDFgըE꽠f{ijHYwqB TxHԀ.]B _&՚B޳~ a&@_\݂$:r9p+pCMX־\ .Fjz}\c7y4C[:fy뱓*"+ @xXX@ ,XsJP@ȁ;{]A # " C8Yvdupz#5@k[64\kndҡFd#3k!<f%.#LFRsA$@gE x)\6VR?%;"Ћ?I ^C\Q'Tϝ0!uyG 3DDʚN$pAA ,\a\gCk- !E;\* 6L37̀*f> #)'?+lcԖdĿ3x4A"E!R`xQ`/,@zäGej2oV`*^esqj*S?c2V=-Ͽ62T¦,د`6`HìTN2k'XĐ*Ul0VfS*kjFWL)z\*h|PCujزD[<tB{J6 ?$^1lS77C`w* VM//Bhx\Q"3!VbJHaz⥘js 0^ ^G_~r2ɔLp 68Ɵl Y"8ǖ[)F`$KFl&Ythm F+v' ibHগoHlaɺ"OS 6ڜɹn%xdϸ'0<#`  `/` *l ̹E 1|(h+45|` 6Z rKuF:aJnɆqKVYev`:m> 4y&ew3SغŅ[(Ռ_.~![e^Z`&ܗ-݌ ֒>a4O T!i( px> d"0JP<=a)A~*N0 t 곰tr+f!9 f [I1gm F Uq`HʨQAlU*c61-SAYm ɩVI%^YD\ٜonU+-o`YwV逻[ bf0]Rŀ[ Rjr8hǼ Q 9jB#|h@ H`Y]<@C@alLLrbxY)*T;ٍv4Nfw r84d\)~3b,actez 42KbRut')v䛞$x9WFa+.ɐ;j ȂԨ>݌.RH*!'2("yuaLN Ō_$ZQ5PMB!(?U V#D!+8C2La BKTTN%`O^Ip& LHOyJl @\Rd>'yX0 (A6^T - jPVE.q „X s58&2 B3`;Oy X<}5-\|U 3X 32^)1<)Tx ec<7һF+6 [అ=hX6\TH@D2P,Mu RX4dCiYZBXU[r\0 ?`h:<ܪ!AlO͠p >N0P $ ̸5jp2&1 CщV4Ik`,؈QiKӄt5M`O+#ӓnK+љ6t1V+E'mKӜ5 VƤťQjLծ4M PpO ty@O28MlVnwEAHjЂm>( R4vAH jPB\z_9qx(@Aj0I $ P!BpR`5h=sAzNB(A(XP6 ,fVt@ @7C^vU0Al4媊$p:0 M CI !)!MX~v񉏷No0`lXmfH0 Lpc $|^WY` JP& $3(zCa8|]xV!A >\#zA8)B bx!#J bF@ 얡"P'+/ [2;#tj  O>0`H k^ P*   l@ -h$@` p P ǐ  ؀ʐ ې - PꀈNcPa in, h`fbx  a* HqʠL I@~ia hVAA@hiA?`F*4~ QQg@>i >!iQq!#  Lɩd ^`p`x, |a(azxR~a|Jz@+ir(Axy!)AВڒ,A(aaRɑ*A)A.-,Ar/A >+K4a-2Ax!A.24M+$unj#,@  ؠHbZ `RݖA"xa.x!3a, h*}+-@x!0<l2-3<1?RAr06<@A-,<Q-Q13C30a4}4!@=!|r,! SH`+y8KR PLᨼ O za JRb <`|Pr*B 27#@ OO* +4à5, 4<Ё˓aT?3%474:CsH7 3bByEO@N1TO47o$BG{GGk2uIS-331Gh^@ ʠ4 rF{R.?` b@%?I y ` #$`<&E2R-(t)W)U- 7{RE@4RTD? 7HIdKdQt?6&`DS7'`*4 D-CT3IW/vwVi Z<IwhV&t (b/d *(-< n&.<1a>@2p}e5ߙX-Ұٞ7'Jr"'qq!Jr ڞ pa8\3z 5!Z! @D: C", Szv tZQ@ X !i 9 p i u*!0: ```zD.",B1" L@U v RJ@ j kCK T!R @&@ B[`Rn: -rAځ p[Q[{۷S;rp @@ A@ { p-00"* K>f`^NZJ]ȕCsoj},+OZ]$ @@@!.kh])J e)&KF Ck`,lǑ)D 6ʅ0+}`jijXf{&/rT \(^ޓDL2)0"*a{cv1K` ~Lެ;0p &hް6=A@.OL$ `L`^0J@Bt:f  \j% ت~,IT='\j"+Na"vkGΌ; _uPa K?SMhJMABp?b BK4MHX)d ^ XEkmE1 !4YX/`K$j6Q89!6C ^XO4f6Ÿʌ}/6[m / P :XQ|X!\C' 0 72DB AB}]>@EM#:|**** .G.&UAplLMWLBN-/T~H5KȦ2K0V,i a/83xǥ1JXu k,ZwT4&l[U>دƮBnĄrnD[ \0ExlQ30ы \dR1mJ8L740VE*+F4ids02KUt O d)Z` •᳐nEπ)6aq* Oi..Z@7_+ؚUtUv'lPmac?g|K&gT )uǝСF&LE rE\c\cHE/{ P@['ì1oc@QU_ַèe+\Xi"h"Vq+[Mzpd* ȁ0IWUPN\XcPl`B 0MAb#A$l+V9 (⻌|L$`DHQ":0y I HPig|壖1l! ҐFg?"q@/0.0KO0m%@VN tk1 bW܁@0F/ 1PeZٕ B *gIspA6 cP"9 D9RdC2^tYe jh-qY,pE[lD^&! jXA 0]J!G>gjD3 ^&0EJ[{bi.Lk6rSl*!"PC3rh,PYPnQ~32!0uģV<=ܼNS`m ƈޖ 0^h+)/6Qq̢, 5v#FV04 NpCJT`Fs,pcXCxp:T'\ RԤL[*WUB CKDـxhnB`* mm -JcfUX@\~ w $Fkb^ܴ"ipgB\r$dTHO+-Hvh]m+A@'&KY D<@|cA`$̣@(:õi?tE㗪i9 4'`)\ RP߰\.Sle? Zl: h~S?_/ d5@ j3M&0pSuG5b2iWˬKע]X)za/NEqSpX8|qY/%ԁ +(P;5;6HMjYmwҹnd%p]c(\j[NPY3*. |2f?P \e, NP#4 pqo/l9@l,¨~K^-AR@M.v@ ЀPeA*rA5n|Ш JQ#GlmX؆t6}lY-#2kXU4 4bEmsNH(YB )&'41YE@PjAu@Dd QIOT f_: |CX^%$Ð~UJS/k* p֨>`{PFnGbpC=иsfxא@x Z<58#ܱ$ZϤmHu4.VL.x`L\b^Du#-09"~RiI& s"x5dy!y^}r`H[İCop:W.PPe0 p{6{x3LOXtrP\p:GY%` $|PJ0t 0)rp A7߇0clw~`:[~~4I "02!gPSa$1#7!T7 K 30/˄;2!@ P t?qXno,9}1`TcnT&x(>'[P7cA "B #0 @ &,6'K`` 'o$sT3@ Q\ Ӆ 3?P~c>&5q0$U!d fv qwׇ$-P&oC&@f@ *#yfIB`T% 1fh?LVwWJ`p &$( t1 !ޢ1-r7a .u5.0δ_!MR&_ ` bO wЂ. W\l@ q0o'[f⑶ v&6a d Max8Ip1WD) gf+60! 5j`=H:Ҷ V p&ࡐ1* ZD gF/+'6L2x 摃q@ D APl70m-i@',Єi'.1)}PZڰsTDG GU) U9% @ ȡRDžL05."4*&z1_1eacarf cr@K@mƂ'+L2oR"+p @_e4=6 LewmK.PP Q@(yN Lp a ؐ_T`@ _P\2ga@C' 0uL܉?@w **p *x P^il @'PW rp3 lweW @3!Q^.  p>M W`G q R-6} ƲB-+pcz8 IrM"Ah r'( hЍ)PNJ ֐Y+@}\P\F,}P=00`ؽs*ܗA@d _T)p}g<U  G\U)Iʐ n> ؎<+ p/xrrB0s0&S?&90P9 uؠ L^Q Tn0Ҏ|03*.0>&79&PD^9 K.OX~Wnʀ s h@5 } PʺTp @7EP(0'@O7> Hꬎ~Ǹ-w̞ pǯN-THEG'c̴p [Uj3 P~PZep\Xξ0_  Y= s6^Z6>?E< Ga6oԄ啀 A`7 TP02?O1uMT F0tR}rM07 NPR?XBP1r;P PC7u$1)L.Or?t_N +@h_ʹJh &1#sd3`Pv?2EUUI/+ntÑ@|p CWGp\Gpq/ PPEt@Q? ҁBPB .k1".\-^ĘQ#e QI LT(s-];a3f_\ L/Z%XAEB ,mpT!"$AjE2T+e >|@cm4Ž5ׯbɚ&Zs~@qkR:݄ S " 0fP,;w^NjiFE*|OHg`bnF @fX nZa5k[]Xp-ѫciMY 0ltyTx N (^|gR xi&F fxg A) @}ƏfI-^Tv$Qƙ`:T6∳7N>-jXP>@r)8x@A!s*e'sxy2 Z&g&%f1K`ĪUF^fҙg(PE,K3xFqr_dlWhNaKa~6nf&hb$3@'-@add5Ka6LtF3j XT7, t= *|UخvɑuXdyMއ|MPInf[D^ldCG9ŌbFgWkj]gA@f;A ޕ"ǔ` <'9l_)H$0ї<%YQ偎|&.$`k" P%ᆘBx#9ʨbգ`$w&bkљjؐuHv,,w_0XOgA-Sн-I/I?HNF,n S|XKIxJs ,q:Xb 8-3H|EXC!@`< >>\"'aFN@6!%@Z\dn+1}Xź0t:14Ӻ$F濷RZap3`t:6Erl #E@;,`\D#l[ &DqNxx 풒/X;tV3(-d: #:X& 8$z; - f$(BHGI}qя: P䲍_l)DC/EֶF* x'!rȈƖf2#9 eAf(p3h1]K,t0bXXʗ|)h /ǒVr><5ZS@P %D>Hb%TW; @u`g}`s 1D*?є|P PMdX p~Yr6`PJ|ȇ\,1XF @ `$^z[3+P6";Qke4aW."#t4YO2_*BEl ^b3H܁   0щ/ilg-HI5(BС+XCڹgP"t`񠀋rCH,\ X acy5]>Piſ.jP%#81  -/>k`Ck!9Bp1Hٿ2P1,8 ' 464% ɀs2n$K@2"g4 δjlP;, RXy_ŒsuhABa>u.q d (@"pխVr1kZRk@t]9Dеa(!HA 3@ 03khA m(ZxfD`8 a O@@  0K\oPb/юciǘrP$" >^5j"рq`g< )L`Ry`.{ۊ0+N6K`#hǟb' q&LQb^a\u!(7v2 /CA>`XF/vEc1~l G pkBv$cD|T}\545 y3`PWL \HP A>)c$>{[0_>[ۊ\h (8 HHsXH[`<d|؀Um ULJHtJȅ`:p"HUHS 8S0S8TXL0;HXPC`[v8C[(}ittKͨtM<ԼM0M@tep>]8y7N,MSH4%0xMSyɭ&nȆ P0L7TC xOфLJ4臔قROEV# jЅ=*=˅Ѐh%pc؀Y`Y؀B,ĥ }L ʖD诖([0(YxMRTC5N5#lDj̆ N4٤FlJ@UF XJ!| 9TU cHلUj؃  lm͇[He7ٜ 0H4Lx \hST|ʩRX1TMe3MH[0{9]4M,]S@M+UL)YXR0[U͝ܧ=XT;O l0{SWݜEݐ-X5\ŀͼiB`t >p4-dK[ xQѣx~S5(NӧLN|ؖ`X8Q S΄ꕀҬXX ]?U)NghRXHMMU% Ω-fXaՊg>X?=\ >1f<A#K%(8H4 k([pX'X-x:h (mWcNt]ǩe H<ǤCҌW!; ޅӆ^[cxc[JUZ (nSwZ]JTp.T8T MHPS,]5U}ջ^kT}ZjxWLg+uO6&dj 2/E0- pHaJ@X{ֽMyljWHWn@jy=٥<@MFn~^Hl1-t|%eSFEOVD>ʃӦmzNkX6>δ;("0< x(DSl8`Xou}0 WYJȆWHf":*8:kxL񓯤 ھI=:(DƆ<~ WiRx W0T( % SܓmHZ&X~{-X͖<]c~a,٨ NxF؃]جS=p 6^`[nW[6uFOtXc]jIUoq[XάgPO7qd젃`Fv"ux}4n ܎VX'WH]acoX^rH>xi~UMc\P%v) يH HMr`Lyo}YoN$USe)O"_^l qv*v^aMHcP`Ho0=[lc=n oY͕-_lj΃护6>noUxԿN"S?m("|侖.pW^:=ȇnnEq=[^0op?`uQ'dOeI(uwk.M~jHC." N>ήVf3.O_5drlypY,] aS=u0Y0>j:]x8[ͶڌQ^IcuKBKG7(Y*ܑp©S :? QBMb_T@0\`h_,aF“8qP—#zp0oCŊL^Jm*^yg*W}"X0S l.(ڴqg\;LZe* }m^^22uk2AwwOW/.e]Q 2sB)\zk1\oĤ""ѯl0acjM1QBme :9 c׳Zo׿Bb8_$2BI1L+DR885|QGAuP@O?5E7[ a`L|sB,ue՝Z4nKB杦Bl޼/P0cuwLE- B ra`fC$u&m<>KDPGt bzb -` Ĩ U %/PÂ!-걷9WycD_TPDhID`8%0sqHLD4@*6,,,#+w|.@UWHS*)he-rB,ɚ--BFAT7Nr'arBg !uluvwlV7޸pRb d zcu A@֦نMqsguPlA s܁Pg,?,~lAI0hI|H`%T', I/8Q@?mPCeܲ$LVb6,IoPs 5VºܘӄLkg]Wb]-k/Bsm Ըw)z-c$r,c@w0uk{IM2ؘ@QN f!Kg"E@@ 6v>!F$( L0#&`8I|cwX@ Dnri^@`E/8cEc:%Q?L:حG.|N%@i6(]PeMM3՟DMLK9/(il`!y]9~Q)C[`82ADf-l Ey-n"s(a)~bk4ݣ( x`Qq^azQR+%h@>Ib ؍  GC%@C$AmGeFu_`v,ȟ9T'"SS0lUlt`B]*9*jP^1Oy\%PlK2@IND!bHf)cgvMlyA7u]*eÙijF>aYda]ХR+Ka'> *p T!  ̑*9Р9{@xDK;}igPV jl:M>LD3cMia98KMutjU#A6\Pʚ0B)ChAx2nB1YE0\𛼈Q2kchjG <[5 /LauR mq ti)P Dh0%ZF3J`8OL$p B!Zb} ^QRUA EY*h@ l@ h FּFF6[q!h/$bňg0l4uA8s`U.xP"DQP1򕋒E嚡QYRXv, D8`2k^OWy!HbAZq/l\c2 npϜ?q B|`%׸<PB  J R@'ȧc1&I< XB50\a nqyO?$-tq3DL  !*:br*7@bK !Drhh@3Zj^U]3KW X l[Ip;/@ *8 ,P& O' ! %(A @q [48, \cqX < (#"mFQ!f+8Y$$ZrSd.6 "|kd*La%&*a׎2@D8A*@{,~Rz2/VJѰ{= 0d)3Lmш!yPvf<uRfX`TϷč8 p>u::q |@($P!P[Z@DX\̀r `8Ui`/D -_d  -bqBTn fO< ΂Dajޏ\\?ǩ` KР S@]ق] 6:T Cr9 S DYFH _Zë:4mJ؍6 A H@QBAe))-w@AYm*"b q64 bI ܂ ͎T -J Q5BD"i. c5KD܂:ftq,2fdd\$dCT ԁX(I |0xVCpTA4@>d,1`Cp|$H$H@G~$I 4?,2`C"@"xG@"4Nd@#CP$?@P.$(T*C.L%UVeTRUFVj1h3".(1d"|dM~ML6B" MOP6LP.SUV_e.dVneW!4 !H I%DdBPMA&DU$ۙ.i&ji$ij2ejɝW&n&kfl&.fiަn'k^HC  |#/(3i] q-!pHAqz'{{'|4ATA |5\@B X0H8d|@)ԁg|V^(fE P%H} ef5 Q(  EP$&.)bhCA @Bv$ BK^B~| MGRC/Pd7@~ i)E܀rF3I8_P0-$5A!Ёq {Ha).E(BA\)\t:ЀT@ R@!T[Xa؂78 5a髆+{"'Vx$IA%2H5`/ ڭ@a\ax¶~k*N,V,D\U@>a )5I3l)\D2'(` @@.-aHE@X T H] XZ $\\_Q |؞%H pQaH p68 -޶j#L@,l)I@䣢,CB^7=؀I@ lL@Pa|m~n΁-B,A4 L(e(@)BVmDn6tD @RATr$ö&@? )B?C/{L J.APH6B~ H\%7,T ?6BHJ! Á= @1׳=SD# P, %`I(Zi34EfdB ppAڑ)PC1=47B@09dB@A<*N*\د@B*C_IJDL |S Ȁ5CNXo0%l5@DA u @2B92 TP\C,6|TP@ @܁5T@PR9졺9@bCn(v0\נC9r :koxvj_B9l@9 5(/-lal69x8sl.B9T9v9T'mono6l :8e[l;~:%7}|||_ h[Bj6s /HewS1j 4Xk-Az|'%;Z{W)T,-,B@ AXP9,2}@!K"s@ !C? A)l@0 (8\l887›Cw ܜw%Av%pӹ̂!yvo74w6ow9w99Ϲ@w9(@*99ã9w Sy nCo¢z/s,xǹnz&Ϲ!:Cw0\o66tgnow6siz*(\6(y_l9( DAC, DW9!\9\Z6O5( [%HA@@!,AP\nyCίC8*Hq*9عz^(*A8d?_8@@gr 绪X.+5X}*:ݟ58 ʫW}u}/tw|/>+8cx< :*5B|)ЀB>:~KAGׇ)*99|zH5lA1@4A A=H$рiP XgQ\=P`EiA BAASAX @ԡ D @8X`Js‚%X(!&NhFѡ8@XbB&FPQPJQ!BQ"D>xG%eV!V;zMJ%Ia e&5tlXJ"0ËW+VO Za0U{B nZaXQ$ԍohNtNJuw&ESD R3@U"40P0/r-*04 ˇx2qaoE"䋒Q|YRD NܫS*۞Pgs w(c  F.tbc";2c찤Jc(9%ʸX,@!DJ>$8f"HC j"N1xXH;nhIB(H *ᴢp3pr:"={-*h;Ê*(B.83/B(4JF,-$(mʖ23>*&k.ta:pXp2r ƂVA MbÆ ((B/J.(b "8%x !る&]c J :ӬԊ=>0G>H@UVIxk½-2G!~j "!*!*f@9x؂k"-aR/j[ ڍ:H/={NC0R^^&9&&!&$:f;xp%T,h*0;H&&$BIhy4 @:b8[-Q$>f`9W )& @=oRHR8tqp+nbǶ/P{E.U-`(!ŰJf %݀#ER" >1h=- @ow k$(=+8 _P +Py( %`!A080"0 .>D5HQ0I҇#<;j8'@U0(4#Dp G[AjЄ;4 kB /$U A @aXpTPʑ%p@xCیhDA 4ta 4 "Ȩ]BP,NM13@kgO1Z4(b@plSA&$).1$؍,D@Jx p =: Ǭ@POo|P) 3͔=E0B "KYP m1562pUJ< A~~rHM 3PP ꀃ:S0"-)`5 D Y 1HZZH_8fBU m|@wr $ 2 ]!%@)RC !)P@uXʳ0sE @b a6=En#  , " BpPa _B ;>Dl@ЇmY@%[x?H`2@Hp<PV SD\5(@4 Ծ@ +$Ğ$ %ls"Eq@)A" M@{7ef $ H@ Pf7"oke5 (P l"P* fUQɀpA%qb3p8m@XNc;逃4aٌX 0T@e4Jp:J_P۬JP23Ł Σ[O b;iRXM~U Lѻ.BQB]Q: P=tQ L$ԁ%G0P1n a Jw-s2ǵ_98,nU[SG'͝Hz{7]\Ƹf` )l]U)h\Ѓ.AgP@wVAJpP` H[VVXk< :iB9(e1(̸`%8.t N\ 2`^5:5Š\($ 5EL/]:| A8"m%gB {4D Ͱ7 .agQp#P4#Ad |2m-4//[|BA 6$e`xVdJ/@Zj(4( @6v%!@@+AJ9x`$!hA`h @ NR(d|*|| hF !މ@`bP Y(!)>Ca@|@# B(g"(b p`Ld0pV`D ] .^PghDlimiM냎 heb +Bz@f<"ƘEg&^f &06hz+ 4xJF@g:F O,۸4HEkPG QBCh`BV^r @a؋ $jРU @ h౶:!S!!S RwR:n#>@ jYc*$l @CNpnj#^(r#|*@L(a- p H'iaJdiq'4$c R HbPTnjCRf+*4lEa@tJC2Kj7t&*B̂?@)cj(!R"36~D ;LBS*@<(C6## U&hFu N-4Рx@t NR`A ΁tU/AA3:?!(LB'!%@w +w `(!N :`''"2f!t2(-D0KYFa,R-r),$-6>V)5aFPM(3ne-2CEACI 1bA v#)N)TФu}k JQ.>֢ĎC`VL+4i A6`'b|4 "!_7Ѐ"2V l@ B,+ %b & <!$!~k?a9S:ZxJXNu::k X) b B_B@A"xyW V{@fK +LAA VՈp[>B V  W( Z+}b~x-wU >-^` S$;z(N"++X)`p` X@@\ؕBr^`o#p[f9O@&$ !@+lk @;usvkMw$vP -|'':%Z=:R$ VAvqt`%$X ?6۵`ys6[Й`M!li`;b0ǡ@,Ρ:$΁w nP@IơT`ΡaǁԹ[>@S{)˹);绺;/@! +@:ồ;k?vYY:VAG) :Xΰ"rpxtPRi@S۰:]ʡ\\Ub7 l{NzL(`{ [yF;C{Ё k!nͭa K[.~? k?`} Ļ<5Hл/0x뀾C8O@ VBYD.P}:q@4{\j@:`:;@ƥ@A%D۹`Ub$>ԧ[գ_='4:AP\|-A( [Y(k:*@=c$V{ߝ[:v&n'Ky}K7[!e|/a{˃*` TX^O[V/ ܍f|Ǚ?` @xJbN/a9~w;A6X: ~Lƙ ԳY6 =k;&A!󡹍x% Xb ,/@<ݹbnR5ԙA={^u kWo߻6:~uP& ij 6ɁN! Kl@!ӺѝѠ彎 ;:M^q-%!m'!l"K'ʙR05 ƥ@:C/ ,3޸r}0 t;W X8E( @>Xp$ޫ`MA9 0TpƸqm=Wa `? @!Y(7 tN5ٰ\~AP UÇ =5زgӂXm\S97!qkPKv!r>A]K AIg /s\璵ARnIQҸ:yaJ 5+B 2)g@g:\/&4uOJ^#}p93/\|PG9wI1|rBpʏ 2  !sepӁN*Hp)@Q$It05Qh J`An`uWRqG㤲!'tOqu0%xi :5e^l6ԏ P~Jstdl3N@`Xf89`jMv9)lp֝9K 5 Քdx8:ӗp,fYIu]xJ9sÃn\ZbVE0u@j-@0D0DYc0܀' fD)<BLƞr5|P Ps> tBMtFtJ/tN? uROM5}uZou^ vb5cvjt2n6eOYlw~ wd4>nxexψ?/~w_y#.GG9Nܦ8Wzzk{;yޠ.מ:꺗?{cG{:u/vԫ>ϸxm;ֽxݣE| ;Epx# &r d Ag!96F?992/&:51&w͉=98))(444WZ[RSR&)(565VOJ`@,20.8+$VWW8/+1,(#!YYT,$D<7YYVA:7..,WXZ51.974=63.01[[Z TSQ996YY[ A95[]\VWY ""! ''(LC?VVX)!JJH YYZB;7 MP"!KC>*! STO!%##%!WWQ"&$YVWHJJ B;8!'+VUP"" >F<9RMK"!!! HII ""$E<9D;8 "  "$"# WUV!#PB5,H*\ȰÇ#JHŋ3jȱǏ CIɓ(SB˗0cʜI͛8sɳϟ@<)5k$2HI ISiȐƀMRH!O4 oJ4L|j)oc4I ߰JWԴCC$Tob|e5Fӑłk0* 83īX64-0Yf"-| d,F[$EGxh.x)uFkViW h@*l|D(QJ(>EC  +,iK1bR  yS!Q[yU" &L0\u+[ST&Tieli`fiXF< #fBab8h& )tWTBȃPHhxi&g;>KtiQ Hj)`R>@]ob>F !aH2J g QάĮhg .aatHq*"+xԡDm1Sh)YVU#i px^ʗUH0eڎi@XI%VՇHD +1DsRJ RfbBf!,U|&*ЧaL!uVf̳LH]'!qxR']sx߅ HlWvQ0AkЋi!*)ZXa]ɡos^+3,|C!i"%:QJ t9(1yJJ7{fuZsVىhatP=oHh7M2hpHxơ"ZJ<~=)X ~(HMaB؅z@i%2Bm@rELxb*4Ca)4 CcnaB!S(B h236E(*E-8QA&lDb$+,*KD&> KbQz#1={*)A$B: |ʊV3` 1|fET4}iךPB2:ŬBѫD49՛5թH׌,GFq}cp0elQotzK=eg8!ۋN[CX8XaFѳO37!~5:v"NBs,!\FQ Tߌ"R ݺ`ޤ2B*)1e'Ѱ@ʖCfg"~a`pHH!Ht+MC>Bgʐ g( J+gNwӞ@ PJԢHMRԦ:ը^' jZO\ Sծz` X T%'8 tp\J׺6h8T5|o`Ke%!d'KYhX,V hzYd(+ ;ֺtl($ͭnz3Bn;ū~[Uȍt[[k`6+ᐄ@׻Mols3VQyKzUhpZNjbS @(\2 vf  qr#j@X 87*,_4=>c.t uQL_嚘(gd2&"&[yǞD# `l{L$aNsn/(k._V lWpL9ʎ F[A*A Ҡ<,:ԨpL>!,N( )XwMpjyMlcu]lb_6 Ȯ*jf_Pmk:42/n{e Sܠvz50\ͷyZ1a08 ~CP AN(C)Zx_ԣnq#)+N0$%*?wuae:2_uɥn|a emhb%PeeHo>uX:LOWnW┖ufEyѽEO{-O݂ ֈ'7ˇpo_:ݞ,\{|Uӟ~-BʍTorRv ׽Z|L`sf҉€Яo}[K٧x+B)KGavʗj:ό0D jґ*2H3$ S&LsABN%6S  9T1f ;" #D7vb-91EN!#aN 9?vC>2R(b#R+(rBKb!LpJ- RKpd!A(@heW@P GB'!dF"(CF|!r!AF!3C r-E8(TN!!|B1 er1&P' te81`"|23B>X':KNc7T$9'X(u!5ѣT9~E2.҉Q14`;'Bѳ27}BO8^! .!=J=D!4,2G@pAQ$Ԍ1Et= 51H@La;2*!"?X 8+ב`'٦V&8!(+(!(fP J%r5D*#‹Q$#yCvcf"O3-q%&P3527-"1&P "DR!!!2X(b9$:-C@>b# -StBH[+ %3,(„>RMCX ";4("-/>72"MpDaV.щ# qA+B3-i:L=<2i@7r@ ;22aL8e! pA$f %TDN!a;URR'.gRqi`%!X3ӹ1*XQ-r. %BQ"#P,;R>X  x i5:Zzک:Zzڪ:Zzګ&:ZzȚʺڬ:Z֚ ںڭ:Zz蚮꺮ڮ%:Zzگ;[ ۰; PpP 0 tZj H P4 X %I@2AK=[j!P4@\4]4 n0QP 3&_ dEGbP]\a[2@! _ @$Pn[`a240[]uK@L~@6:@P˹k5{? }1$Pq\2_0q{#a `: {[в\@P@_. ˰[ 06kZ;[@:k~oQ@~K˾Yк1) P{@+ 3@ M6 ˸32P;AK _2P03@ @KnP ~P _@466[~04~P$Pp5@3J5 q [ #ipnP5@2p_`h̵)k\ 3 dQa~2b д_m-tvn}w̰]sN_!m&` MTWo =洽iM@p-鴍@ . @s `"`n` nkMۂ @ !``税a>ۂ .@^a@ `ڽ^^"!p維^ > @ܮ! 0X ?@p 0`"Y">}`  PP!P~ۏ <p 0`p P@P 9@p <`` }p /l! p/0 ^/@0P@NX ! -M @`^OP^QN#O= ` J_`鶽G. SP]`,8p &x b+a6@F$Q 4 &`ذ"0bE:$B|0R%K0el :wJc% 9 B&< B l^g wBȪu/&/ xbCg#P p. U(LIC`P0P`B Bd@ ,pJNa42[5 a*J0@2B D`Р ,`h Ѥ"A7h`2k vD<Hƒ,P2x`2h@"pA.`e/** 6 'W2 6oB8 Κ@ĝ0'$ t@, . " -Tt ^ ZTҳ*R2P@4 p Ҡ "x2ac 8 ,2H'hW  <>;9 0~q'Wŕ.^5p ` ` h 4 8 gݪ"8Mt]8U$`( vrUL8 LDa(m6n6~[2TZ=u- ; P.D6> @<# 1`@Z3Hy#I3LOi* ס$(g$` `AC.V5+f`9BP`@0*8\Nq+:)6^ ッC1Ā KZc a 0Mb \ @  3pX  D:`Y,"=C B  @PHDBj d$ 06':k@@$D(3>X^1tcȟR/xl8 s@E@{Rd0U0| "8 q Oǽ' @b7s% ]BB x˻ؑhȇMzI\60Q4@! "bvK$T4 a Dr|u(`!F\`f G,5cHE(| <$T@"TW)T`c!( ɜD0r|P /fH6*?4Mp=>`9 Ia&L PA s@? ApX% Ш]V$*"E0\3 tE@V)tRԗ$"`h̓>kT ,k! `W+Wiґh2ƁB@i @) v-enC4f@Iax{l0d0rFV*o 6\  "`łv6D@t$ a `Zt! :@.ٓ$0.{7Tuv70`g ):\lj%S`Bc ,BN+Uhhb ` l0r' lRKg!MaK1:XpCAEn.shD@~`Ic 㰐V,PMOvee)~3#pF",3 (b۾ t0x?\ G@HPL<ڇ@>"8`M ]C 500C$a?! Z)p `x (9 (N2 Th]`ȆyCLa-@B ؓ7X )Hl`(%}65 @FE$`!PߌfH 2pOvCS!yjCCtfR'SXplo;XRT omO>d 8+`ٛn/Z iH"  ̼* 7^Sә Z3 r%h Z+&A$_-<:v͋q 98lw7pqуv @a#daZmDen +ͣF1t0i<,qD}`2U̡nQ F "<@ ^h%ɒ0zI;=' !FѾX@c$?1?95r4%r% q˦Q;5Y-T$ AR7%61xWҀ:%$ `0 "p&h6H` 1 8gp@Q'. s q AZ48 "x)PDyJ$ʊ/+ÙDdQO@ 8 P KD%t:$6X p3(xm1&(" )y2P0@40+1TLy 蚊ĄP8I.ЛiJ@_іP @ Sʸ!Y# )2Y`Bq ( p'ى3é<(}ڗ 9)J oHh4!a(X e) @DdZ4ѾpӖˌ!Ŭ@وNh9G 9( a`,fdzG!jqK3&Q!{M`QkH89 33yrI )1CX ( 7먚h AO *93ѐc ޡZ J ^H裸 OYƛ9AO qa(N80-5Az$ztךԈ:ʛD-GiyX!쟑zę`B)ʲșאhӉɂSrK5nc5@~ixna brP%dP~cYcQNcg#E2eI_`f_^df<[Efef_fJNlmnNbU^rff8fsf]Fwxyz{|}~&6FVfvnhKYXMƃ9CACAXAK8P<Yi莖9xC0ihքxKЃЃDH^ꁶP8i>X{XH9 l>iЄCЃ>XEȄEL.?-xp-OЂ^.y:2o?x/-"/vhZ.-Mƃ+߂7.8N/x7V7eЂ.pxD _p.,e5D{Oh'H_Em- D" .(m ;'2x'?mvm.(/ ?pghND?thrO//p".O?'-xFl:{s'(-.G'qx2 {>h@qv-ZpĦ7_-X<6hDmNs.XX/.(<^,7N?Ђe~7kOH\7~w~p$v/ 궁?GZ:([~,'.p^F8@(p=iNxք2FwȂe//FtN+v0- {}O?؂.'wN nM ?eop?X/Ex~D?z?؃,/ XoDEFׄFP/HChn?"H.(.{,o.Kx8_eGh9-ȂDT7Dr DFF(,"peFAhKZIE(z~O7"W/DDXtIF|Ĉ'R8.\(㧌D~ҒJЌ-N>!Cg dUi:]rr ]qK_S?:-Nȸ& .G_XB/tl,]lͼ5IƖ,Z-l"Z2ђ107᱆V|F7#}E'')=e:".[|aH@D,#"Rh(ztMm)J!̰\P@(Tf[l8 Σ&=fx`b }V6C]F6VP@VK7rK! 3dA@` h踼4`u+ϰڋ0v^H D(XK-_ Y, _60WWx?e& b4@]vG =@0yL7 W7^Zlxd\@}Ao0Yhec 'ah8[a>%m4C/ v˞)`= ]j`)  k)C?dtρ/x&%h| ߘ6dxЄ&`(~p\LȐp'\(f=Maŵa\0@L "@` XME @ @wDP @!ApQv$E)L0 IwA p} %K @%VCY!x`AM)$cqWLA@2rI[A@3ԩV| " @"hT.$ \s5A!LD@n L9F|l@Ʀ^8]¹YF,zŁvc$b\_QFDdBMD'G6cqj*8<=$+ 'HQ@%Q 9\K9@!<HF@@!5]5A Kv%Qԑ̤FAqC i^X%Z0U!6Y>2  B2hpji$6ͭ}oFE~y~ɚq)D-FTLH@pƤ{R b?CiNCcQ i< o&QVr!ltz4D*B+-# Ęl&#?xH:_$X@8f|:y@!.mr*l*}Nl$Q2pU^IFJZud&NٚBI x]\pΝ:EqgTTZiuLm68xX'vUR#3승yNX(>/bV] VaAĀ5=^"xNRLv ,nw:GR{K=S #sumq E7 2HA7y ݹwf!YXvFi}654yzYTBDK  wZB*N]`McK%^a!Fn\‡fMGvv @жlZ x5eo f6 B\Pf8'3z Dt `dϩWB5ݻVy%s:3?ŵj[$PDQ(AFymaVDiq rB/B۾K?I]-$V^#(L{T?\:  /!@`PA` .lЀ Z n;dԸqG1aP@BJ |`)6RÑF(r Ã!B4BĆpNѳ9Cp*၄^. !BY b'HpR Ct!)LC@|)ry޹J,da L/_gJC2v /pA%B1 Th8;/|aP!o؅X6±A4WpnQ/B2 %r%VzH>[ )E"8M$B8]lBN*1IA߹<Lbv{(X\p/{ 1h<5~ U2؁v _B0 gPڀ V@V! @ D؀ M~ @ڠP!꺆KF`n!MV$MdQd\F`KZEA@!obFO2AȏP F@b^BEaf )`!^ ^0E!F@F l`"#@ AB0<`Z7vaa  L~@ XF@F\ aw!@t`"]`"4=1 HP#HL1 Tv ;iqtH`9B 4119@H <@0*<{BBt *TK%H @-TL=+D')b97B A~sE @ @  R@ UCVMT @V HϢXCVӳ l ,"V5(b dUB%ZE L)(&b.ZKCZ#[6,$B@.`8b!(2M3`9)Fʃe Z=W&FG1zwvKC#v g@Cc T,$`wi8XFud(B (`|ӳ$B$~TUpmbQt%YCMPKT@%2 V-,lMF'*v1`z9U~WlU_Y`'ZX.^>*.T79T @Z+V @"878XCUb{S,lm+8A [>osYW(`yVOu)b{7 Z @TmC#9'81D`V3]3 t0UB, B$ CV *:MբGw-z$""N'[-(? %@4=4Kv(*)؂oĵ/@$XOZTC5yF#*X [14J5 Xs&95"1.@@Wu?9 "X}ӖWq399@@RAPB_v 3[ &` `%  By``yX  f" Z ` hW_gU`'fzA!`mB*" b@`! k1‚!?6YV_QVuuƚ;1eL"D, Ġ<V_%m"9 279u(`yz"D"W_ՙՒe\@ ŧ "``` 5{<|M%U۸U["" a+*\<)Ca-؂WY-x̛uV@bfuDVŽa1n˘9% e @ "W[HqM3|SJ]sSDU 9+ ZYT/9%2'Dyi[wlr։"k".8AZ3 v<4U5mwcznb ,  РA#2V ]Ǽт'b W-)B|,TUX<79]@\mK`+"DYg‰"e5@T;%&Bu\  ^lY)u &{%BTVOߵ&OBj|×c>˛7/ `C lw&@,,@HoUaY6yE ˧0**] =#eX ؗ @ bGޒ %h5ď;BY PBW#9VB  %@'~ DuU_ =t\\cO{+b P@vMBTʽ` @  n~ecw\. | b1 `-DS5 ] DT->~ (bz3%]¦6 `!a_0 8(@O :CLb8 ЀDTpZjAlM[Uu( PPv@ $qȕg lU@qTcYoOw2S0@:5 ^\7ԙNJuy)wjIy40Z`:}0Q.!hgNq! JZ(A<ǀPD5JL]5[~EA! YTV! qP!$<1E] I^V\AtG,AAPF)/QCV!l! ڡU3 &N4*HF-6Mk`@ N 4u,eO<@#$PM8(5/{`*02C@E5 h X*藁@ؔd n,Oʉ43d KhJdb@Mij1$YLsmP iYWJ)0n5"݃& ([3&,˖ RUź=.~!("ATF @O=UPkS^* O L.F)vIG(12v 5Ad:0-jT"azȎl )B8@B> go P4k#,@*IN :z'h~( r 8`YUJ8 l(wzPuXN܃b 960`-@/DV@0OP0;[@ ~rqD AdSHNivw! { |tY __\څ%Kh%5`$v4~bS&PH5A@Y+O \[1LԐ4 46 XԦ" ߏh$_BQJq/EFe݀ep %&`K`j`N XQC#= 8D`NN6~#$q \BIt/Sw@ s+>%a1yzMT-"Lvj~?z9?PBO%K$h2R, A`ϡc}.@AF Rx=iD^c}PAwհ A89s_o@xJ!jǾ^izR~~~ ~w~wp "@{7 0 j R# #h')+Ȃ-%; [zy!9;j 4 ?0. F?'m$`<(<@ ;PGHJ LV(cHehgikȆmoq(sHuhwy{؆x h ` 0x x x##`ax8(| |ȊPc$ ( $ @8ާ'(Hh  5` Hg8%`hA`v H= D hp h`v vpD #V`IP p=@AA@= $ 3) p p N0 Je e CYDy r`3y pВ52 Y N ^ c 0c / Z閉 'P#+_@_6 \@\4`~@iAP\7d\0{`*P=ИY 5@ [_]@]PD #yApw V & 5 D z 8]`vPt}d6 t`~y}] Yi1z* \@Y7Z@ 64`+_pP[ o@v @@ 0p%) d20О5[ op D[ 4JYK_@ 0~ 9 6P\ @ $* @ eT)dv `]}~PZPo$ pWVI3e`o 2` 2 _d [6P tIzo2Y@ /J0yJ9 ~ 0}Pt[P ed_  t0U*AtiI3@] 5oP6 j0I}@5`%IDpz RJ50jYک]@T 0tD@~ Z  3  4~* iY` [`)٤ࡓ:$*p]* [ovV:X5:IZP*|۩YoZr `3_@t7|{$]dP`{pY>'Vz P~3e;j]pe@@zд zV0}2بJ07 Z`pM:} z+7pezx[o Z{nKq{D z_ t `; Y; 5w%s 4A`)[`5n{=[#IY Z[2 @ Yi$j 7P*Х[ɧ`[p6]on _p^d AЪG3@vIne 3*v)d말ɦ[zUxonQlo@ I| u0\#x0 4@p2˲` p8ó;[PYyvƓ[ 7 * spˮ 9Pp5Fz4 5_0̒ذP   kd AA0 & \К=0 *`3 2`͹*yyp;`;`lFp p |f_F :h 9:= 0DP9PiD$;ͪᖣՖ` / 00pW00p׶xqoɴKmٖX=KP 8x0K0 xX=t]cؒղ +c9 bć+0sש]r0@ހ p{;`V8}j:? ν MՍ}ݷ# !wp ; Kz0~0~r m`&56&MDx;0.' Cxp-0 r= -0:^ٷy`Lfur ⠧:`3{J4r :y :`zDz`1Y ::`Nm"@8y0Apr: oyPYGDg'#3wp0!Y~ds#Zyru4KywnC͇VxGg{o64l{|4Vc^fuꝧ뵇oRf.s f`p#%̂MbY#a!H[WYotOq,`St6t& Ss0Mt  U^vP`0gwH`^ c!fVuCsPV@{`k S >d&-NO'=x"ej{pCMв*}`[8C /AΎ<D p6r;<s`SVJA\'-`a"rfOL,q0) 'k C1 /*vu꬧֡eX"@"dDM2Ueb[,@pCK0pB3M Es`@&;A/pOP\0i!C V61$Fae Awh/<90` Q P@RT #xC ,xfF\ tp@f` P@ P A U-D,0M]qLYc F e:a)P$`Kc4A#_%y(fZ@3 6C 0DxGS!0$㤾k/h5 GqIDص(yI3=+WB*0 "j D:"k<`0NIJ2M$0pHXC̻  *`F.a>Dh8+YT 0`^7-Hd*3&JVc>6@ Bpc 6d- HɔbFLD`& zNE8Q˒p!$i3ȓdo>R YDPAR6h3( J20 h40 ƑP{HpRZp}d$. @6;x<CoBؔ)Q;&I,4RR) 8怴& HDsN\"3 `;O+@ N] ; j J0 xBRQ1eA |0H{+)ӑ` Pb 22eԥOQ`cxT%/ g Ij*=ed?ewC5eGdeX9fYL/xFe3W8^5b .D`I .|t܇ 8 +Є ι \z- Tp`1x8RdA")؁%IbtI:9؄8u*J cH4"WQel qUKoh(H\+[Ew:ĎG ' ˬ0AI=Dxs۪08ψ ,ƤـN (* 5P΄ЁV  ]6O$Y`(HKJA @ ؀͒Đ&TE54 8 ) 0W x ʐe]ʜ<AV}? { PL!<Tw@|8~5Wv(؅;iM)p>@x؅g}VpP0;d>pR Nl]YXkP ֜PbZmZqK(Z H]ۭUPK̃$mں][h۷6؁m\}\C.B!GPGh=hfНIh9\9hIPIpUE9̈́% )P<6(a6 ` _]a La0aaaa b!b".b#>b$Nb%a@Y0Y'^b*b,b-b.b/b,^`+RYx2eKXHeX9Nva;cdNC8==;[pNmA9X5CV@XMdhN6HQQCZ>xeX9P9Ѓ9WXee>x'XNX1WDvg eF=VS^j^ffYx?؂Dx-79?/x&zg{"P;;gQ#gO8> EDSg{FKPdyI-;;ZI "D(2 EXkPi\Vi+L`e>g2 PI(Qg"S h.h+@Ahj +\ iXj;(DiQ2~kzQ<wf-VX/{/KgTɞl&u"8#-H>Ђ?؂/؂E8nmHh?8g_P?h?pg;7?@;~:?7x/..XVj=T+Hނ.;Sx7x>8m.x2y8k֞lԦoV-8\Ȃ7oPu7"pʶ<:px,ExPH.Nxxon"Ȃ,X/?pS2Z_PSq~m>_2E_6Ђ>pgtpg؂t7PS7 @Q ^x6_PNo.0i2EZ؂p0Dȁq/W0.>q2Hp/_>tpV2.xp CPׄ&Xo"p-xZx7,_ $n%m+5OoQD@glp70-qv?-PUp->7 ;<^H-؂Z2uAo?o@U^bnq2v߂:x,@UX-emnYQ8Z". cׂȄ/aab8/-22'm"p2vѐm"hxUw2H;΂07t.,PXu|.p#n8x@؂, pmOSfb.P2ppHSow6|tg,ZOw8X.߂fyp='KvS-vȂ{SP#{.{+㿁~_>0uUvE0g2'/r}v2p)$=@+ .bK ;AЪˎtca U}в/5b >3(2o~!B _F˘4h/7|zМVF -3^p& ɰ',ݺIw/_;d-Lir.ta{ŏ2b∄QyȠfK"d|cC ZDrqAN-xC˗DnYT0D .E+CBc^D"EYUfpQuc5pqgFHСCԳnЮ{c"YrG~EiqM/]] ZȢDt\r""/ 8QC/ N,RN8"G\"|NL";( 95:!(r|X9%QFp M:Ku ÜbH#M78MizM/A,O0DDCHCĚk&p g_q^VxB8Jv"q dҨzVXHZ*Jy!}$# (Dz!mܱl`Qb{G$j׶$.:ܢ..b kĺ::0#mHqG!D*DW/Z 9#|&'0K 'sh/%JtATs4@#K+!ZR׊(J"6RvԷuD' xA5""2N+ Ӈ#.yH!wD;a_=v+A^n-Y k$SSA7DŽPMd+ 8'1#O BOmp3#?M4_=r@[=r 7x_>ӭ L/@!lJcpx.8xPg@CKG84Y8|p:dy[*P@ip } D!G8Xp?>P}@.逈`h'ALQ Ң(E\@x89AB} ! @M E 901J6KZ F th 80@08 @&ElLCwH@@C&N= 88Tqi|&`f@ȀwT Ȁ6 Lx 0 (pĵ"nZ+9\OdAz` TK%.#p (gѹ? (@8J0 0@ĝD9$l@&0l 0,- `B M(M  `> `T`R pPuXM ڄo ( hWV@ 8,@H 40Bh!ڵ՞q| R`!`/-cASRve%2%(FD @4E)Yb` @z `\8)t ~7=,h?ӥ`8k#Y~i::P@\@aVެk!@a` Z'?*vQ $ 䮋YYSf:탰\\146O[c;bwP S50H P6PS HhA8fM (*f,zmoj%!Ьnx304 `%?uʀHf e!0mԥ .% TH Rx?]f :}xVV'\۵3׵ `lWW3^` b03a lXCP`g̴7\sxדB`` 0 jr9]mfv ?GW6rl:l՟A]iYA @PB)d7 `?Y2d*eYCcy^b5=Wp4bw5* Ի_*\*k)ݖ=1^X\xA \ZI^ c͠UU4L_X%=)XN-s=F U= ɟO_ B*ڢ)cP @,MƁ[= hRq>QU02ҦV!nN1۽4]$B$"@hTe]'v)Vca)>Nw*N}.DnrmQsd>Q`=q_OI7\ZdՖ[*"`@ - dcM;TA*tVAԓ1PtdD'^O CJN @n0"#nfe]@!da eBE$?Y)lNA$ԁSb?+&]\az7 @<@QXh1D`@HTh @""c]?&TQf$" XFMLG]fpZa gA| <|Cg a%B.\*׼fX,TD#^ 85h%pa'`4A hUO'Pdra[xM\ +UODnJh'؋RlٕN"`?Qb 6%%d`J3) LJ `A~ܑ P\f!hN]P,C#>̤?)b)egD`YOZ"ʩm!PV,Χ=| `ā6OE:ZxwI+im*dR p]>^#>zA!a!h^5 iO.Wv[*]y⃹:I"xN%žV|ԲB !`r%^ N֭EXhQ!Wm)ar@hUZ4[d>)Ɂ 6@HI -rH~aI@ \ 2e-rc YOqQUO kZN2"]ޢR)Pe\aN%8AT]ঝCzAݹ9\ui@Lx*U7(Z:bjx) 4 X!*lR_U<ի}6.eb}씚=,Xa /d&>"U=M zn6Ƒ Hd9ԙLY1W@$|X@z(@Y]՚ @Y5A6s_:EEr'N@<PZUcdjNuUR% S 1dY BD=,|e@x#+FXT.-M/ 6@d,fZ*%)զNx \Bh$'?-E(NLA,Vm=q^ g0{,P02 OAHu2YQk0u+&d*nj'52'Xl^ZVk,T@+2JZ-@i6 ˜T_@M?Ř|%ŁZVO\aMAF[Vt&EXt44P :j QL]qSm+a YziPgT@1 ,)h\g5A>jE((d'A)_/vA O1p cg]c@" Ys?YO\a8ztc?[Od sq;7Įur+)ӡ٦U4i3AP!)V@S5iE T8:V+(birbZؤ-lU|k2@ifŀYzDgT"UWgD 5PI>]X'W`x Y}"Rg]ldKSmILR8+*R9t G_1(8) )G-M@$< %A)sj5OSyS9@~gm%إ.Mw],p6w3YإLO8Z]P AI>e-0SOv\P5:0c}7:0U@z-=*"Z<3 1<"U )[ޱX DAgDwgLW@JD@4T;] U4qW˂@?;@ԱZSXZ ?LTA-N8:a-|B IdI2zPXH oP NK3Yjn{f ~k+B7pq Uga+זjkd.j`eAeȵO@{ ^8t l}[prK2x.v /P?$ 6wν aHGLQ? *ah 簁 avTѱ> */`Of?_ o )?a 282A|E7 Q.?| N(Wց?èXe#dI[:E "z\d B-T 5Bԧ9ι#SaxDlA f`Yr> &v`P>1fq:0K^ Y"Eu!$n2fTp @\w'p:2u@y|+foA=B"'nlA4!*t22tA3pC,2<ҖRɨ@bp5ȅ*G%3+RTc Ig`k& w;)jT%2A@5w60X@eAp/ ?<ܣ7!` Aa|@~BAUA>|!&B*6:= 4@SQ@ WE`Eu 3"L*v *a#l4LEDztFsED2EGx!#)DiEut`T;gjIPS3"8`|3#1I E%?yGݴ2r$ #@4!fR-R3u6{:wP%<,@ x@7M2\4"s23Eǒr+s::!|4)v!K/G;$$P,uu$ ` Ft,R4%:=A X UUVr"mSA4(OTE#AԳ& 6E%Z` S[5Q!! Tc3Vc/Vc;cPA)IU1:`p 6M+QpJY5#RA$ z V@<23*F!|u-3v>-R"`[x2##LmS/et^s*+^B <@ 35#ɒL !#@k@Hjc5Vs7wcdeo 8AOr,$?`'q^# NX=B@$@@Y-$A`2#1 r+""-R6)! nEou9?"a , (Ejvnsr% YW@]2|Rt^"#&vs#sg>765` Rn=V |='Yt$ #'\+vWRaA7s+$ 04+k.@t٠EG?Xq 8SWxJ_b@} ~:S H F`rM+sR X=@ T%16,CM58 ?JM38 %xnqwhoo SV)7!x H*2OK17 ?}sf.!imwq)284# LmZ1#k LO$WO 4㙷Aptq$< -Y?{s8s#`Q` @x]6ϴQ|@;3iy #3J#%s9q32+ #V@LYoÙZq'A(G4O:"FX % OT5 ]V鸀# <`xFWCY@ U9.` 6Z$=z$`E @' Er/C@ 0E\:r@`(!Sۑ'Qz1Lu{\Stu2  L2"Mm*yRDMROOm{=4a0cq\%xBK$e i+vPיUJcHmTqM.dVk0z@X.#$IT'$a Yjꩨ6\4с $eA׀6Z`j<@YNI \ gEЬXliZCtM;I1HGk[8\X@VJ)S$9$,!Mx{+zmp\_GV@kD!`d|<".ɑ@ z\ 5d`8@H*pIBIz9PqE%oJ`q`d*(@'R9mCMN#fMYLP'dvzLvTR 1Kzi#>a C̈́#aT,aKH%(GI)?iK]֕`?Yֵ`R'Wp@ j#.ACe URժXF3UUd*KϚ֕M*՛z!h@a- 06V^(AԶ'nw Q՚jY6V,m[:ЍtKZͮvz(dAMz[`X(+y;`7_Ek#L [tp'F<<s/oI + P^Z܃kcD'pcB%&P4}9 lxć-ӵDK /';)%a%aȸ|NN!S;g%:=<'40b4-pa8 hAjb6HK+$=(Cr fG8ެ 7HD"n ;܀2DnP`xu $8@9p r 7С HF-`6'kMd [!j/BA o j. S<$ թ X@9x d0o/RnL dmOl!Z i.`v0:~A7A]v,Tl bq?Ԡׯ3|ADA" x-Ѓ/D'@]P 6{p|HxN.! Dp G\9T2@o?^h= A7h E ~>7@ehaiDsE[;W 7!Nn[-^z*hhhU%+$z pGp T @ @ T@ 6{ 8KT0 p <˳>K mpyR: 0 Nvz`T<%zC%e=ZRpJ  + plײlw:TPrKpj0P="NT-[8 $!: \ "w=˸,p:K0 {0O$й+ BзO4sFYR pDO!Fy y;"M˻/)C.!g:.q[O?R@QJ j0M`!+@3[Q! 8L +TAPQELKKQAfT盿tk$+)2:Dp H T`++4?r p!uF !A 2dD/ɔfg! ,#4M@H`>%QpD@s?3!0L],`;A 2  C{YIP| as>+KS#/\!Jt\O/`L02G P 1 053M!00Q`'|11n[2 0[L  `6 0͂͗A>9` a j u G]BS+MAbR仵t$@kPt2R ͞#7&!;Q Pq+`pT 1$M&.i\& 2sPl JD+ :+Ȱ!Mp>5:^=)bΜfJ<U< \7tحG!rо) up#Hϩd ]1!P ;p ^ 3FAz4|A!q",?`L!'c)p]alrp'QP <!Jݖݖ!$^- '' b@@d;,~LR'#2`<ܕQGa7 brcN ܉a4 `)-F!}q HA֘!- ^a<=Z9|`K؉!u M@ a&KQ* !  Cq0DHHr B7Q%``%";Q9Λs'3p}M wp_29 >q̩7<,gB-l~z3q@W>u.xRp8 qnK̼BC~MΎupF-`SvL-lB=$ Q4"i Hq KM&1q܀S5!<ь!GPI-p긑n0 30A.QaCm)}CL߅-0.nP.` ?A\R ҂.D5 +f/+c06 iqu A$>ճ!0>Cd c|7!HF`l`٘O7dM1 LM0 ',:A"=]ۼjIpQ S0q @Q >ذ%8<0(@2TP (P  P D@4xdp%4!ᏞO@99 ` Q(l G 3k8 eJ&@':^MQPb  6$t 4Ȁ!MlA  XcH(` a '" (I) `b8Q 4H+rp!5 B1,@h{/8 0`& $GG<;K@i?5* (848;NJ>KMJ:@'@ɑ<8l& HWXi)$3CE5xDB +p`3JRMDM6lО 5& p=cHMH%FD$<:A"h@AJ3 p>F /8*6 P( 쀑gZ < ` c6,XhGX`PD8@&NVL#oC.S .۲2!vrCB PL4(@( "( 4VءHh9 Z[1/x̂ֈp HuEsl*̑vۧRC@dvcPM6z M 3<hP2` >ƫ#Y@'ynWXTlz!8V!$I3p8`RBW: @p  \kĒI"i pLlx7,bJ Idwjh /\vS# I@rO4H7h${yUB\eNCJR!^%˙.pzC 3H 0PI<FbqcѤEd927& 8[ E z \`E\O!a49#P)EQQ!}#!AJW(D! 0_5#2AƌډB| 4 2 h[0S!S9:N`w7oȹ 26ࣂI6L@d +-ȑ6,)RJ M R! Xxg|{;A H0 06L8P8 B6ċ{1ѣ- XH r0pΡ X90Kۀ%@@  h|$_€X 8XL8)@hPP{|(A4-HB ( s%3m1).h500 ؀ Ȁ P R 3>-5X @8[t @B Hpv  `X=Da  Hvhk ؀K0hKg ;i9 p[ Ȁ^ x 0FȀcuT)E찴jl| :7ϋHsD @0<$ɒ4WAkH4ɘIЃ+-)V؀#IUHVy d@ipWT%QK\d-D5؁؁pdM\|M؜MDMۄ;PJ!HM˿tĴDL#r 0MTN܁UpڬMMӞhDT\O pO|OOOu MpIMhMGhKuQ)'LhI&X =p:*09؃L!u'I(1W(E(0D(*h*E#G`/89UF#F(%yS:@STWe(@p8G'&8@RFuHN}=@IpG@:x9`QOuWXYZ \]^_`a%b5cEdUeefugVh`hklmnjVoq%r֒VlMdVpv5xyW|WPV|U,Vpu]uvׂׅelׄeWMX{ WMw؋%i׎Xxmؐg}Xs2,+$D+Τm;1w>YR?qPxL>A.`35)%ufg+4<4!ZOS rQ0i}?*'QB@|A )xmykPƤ`zt^C02?LD>OVDĽ>C/+peaďW<la]C+ٌ:#bB5-,wTržΈ߲ H9bcevh)#bZ2F8`,C2#ɋ"1j9c#0FLƛ7IBiRq҄pΉ>)1JTRefD@>ZSSLB̪Oi̚C?.Muܥ?8qAթ NҤaToڴzbX͛_Ǎ3+6w1Bb0^,:ОM^Z ~Ml 0_ Nqj8qd㈏#BhA}?jޢMԩ{F ߣ0%w޷s[Wӂ߁āv 0<灂!8`v*Jρ9w@A0Emt`H#_H,i"D%18F&I#5ddT0Ӈ&E܋ F99J-ֈNed-$Rmse$CD-n:]O.`nrw.e;X7CZ("#`N(Ci [f# L% 1pݘBF<)X6#Ae,l3'>nPRp d XLd"C},K#6qb!Y7\f2|e(_>r,и641gl<Љwvc93/2Ob`2 i\Ҹ14i>c7ҕoB<'8ͰGMRgK`P B kAՃ\Gֶ хX ձ.i]l]Ȯu%z lB8տ!=Xs;ņ̀VF7=f{뎷[-r;]ӛ׭Vg|N(jS;7g P94{5!l#? W| QHnx޹}Hvt>҅ QPˍY: ]VN^e_ [pEt{~6n o7Ӹ=NZ]ډPB &;S1x2CלWg8uQj\w X8WnȀ8مƌ r`|pIv[3'WGaiI<_f)t Y7sYٛy[RiꕋIx)p@Tb1A_ڡze}{TI J . q8')n:jxa^5z!ZiG d).0aXg/h )mr189Qv੥tZnJ>ha *{/R\Qڗ`٨q&wJBx  1`5٢Z:9|88\\P1vJ׉s:]Nw@y:'ћ3GHaAw(7ޚJ Jʱ{Z\k0ְ$)>jor)*`G_u[ɳF{ʅDv|!n Py9#:R`; {^ƴa; (DžN񯄰v{ڗ䣻G{cÀsxo˲]kԌy= h?s)8/;QȌwz (T!Viџu+Yw Vp {SWzkQz[0qPgAE;wRbq!j[ وq`\BPǪ>~ս +=ЛB0t; \%lP[,£*?0b4Gy fK!J>L,&f/jæ p99FT˷uK;\š0y)3{^Bï1jH99Ka^[ǀ\ DZGr®~ ~츊<Ʌ鱳[:ę% Kɨʷ僞}u8^I6R©Ţ{ѿ8ʥ{p!nL<2y : j['_yRE:欒lx.g'u쇷L1\ʧ|\l,H#<X.4KspLjNLͱ3B "ml 1J<Њ<,Y=6M҉0[O,zY7[ <݁ZԶxLy<,Jՙc ir{ڷ5syԛڬ>g Aә `[>z }}Ļ7< =,UC|ד4횮M-JuMʉ ҁ @?[;}=B룆sm غ9c{䊢] [n37_ BxzgCzM[ą .!>SҒTV{xˠ쾫_MF0M<)xg#>.}X'>&׾޽-D[YyQ[ٚh]J0'D)1lmHYbiʏuQp*>ǑI)c&IiA((gɒN.֜H+,[7!^.c4d=wİȜs虎l.^] Wϩ jj-|>`2ޚv ^n.^.(  R?o DF _@d^f%0/3$ _n!_nsvBap<<_?=o?7?f_j/~1dOrCd78:/3o5(4#Gu:'R.O0/C^qlODtx .U__cE/H_ȉdi-دA| %_ ]`&cMޯ.UI,@__QqhXQC89Ⲵ2giù9ٹ9JzI:Yy*9h z+ \`9;(u38'D0G $D%@! M./?O_?mm+ө[*Z$…&%S]+-&2hѢF qR&T<"ҫR4qrj@:ՊNX^K* Th6Um\zz/U~D Dž 2|]a܈2Ŧ *b_0<3%f:smSF'"%XȢ:mجB{ 8 J'%4?~Ĉ!MJ9Apá pe:9۳ՖzB'*̞K<סe~}t!I^qS 'q~~vPqB8!O '!㼗^Ǖn O膏pDpR1HVFo5qE\a_tQbwmvxwxi&^G_6w&P ar'hV3}ybH'Αb,p1M7Ij$pdrZ\"NWq ܍Ǟhk9lfq^]vH]ԭh (>-QDR))U e(n H6d:#T6>"* U d#[6˭jZymgN^sr]"UPjjJ[Bʇ&W߂‘ٰp»0P\!,MTb .zEtψ$69OӁWߐD4L 5G2b $҄!4c!@0lzlTflG AJNP4rp4" MAL`=K#d"!i!r8d1PS"0I63j80# .  ps,9Ù4haL9 xsNI\4EPj( ctHk:<ь (5-% ӭP-5JtOVҞT:YJT|45j)Q1@ ph&eWUڈ"jX9֠^gVU L &6GM'cj*Rt[ZiMf:KN=aʖӮ<KX͎qhXGV5chO Nv5'gQX3=pBl\HQ{}\ !3̍7~ԽsVw@nt\6  !96]6rK]&`tz@ pqɛ*'GӯjXow0"{iM0n7=BC eO gDƒ1?wxJ"s4é?9X@6\"[YMe:l[r}[#C Rv/И6Ne8ycn<,׹:.lP,@w[!M.Xq41iʕ<3+?4=8[z`[?֔tZ꘶Ϡ']zj&4qcN&/bDe>gctXkg`oLrlvssZ):g@mfe|4fzپ޷7k{ލwL-m | 7誣2qppnu;!:Ows2\>3LWwGɕ,(LEG +c|o)UyOl'~7pI5MTIȐ =vxHc"Iɘ8tx Y)9yɒmbpw3 ix9$Y!A)r'Aٛ玨ٖS9xXu w A;I@(f搸ٝhHi>؜13)Nȟhhm7‡X( YC ɑ v9:" ǐ֒sQ;hC)ܩHI_)Y䙞DIé虔8gr)g j#dٕ7ʠjyXBFVoiYJ4ڔrPCR ^5%()i&ّ:ui%zZz} 'aoفوvi7( *@zyJyb*숗pʭʞX*jӰzq螸FyʦڈڬΘ Ժt92;KtJɊao0'i;JɐzQJ~JׯIR;cmQ2o0Fy:4q*4{=])}EkIۭ +8_ȳ +k85J @P^ps[fgz;)wckk%$`syYv:r ]&V򼻛p?+2F<`Jhrw e~e{cVf;b[r5/T0zFƿ#F{[ Ka;KVJkcT}r#,Kk-)j0ڼ[;|L/,jvaJl$L| M\"u˸ i}_ ˎ 1LDwJǬ۬JRX, #Pp\ DLK|JHsG]ML}FM mĀ/Y ҭp M!M[&m%.m8$/goT =? A-C3CGۣ>IMO}X#O˚p@[]4 ] aMes_fmcH qMuМLR)wF<˰ɦDoKD̕t-,LV BLخ}Vضً=Jن=ڑ=Ϝ\K ٰmڤ]ډLK۟mۡۨ415R4S-JهMG-HH Bʱ7|ܿM۱p$M߭GMųܧD}mߋT/ &adTEP QOTD ^Dqs;Tݶ9%^T.QJE*uUD+.O@OBaD!-qXr%2\PTN[uZnZ{ /S%XUrXUWƽ~ǯ^\.Ƈ $A&4)-6|ĝ~y^蹮w\^>j.~; .uo׮I|_,~wbឍ{zHخ'78z&ўR@~.h[cX~`W\54iwh] ǡTbMB zFZi'X!?{3+}iyRj*;ξVz}BVnp7t~_Tyx<Χ&/=M8gi\KzWb%gU.?^.b^aoөnq~D$;;{'!zwoPAJMrfȷ:E&Oq/?h)NgNgN pT rT1 RR 1p11N΂ ȅÒۑӗTղͷе̺»ҕB'eJ ZHÇ#JHqAtQNG#tmEvORN2ϥ ɣQE2Ri'!p!aU4djjʵC&JHjH\)Y)&4Mr3ϹibV)nɳ.c&T"CO bHT.MKWg!Ez{]dvm^Lj֔1k{8wmVFﴭc)W~8Is/HI|ٓ& $©udٗqOuj]xjwG5('|=&Y)ٷ vu衇枃|9Jx_ LHQ%(#i%G*EatDcaD(Ay +X3d?C:62ic]6(qɏr:ie;̽e/ʫoR Gݾ`hCNl۔BhNȚu 1۩^6{lԺmn=z[X{}$r#/<}m|{7u{~?3y}'9ER#&= ,S4ׯEk=ǥ00RE_n+DѺ]Ww(|+(A(l,^N *i ~NxCae?<$lD_Y!k iHF&=OaLNB@D+ 6 Hĉpsa< H<;=.i9(sD7%"<0 Y)(x 8?ŋW#h)2%\ =ejRReeb:baXH^2BV._4VޒzqQ{0wE:vw%N Tl$ߥMׅN~9ڮ]  } #~%n')+'.NБ[U{ه|yP{Y8wxxUyNއNBP邘N9Iy+OڝڞZHIȩ P1zUƑhwđ΀ yW PP iz熚~Iސ.EޗNt\[ >n雎ޖ>h>^JnS:n|TҐ>pٗYw-!w°}nm+?n^^Yt=ωFΟw^nzQ~/ o|î9oY6mAtu vOzo|~ | s{NWp ]t?nGp1g_? O io߂7 S PY~eg֯_O&( xs{yuo@Gx5()E%hiy8: ىJ0+@ e[)uK[ IHp|( -=M]m]}}Dm-M~I).OU~Yx8h!4THF$*\)Ru$jP=d+ v+׮^$X 1ɔM[4N夢RС@\ҥJ2w.SPJ25OʙlnjͲMڱbҵD.GdC > b%F Z+]:\r/[nJ.ґ7ߣk۾mxqQG[ jkQ3SmAKumgmv.l*zgy'1"hP~׿?ŵ!_oG` *WcwIzᆡmA~ 8b}aNчc#. ecmM2 9$-އIE> e46$aB̒zQIfhf LE1 Ā cԈf栆^=Ssg衖^z&y9\ !<[J&(k>+%Jkfhژ~ +a.k;n:P>XCM{-C ꤑ.髅B鬘:,,ʺ2{mʛ-@ 1&lZ1kkoF0}z_J ̰^Rܯ> r_(#= oI^4TRO]q ]_Н&mtÕ5S]W GM]׼"qRM *FaXq`(vmc Jve/jvg?{Y\AV2=a{iWߚumovo+ZҚ;*wms஡騎RESQhtP`-yϋ. *. w U }`[lW s>K pKx$.1M05P 9Me{9< bC<b-1nxLnmb+=1z= i 09&HTI0--ggJ#3C0nj<7gN,):;ֳa>jrԃbR@Tq0DS `X V`NwZ^l{`ԟU ȿ̊(^`WҙִdSPyjϺքt/AiKӾ5Mlc>eS }(-_ N@A+x4+ozwz[m5X8 ܚ^[$MqR{еW>!؍w׶c`o7/ZEvqEQ8mۖs؊7fun`uwkW왞pn+߬CtVSQܵ1ym[O{n/=?险$pҵ{xxrdu.~f~{ h~yf7{m Y8rʷ|xmu7 8jmzƦ,G}XrxNSVyZ E`PUj}sw}3~1{EXzwpWp{u @(DFiGfoumoxYeWY8~xa8A8 H—Xp؆LȂx(ķ᷇x]ge86td [TwvwoF}󖈖5qvsZP)-@ir_t Wq&gҕ_}f i4p }ȓ^@ b9`p1\9gtU&VEaP`uly9uŶ\9&`z=t!UzQu闋qɆL\ɛ)N\yIlxvpɇTtm>bQ$祏f)IŏЛ9UTJ3ŨG%kP~yL7YzJ.\iCq_[*sz EٝW9-`0 5|&ڄAX Xh_[Skiyɝ%,gBzJ J I;}~^]Y@Zh_ڊ{3E2$)Zm*eF UI%M3?uR J`:jUj*d ":zJkYP:#c<ᑚ9v}YjYZBc RfjHzNJRśc5 YZ˺ZCV H`تJHRE:T@E[rE_F&vj躮z!5z Ka p0#C7@试PY!$#&&k&([&Hjzza * KY$[$*K&@;&B%D+ղ/z0Vб kY>;$F[E2b?'.0۩U? zV%(sYoYrjE Rp}AN]ŠOͷ^nks۸u&w˸[Y{K~˴M;j˶;Y;f"Ke}dٕ_KE뵸(&6px9[Hp+(K0ۭk O bKAk;C[{EKb=yC 4k Ll̻|0LR3 UFR`']N{ * , Ò 3L8l5XkL̋(<\> Y7 )ďUlSZܿWX@ۨU3PT.\eл\橽3qEm3- -]bh>9rn\Nt~ ^>p薎B0tw..PG4Q.]O㵞^F.ۑ]W^=ndlˑRؖ^^>Y.%~.~눾ҳ Ϯ 1ZwP]т=9ݣ:l^nCΞ9E8.k 7AK?Ѹ>  4~P^]EF_ l=ip45^nXC^>SU䤘<ݭ:npPRNu3Qo >?یI flMt.a ? lobLۍ/f\2&/S/7->_fNsSowhOR'܃{5TT H3IH01TggTŦƞŒ$4kkIRRr₄͌Ĥ1RG \ǯ X%x!NYiծDA !08YAT@{/|ǓAf8s lň@ gWY)<: bQI.uH?M5k0#2M'.` xr$͠a5p/JQq& Uc`m}E+y UWNQibxװc;P͛Jسg&d9uYQ kDmoِv՝$%J.V"|2<Ɵg^pǝuidX^g$mGv zEQI*oaR]9%c`ǡXe]"|Tv?YMahb,gsHHaHQzHӉ4}3jtpTsBVQ`" fF&vgBJc,p%siKFb)=l( HT lJgݤ>z E&fj)p`~s JҼ+6zJb$k þ>k[oUr(:\jL 6leA]ŞfVKt~Y$0X"|˥M(&XK*6{ڂL$ޥ_ (Xib%i@B!! 30B RAhX!0NQ!/C+ْ;$3!j!<&z6pT/ MHG,Jnڄ{!+2}s dZg7YMUd%I`8^̗dNcLIsD_` ES S|*}%!cSA"+/'h+KPM}f%=)mT@i$SjçR5j6*jKKUүgҦֶEbkV >A HkP$`lkW&]>n ;֔$; `i(rBb6nLK .~sc߀WKYUmae/܋(Wԅvu;ئ0{`!m8ϟPgt=%!a}gq*r%8Їtׅ_z~[UN7f6LJ+Gi!#! n{N=u;Iw;w*ԝ>3Ǡ ms&. @w6y]Bx~:ŏך鰏zzkǞkGԘfzunhIOzѓ_Ϳ3 ?[}ozu=яI짧l0vrxxOeywx}  8WmX؀ D'}1&4R@M3@_i5'o2iFWuz'c|쇃?gp:m{N}'q:y[PBSƗ O;5816m}DpjeImIxe?(uMxHm(FCXR낂*lѦf׈Fb8zƁ~'w؁g(Xo98/ic~뷍Źxgzۗi"Ud7ia9juinDYl9A8Izv)u؜7ȏ7ixzNegyv)ə'x H<) b bT4yٛYmsoHxiΩy~f p Th|pQ%-'@9YIuȨ৏هuX8i=WٟW'dW*"W Y~ڧ@ "Iʸ %)צ&KZwW+] Ƕ `ȥnItXAj~tvI~j~E(ͰvTBWk!!(7(B*X2(< Ki>}ڗ +w E/˖v :٠).X w :JxPBN \b/:nij r=vaIajbzڔ,+1i|QLTVeձ.J#P1K)z-ۋo*TFDj$XE+yv|;ڰ鯓Z*$Y}V 3YzzDyioFlfii8ii1$jGzl.:9keee˹;Q 'm6UFRf?Ѱ+[YRm5mݫm@]Es^[Z0V kmKkbx lk{狮Ks,g3p l&(l'|[K(A+E9X /G,ĭګJÙZ"LJ̑HW\D[L!NT!D(K&+npr` p+FI)*.(lKn`_,>cn0փHJNA^q>P~\]M._~a~>N#n%NP^/]n ?D.^Thb䦮Ve[K>Yn.>ܲN|Qn~ BʳÚF#M% =^]Nr^Nnꇞ,j^jn".>NC>^깎n~N onNuy~ۥe%O>a`t2~+<:D)~5`J4<l6^װ-~r`;u~^dI/6oE°MBP>Z^` _1s/./ue?:_@?-a͢D2$oohw^-BK]0mB.rOx4v?xO?̿  _@ 7HXhxSC!*J:zZj 9 ;ˋ 9I + l\y,[ܜKKFc)HNE#$NU!b0 jDLuV " !ƺpgݦ(b7:U G%bk0!u gNCia'GL:5߿!JpV+H ZX_fy-Z4kZmYR[IU+WChYNO >4?3= S %{Ʋݶvݯ0N{]ϦK /N;!R*C箋>R(YP嬔R9tޑېҭxRh϶}Gg!'R/^%=sX)ѝ]2qv6}y3e!AGRzWM{g[* \9dF59.V]S!aHVx ~.x1Z[x)spUP?nPA0Ehڈ` 8!]HJycZqMfIweZetǛQHbcIi2T#Cryy;^`m&.%} 'J (;bǩiHꎦʉZX~V*Afl/J议zO9QTo\R防 1{VkwK6fHBןo ͠:.|n'TꠥJ!E}.0~ġA;J͏'";JjRvD o8lID&gf)?cSkAP_QTjIR/eIi'=N鎊::m.ꓥv+'nP&6*Ըz"Tpk WZD!,f12z%I%m4yW*6kgYelg H}uk#.iYfVmhz[ LK_lmYWm5I. !%)XAS-$Q w-yˋw=oz{|^/}sA ґLk6#vUڷo.AZXT07{ FGkLu$w(~i\߸"zU<@>Ťc(K` 8 첗 0keό1+fNL淶ts::yqB~i|j_|+g9ɥ HKzwnǤ/iJÙt+OZӴmO>~մ;h}j[:כG_{Zտt;BO"W6syԮl_Ͻ6 posRnT3{Nwoy`&@TpPB~/Ո, a_ `C+nqHY7]Cػ8;k7OyW^斻(1vOfL#(X4Wr++}i5җu7rӣu(NҾzֿzXsI+ $}>`zA{(m߼zǸr|hׁ0wb;͗ǛTdz^yK;d؄BHWxHDfh>؂CxȃԨ脧|D,Bye'sxcȌgz6X8e[mX]H]訍xl;1`]T6E`A1ۇXl؈hyDž`h(H%YG(hTGC<(Wit'|) i y؋ h=ȑH(8GDOwYJ8(}vA2IuՍOѓwUXH&YyrOIIqh(r(藅I19R(vR&V]s)$y}uX-lGvi\ $ `I`oD{'֧O/IɔB˨ؔJqXaFh5♕ЈǛ*ri )t9锧 I T尞~X7&Is쨠YX~)oY"h_ jkii1$wOדHIM) *wUǔө2؄UٝVZY9]J^Jc頻ɣS؞ٓHJJ `JhhYʝSjy) **+izlQ *@>w@Je(y؅hɉ4ڒښz,i 4.(oӇ$8pYp+:iXʢ 1WIc*(zӉRId+qQ>I9tکȟj Zn(jꭵJF( :v艗{J(}yp'Y V`"p{T Li_ 0[шWʭiZȳ #%` Yۧͪz8i;I >KX{IKguHl?)pKYfwb;Y 'p@ZEʓsۖ8B) ؃ZYϹu)ZjbkD;ИC}~N˦Pu;wy˰)Z_+ux˛Sڸ[h)LnFBNMzyR'iˆ(ڼkY_ қ{!/jT;r7EK麨;0l@˄)fֺ=k۴\ 7 {8{ؾh ەZIi*˿72ՇWud9iZ l﻾ySyD<+@ 6)D1ksײP̵\[9ZE2̧Z+ \ j»kAΚiTܡ+̫al,IzjVx@F{N\EP {[q:%+Lҋ(e<[ՠƨ"e,|;8Jڔ2 F{7[q\:_lߊēlǦ ,xjVɌ{ ͧ$JКpۨ1u:Il˸̚뺯(m@H7]UҴ&, \*x܄<8U#=ci' ՠ[),҅롭cKD6O<| {4;{p[rz|T咹!1_»_-kmo>{^^A (lUma.m^~ꪞꔞƚvkyT5~뀾^Tޛ>Gor&`9!vM )0N@ ~خ~RV^ܞ.N^;{?;IsNMОPpnYo_(뗂뷮 %/>q%&_T`/)T6_1/,6OV<@O3o5>>Zi)pH0H$s@^_oe(d7cmsqGz)fMtsOm_/mrL'ئIO#>*C?OG>)?o61ޛ^?Bޛ_.[Pu4[O$د ӟ=PT2gꗻ=ο=%hQoTT1))TV&kkIRRBr]/V4384$T̛҂Șӓ˂՚ޗbR[CBBD䫂$HQ8ڧiA CN>$TP`R$/s$&K.wh,1N\8qxiʕVŋil 4M&rTJ~mhOrEIS^NBe%%hKv]̸15r 4ikvy9r.uj77>ץӇ?r *e !PN:qWaeפhof~R0oʋs&ankx쁵pWXpƪo||@WwM\K=4ԁ7TLىgO@{WoD (Ԟng]jJ~8}9e#?8n#l֣gCG V͈I& @5UNQ\"I`Vr NfymAH?xW_w rG}  E4UWTҥ_9fze'o [nbj]f䞦^hV[zSj=VE뮱LZd䋤9)iMScْv*2.FlgZζvnڊ+oSNpVb%EXD305ҧһW~GlyzR'$2PK@|<5Ti G$v|yx{f\ "ZqjP ׫j™Dm{&}їͲ,U5ϙKmtK]n"NeeD[ 0&\bL'i\K \&|ﴦv|LJ$ e|U 8t_8Kʷ:n5~|oΠ\{&FoŹ93@>=_5ermNKWLֻw͚Շ n;GJoTbé6f"샧zzZ7;>Rz:io8 M)ҕ'~8hY3Olס} B{]P͂85h;uHb^@,EoH TD}+Y>VH!]P F$#5⫍n%rXG'Xƴ8/\" B2_|$‘ş#)IEd"(Q^1d5*Y-!VJevcId5(JOL=e$I LT 9T$l)\rf;JeӔ '׎NcbԦ<ρ'I9|ӟt&Om*M\r` XJֲhM+Ya (PѠ*UҐ\S?Wo `K‚kpk Ƚ޲d'KZUlF*suBPtȡyֺm!VTiTPURFᕱioIMrJ[b8.jcZͮ@S[)TIT*hCҺ B02V|   6 'ӻ^M W!zKΰ7UȕKZ4Fx+єcԉb@uc| ;,&;D-|PƠ1(1b2``C@Jx`#{]Lg v,Q9πnp!NhP5r'Miw -Œ9`W& 89tWMi+\:E&|qjGոε@^mMKyTCeQ 6pk];{҈U_D<5 QZNr9ڄu`(٪6[iTZpB]mZ?V0NO8b(gƀE>̵Yq1\{ ]p{ݛ307\ۂ !Z8N@FGяDG:CU p<[c>|d?=wwҕKQ  ;>vW`=c~֮ҋn?_p/aG!?Sɳ!(V_7~?>tc2W}Z_ī~Vޅ+݆'k>GޕOs^9=QOX?hG{}~"'|@\7gR7x3ї7xӧ7{7SKXh4^=0Z!w^]|yW6y(zV(v>uWzB Lwi_iq9Uy.X`ur4(xzXՃ7GVWY7H3Fh xAp?gw\xׅ6wn^w'ȠMg7i&WV^?n}րw:|XzwX behhhPsivFk6'T4g([hI8FHȆ̈$6CËׇ8z؅Ȍp ؊?؆ee{XhQ9 {;gMWIp}y\x7zȃ!|وՄB_4t1fH(_(z9izʘzIV${buo!X#_%)㈒~/I+9yV:<)!?@Be&\*׋3p`(tXzwx'dHwQYSMo֔لrFfHM 戓tLq)vy |iՓQ)v(y툗9 ziVYNOe1ћXegYpxV9h 0YV>picfKSHMĉ{^ )y yV cubrr4w|nɜ-ٞ@y0)Vy+ 7 dEo>`[)ˆ7vH} )7ɠ d`oE(Zbj i5X"ؚ(ZV#,Hi 25{ڡy))Hj.- CؤJ:)VSJ\$l&WF? )"J"ȔIdz0*c~>y>]wyyZ)(F}9Y-Ie:*VJ\Wv^jpz_ʇzhKɘV hs.Ŗcg sq3aHA(V4SMmT}6zhzZ1V?Ʈb>t*dZZVar69(l: ; j K]}o>F[Ȧ4a"0 7G(M0.۱2E\>BER^BhƳвfNOQ^+S5MJ&#t9uqfX ZdH]{㤷p|k zdb[!=ZĵfT{ }= l@9ST_H)7JUHŸkHNTt[crqd9(1NNëH{iKy /ITKd+"ۮ̋{9^]hFK];T pJccd 5y%n Ӿ\$ۿqnkĽ9ܼ<l+IF1\F,K:^++Y{PF|HJ,pPR@B=A=LHBm,=PL]Q}@=Q k`rjo. r;#!MMq] ]|xmπ^ ֟дL:!k"M՞L׌z٢&ϝתjje lಔL2Ө:=ڗڰ]*۬mۺڸ4Eۻ۽ͽGP PmCa<;@تqy=}о-Mϓٍl}%ظ-;lG2=bBpb.U{rڪMߨxu]-"֛iL&w ֦-mq !-m (^*,p>2^pM4G6bQ;^v.}j&?pAIt`\>\ba.]Nhdkm.rn-seN]M >l.|UW~pYCpg`ϖ>ϖ霎n疭NϜ^^ϣ^w٦\~nȃ^Y)؎>H7ս|i櫮c.~>ξΎخNt."n%.V ?+b4>pƎJN^^yu~j,֯R&] snލ붮벞n'o&^,9._ 9.ԝ n>SO|^ _ i>NެR=Ku !KQ[?u. _d~>|E^*1[븵貆l_nm$oϨ/꧎7㚟^qt?|W݈{n^m>傟Ouj+j"_1 }nn#8HCuH@ظب()yHU9hda%EyĊᄁaxz(Wk{ۻ Cacby --sxvF---ވy=ޝh~Nޞ~i]?>|HNB"+ĉ+ 61MrZLeQqҤxQJc:+фr^LnF%L2RҚcJ)^懓\ g80t IB 9IU꒒B. &z.W] +4qӒ0,ْDRqj+ gY<زgӮhqKSBt:-(w{c|MA}yDLn y̛!>|fѪM%`j=qP#AvrucolaHE|s_!aTTV^p0Ph%q[_'_\)(X`S6`!"MƑ}b h.UQ!^}%v"{)"n BiO6h6g6qf@n&q^+yfn,!Tg J~Fjh("$za57 Hd:$=HCɈ`> e#jʤP5Kn`Q*eѐDP #W,Yk*3בZf PbR*ᕎ{  9HrI6ݜ9 E*: +L< Ri?ⅳG)Q7*ekm劰 0c*j+.>3R4eo3]s'0PJvo+VVV;RܠkL㏻3q)rvBy8%AinE`M+(8l u;E^(7M+LB7QH!+[Scy+tN6ܶ4>2Ecڊ];iF& W˼{`(HW {^7Y'cȇ665581*_A@ mcqbDdO3_(Z/` ;hC_ c'?)"'?ߋvt0_؊%;D` `t,i΀_r%8S…D(zmGk\#ޗC7.ij\J?faX 3Fa aځ3ЁsNBP#c(My ® K]02`-{˶'9J&ILnDRT|dh%ҕUW,bEVpzJAlS~YĤ3KSTȶzd f+IًyBW,Sظʩ83e.s (J&}.Z EQ]_ eEЬB@d]z$#i* &eIGɽhcVu,brS&'jWTX-,Woՙg5UZ#ZRY6]bU=!!Ha gYVg uaw"w+A[0F jMV SPA'ڊ|hez> i]890u 5˘|Uz [o u}eVvv -Yisoyo6Y~Jlk{v!+{ftύQ$w oc_cI3Lzo"i iǴظo;~7;GwImo n }D/эNro݉>[t*Q,(cXOXU4M3ϋ}GOz+)fLo5HuKW'q֓`9;_ijz?^a<0A]Tȼ7: qXѻǰvao{ۀ:~~ƃAg$_χ~qwil.h1Gilb _/^@Ͻ/~|rvG|~'}}Hg}'o~w Ȁw72b(d#hWrUQ§m*m,Xwy--m7m2#W5mr؃B:xpD xhsY1TEɵ2txG]{7ȃ ex|g;bh}|`}L``& StWE .~dPh f'i煑8{&qjH׆ {|W}H| hnLJx+?\(F$؅v6Hnw7AA>x@9m}F،Lj/mHU4-6\u AM{腺|׉kЧstX7輻8͠Hc.j~xgXvX"` [E؏ 8'V9t(8+Y} i9.Ё)SZ#ȎgȂIx)nNIm9VXYQ42 +Wi!1B,UuUCEٔsh&y!ɔ3'G'cI4R ? S Ua7EiǗxUg9|9Xi|i\i(ybq:@ElGI |{W0N,P VR|8s\dl PV@\v;3kJ*t'w"kůz7]f:ygԽ.x{~( YVMcZػ}HM؞`>ZBMZ }-MN@JYyپ1PY}O~HniSs\wM3 I]*3ϹX ~N~rJ-MHi<.^k?$B?EGΧo U//Y_Nzao_AiߪFo%S.Z!Bt ĀG Z| o텰;)mk*Gx˅zI6?Bn?O?2o```ÿƿ»ħӺE[k8&ER1 1??N.Tю5P[5l`1k6"D2>h5 8rR\ɲ%!~6uZ 6s͟6PV#un\{9Q&Q3u ӷn{zlh|*./]C5iu׍+ k"!H9.l,%v-6ntCJ"ݤBK"T/'S/=Yu%1Ȋԙ6j`{NGfhPqK%rGkD̈,;JW 8 ՑK,^7p#6D9'K q{}C@̫.sm BXe#28K6G|- 9VDIYc q G@IXP*j~G#.{`L2lj0ÌfD]n #*2B0Mf93цM:6 @p*ϔ&@RԨNWVAVtGZ:UwkQ׽֪!#6Vl#L!~:~R̎m.ؔfv5P/6ѽn[miEP1BXe?;ONO9|wB#܇G\-~qg(<KMbh90g=sj|v3.~ẏ蛫's蜇; ݋^~&p53]Hr̿o۵P}yB|̪~H 3x<-g?O_inͿ{Wg|yf}Zowz]f|zG|{7{d|{vhwc"|o|uTRyG}rA{GGhg{hazǀ'f7<eׁF EkPP0 CsrB~^cx~`~b~OH]~d~阮tȄj'еRHX#[8f I>GEfgHgfN8f6SLazo}ƅ܇X8؁Hxwm8w{}wx ѕ[Rxl%GYrxIfnn}~~'tqVe(wh(Pѱ]|x5ȋx8b'8CxhhӈC}U(w+8nj4xmɐsȌ=Ewu]FI`/UdB:}+ǒ؎i؆~88hxha'hnw^GIMX8<كY)E ;yk!8@9C vL%)u!E`u9 Y87؋77)y 0@tQ"V(n]I蘝oyd ^țdff(f-yV9)鐉7X s|x :-WTFi{5yX*:Ht 1yJ7IiW]ȖGQwW'B9bXe~])yHs&99Q=JR?*('|Z iك&ʢ <_'x:&@z\aC|U1lJʆtjr*Y*ڌy{BZL'*0K| #FJ sYzʕ(8ȩby |Sʫ jzj 3Y) +: Kʘ.Zm& ꉭuڬc^acsҦ9z*آ[pڧ9f(T/Gwzf hyojiaoyMzʪ%ƙ]5Se*lII;ʱZ +ɥe& dҺ''#Ze?+a;Eˈ/ک:ʎ z'J>:lե3;eP^ٝ9H`vu)_Ivvh0+l8 #: Z*nڟn+  Z{JC0h &j۴c[Y踅 7M G:@VYrV+_`+G!˸jfZjٲ~'󫽤ܛKśۯYg{1{[;2˭Ӌս qe_kl믘J &,ܧI؉8I9"vr𠲊E„gؤXK3*Bg2jhU̿I<,\rgĤ۳N-'~C^BNJ>%N NNVSDG&o0(xUE׵G3[^]rI\`^Kp~eU_N)ZA_=od5P镞~n鈰pKNXꡎXm.멮-Rܜ-zl⿽{~|.wNrV.ݖz.*ULJh>FM >^؎ϮվN׾yKN^t.5:7i빮~n뮾N/"tM^W>2Pni~f6nwH}]B?NNGxMJodVvNoL/. o;WC3Rژ;Kj~Vp_vOt]w?w\?Xd<۩N =*o/?[oH;߫5>Hro Y缿Q_}~Α+R{ |&)OƟJO(*_ZoO !AE%HxH؈hX8(HYy٘x鉩 99jZI cda%E w{)!$UYQb@Ccr)])}}xM=MMX M Yo:(>j^8l1QA /!.庈1ƍ;:lY|1wݶrșls*WIx2c#y/h=%! LãԩTZE2fpƒt6Iaў]vmy"2[EPi%Qh9Hj>S%5Xqh)j *R٧IRxצu fSnq%c9C0,bЃ.Vv+H05y'ZxuҹlOpiOxvZ<J&mo2"09gr KCS_fm򖪶4mêJ}в߻ 2jz֧Zhš~\0aiJ}Nl⺒i `f\q< oz?&᪜J31JKc}l 0 GC,&`VTMZ7)t6 1V‚o]݄g֚jPgW,wW}t vr!Z*"_(ro/}βɎg9f|k%R\lN0'9eAIq/NPo9 Os8|#~}-ݿof?~+<[O[o=@D"dnRD֏(1(hAEbxpǥȬ{.#l!'0h?N01:h0#'T`B [+!I>P\)\*2J`$C"' .b,JHbqg# KA#+K\hFb$d>5JG}$HCkPݭ9S0= l+_ Xr+]Q%Y$\P樽RU9!*Url3 h%1hTfQ7 p!ug` xsG>D6 ^]R#$Yτ*t ]=%({ugC/ьj4%WD}J_#W01,nt,mi89?鹑 ? aN#M] Ԡ U0NSuLm3*Sn 1 ` &bR=nө\jWC`ߴop/娮VܪW WUғZ-VQu|e\3VUWA#Hu`hc Jֱ% 'Fs]FStz("edOZʖvB[Ԣkjo;ڪ㲙.*q\WUSvfd>af-hz.D!8uB@!VEr^:Md TWﻒ\Ӻ7}l{XjB*(4EWY%mi do-,a H@'7VLk߼旯M 8n :оRyK7A.2sk̈{x:,BR`!P;eT$$a?fqc/,apc+X3C a~z)ܶKAB+.tx [m 6e/Ds#J:[ξ /3h-syit)Fk0'J{Ҙ 9d. wW& 3+Bo&29XWm! _ݖگ?~?/Or"v+l߰/S[ק=޲Ͼ53^oq~7M;f5E-B4ee7}g}G}gGq}s7k 7|c7z%YWV[6@VTPxQjw{yuywӔ~A68W|4V"tHo&Xk(X` E%\s.VGnpZkfl$mH'hfwwKgdY "7PgXOŖjx'Y@US@H}01Yhk6zTi(b|YOi'#n.is2Iooؕ⨊SFi`nGgBGiȗY&Y`e9gRɩրvzG؝ItykfhIE,fPڳ=bYEHAw‡jty韤 'bdj;ƜVA)I*ى?雴y8g** 0@NG,"!& #q d6dFkᄥiYlI#sMU=SZե_Xʥoڟk QKڢW@t෥,l; sp Љdjiං#v zzjNyJZZ+"\ T,Y&x \u7&bgJ 0ʪѬjʭ{5ʤ'IEh:JdIDdK뚮B up]Z=b1ӮZ-LDhT]QZL* Ids˰b-W= VX#`*c:KC/+D1+K0Fj.Q:k㈯v춲54 K6KKJ+.kԪfò~Ĵ5;O:[lةҨJDkӴB]TtHF;`bj xt;kVh[Z?K^tV:@,Dlk P6뺯 +KDt[K ۺJ+NjOCڧI۫ E^ T`ዾT[P qٛ ˽˿ǫ«;+;Z4Ca<ٱ !l"l'<$l#( |zշN2(k? A,C<*|7DI?lGO `wQLIV{E;lg]: (}"E 0c}Qd3m#w T4ħl}| P ُS\ő-alU|)XúƱu=B֋ru؄J|-۱=MƳ}ٔ]ŸIUXevMőP ٹmۚݽٙ}κBNZM]ڮ]̝}{CBeer%kڌP}#W9HJ-Gf~xn-[mӶ+&kjK|y~Mď ?Jt-@lE`! ."` Ցn܋mYh~ôb^?l =ҷ.fj^A Iz[_oN/|E^;=x)/- O=6_ľzw;N%P>[CؽF*?RG]}o/E$W?=>= *}sA>(L=NjC kQ/uOMZ T`8\kH!.Of/ /iOǎn_AO#ӿ+FHҝQ߿T1V&kkHRRB]iNN r V43I4Vg›͎шؾՉ܊ْBTۢHp vKNIFB-7iq2laBˑ~hァg 2ۯ>~; .ZKoK%AN$ nc bd$X5|n3- 7ˑ6!S*t4MoS}n$IҜM@B#xI`q1.f@`4DV=QmPԬ&X/8#CS*0 d[j&V'V1QF2c%+:T`5ok0թVC:N{:_%lPY%Sn8{ܻSSgFSv%ne"uڸ*W4k;ڎSmkYzֺuo}+ lOkE7ĮnkܶW-#򛗰 (vޫt XE8([ J!G[>׻/ˡ8m5^8`0% a []U'sY7,fذ'6eLc9{D"aylI:ׯ>cw%W’s…]A~s_, V"岐g0sV.PgfũN˛Cϣs! IYG~a}a1Nf|N9)HIWՊ4 43Kg nnF Ycvd*aT 4 býq;*^+Pkqr%g-[pWf6ƣŽ`"< )7*nWLqz+H0%]ڹ#9t4P{P 絴]+t}t~Ɯ/׸.p"Xnc{6f4/n2_{M`vͯ`P7*{-r G{ǣ^p;ZBso,xm p"ӯls"+g<)#/0w8` R}qhyс$plG:[dVFk ~U (8 81g6@#Xݥ"/X̰&9Ȅ؃?x{e=#L,`F03H8oU[*ZvH`^4q*XKw.(^Cgf7]†mgsB8 Ѕ_x^d(bHk؆A\>;;`40$ahxVv& fvVwHzN#8׊8CH،Hzi/~8䈊>b@8uet~yp.XXxp8(9ٌH֘aĉCX9yd0p>pf0.,x\h1X&y(`׏0G292yk5U2,3֍3c4yHY`Yan\(gP!XBH`I\IKٔfB.WYٕheZDxPIjz,6 kv (,Dy{*}ɒcIsx٘y9UI$9y@IuRp<9;dxHXyx9m Yx%.IFYzY&`c1:7%iٕعɝ.@ms'j鹞y9_L\Y` ڠ`p`>9ߩk '&9$$ z( jb)PZ#Z6 ))y.:_ez€_40:1 hOJQYڠ_ ȠPeڠfz jd ko* A `@p.SZrh"HLzb ZZ`\ʨ*r ^ꨏJئlʩj  pj tb)0*7Zy :*Zz]ʥjp:*p(N cRPBYS::8zšک Ԙh_牀R:$ ٺʨڨ 08TJخjJڦ`I@I1*: 2[++?+[ |TG FhIz:-[8+X\X+,[@ fkpji)lc_dش(+SUa۵+T +Zg+m3/5Q }K7;~; Zۭcej[2 +BO{'z+ ȫ˺+h@+@Cnx&R@;۸K}+›`Z^۷ɻ/;˾{?{ԫ V[D"+{{۾nכ, 1 uR@ȁHй6Uۼ (a 1\K˫,Kçظ˱uّ~ uD+KV 3L<c벉 5 EG,؋<7*T`X*6<8l:[<=f(gkI;#qZi0@X&zDž,̛}~,|w̵ dlܸh ȥ 5RFӡ Qzə©{\9lܫ۱ʸfʒ Q˔ <$ }ŦŵŁ ̊ʱ\@ͷ Ǔ,>&4,,Ͱ|\; ё{zѴL D; \BaIIzeLѩ,<ͳ ;5z9LX, 49;-=->KBMi)+aЙO; _26m v б~]?k˹uKlp^=; {y{=˩ӃM }Gǎ}Wm o?\L۲Mmף׮ן}cM96μ8db\r͸/|۽ݮ@ @ :)m|WM}~kݷ]=7 Ӱ|Pަ}ީ\=$-=ӽ㭰\ܳNn]Ǭ  2* r4:z9 crf.u=P<1~W3^L8~G=;ͽMOwRc#Ly%tN)j`!ޗgvuĽ&Qg.i|.mJXyM}~DkBiP#& έ蘎~ޗ&eAmwhe陞o ޯ)\3s^!PII(NNΠ&. c&C^.uu=2h5ɮtkjN Jlc Np>sD-,`ixETEY0PB6lR*H{~M&ٔ(R, Nޯ0}}V<.?c^>&PRY1N}#O*-@`Pw#:fW&XDi PI0VWeX]QpUσWOZެd"iԗ)ppr ?Ud8*\ ޽!RIag΄h5?o_*şȯ/k@`OoC2#OoO֏n"l_ 6I90'8HHehHE)9IY)ؘhI9hzz*JjjaE @D+,jmNN +02*̜AFMz[4w*21C"fqQ5b9V '08yR _pTb" M 4СD iSѥLN8Zc&NVJE[|4 2fe.ꨥ"z*Zgzhjcm*-+ Ҩlr1{mRmRn "j+J CL4g38kΫȭk+qM6R n%뮠Tסxc- M < =4E+}mb-[ 3Q5;o|I9M0ao1=*,cwGY o-#JlmA4Hll4tx".IfX!D Y(|)e}xMs8K7N;QkT/Ny+w^zb;#y 1>{B/DP9Z{~#pƾK| G㻍k톃/>ﱾTzj 㲱hf ra_,.Liݵ4AN`wlFl!0*5_LPǿ.;d_ۼ8paᦶt Ġ%r&#E# j o+b/{< # yx0 >b+L?\I1U`P82Cv 4Q[fF!5£hQъ]6Gy1NH 6RH=7ɑb-ůd%D+vDuV,0ր6A #R ~ 9-d&^\M)iݕӜ]"Uh$3)Mjɛa8{9J(ӄc[@2و{Fs]]ܲo&HPímtR8$UPF1]R8myǎ?:QA j"&u~1q| :Аc\Letf S)d5RRfU9=1miLJfB f @џTVSz#}+ZqxFòDiʬaqzS"W9Tz.qH SR(c%;ڔ"]4.[[`zlmeGX:teEl!+Y֤%,/KuD(S_s"uiSOݖNW/(#r0\8n7]>2TM[}{ pɅB'hoE,_KoB4ǵz |U7j{-z.A%{ zr'kYH.X=*a I؀{̂h iH79/@0qKnj+öB&(IT.sL^`Ml=[P;oXe.L .K83lAk fSڸnY'g󮴓V W#FM͜xG5c&ыn~NKֱ5!Zb5kPSrPG[7cuiggYͭ&Ma[ e z`h zuE^ +hXq4[m,4ZQd)P3p!M%@5wAi |e#wS&po3A(mb76q2hrD`xj.RqB!p8x-nK\+O fiJhdમby1u?{†;| ϵG!5PsxN3=e}˘-c`qB{)?<]0%4$]Ok6^Y :%R0|Y(]@G(w9h߀<{bH|^ӿwOǧ yρ?)lzG硪\Wͻ~;T׷}Dɷ亚+(~w{Va~X'zm3P5}W8~0#' H x8|Wsm)#7Enpn=1&5hF7; $ȃ?x>C'8u)x= YQ2yGCU X_YHBȅVH 'wHXHA&@xkȆm +#0ܰNjy{9F.m,,mR~_nwNm?ǍFy ꯮Mq LɈ֩ > }.NƮJD, 34BJ֫,Pt .NxNȾ)^T~>킰ٽ3Nqz T9ݵ /%oRA/x 3 @'/3 )O,nܸD~sL IA4OE~DIQd>#enWHJ_,@oUc?Zo\[! Nsp,aMW8 tOnoKҽ:!^쇏]=s|ɍ oMtH` 0@b+/O:_f9]NS՟o?oo/싯ǯ0o͏8/ԏֻٟ[>3ԕS 84$11```1+++ġģˆ5I d&VRpTQN?NN1 r V&3,cFP`WUYhA W.[5dd@ntM5kR\ɲ).œLٯPa9faR%0psɺK*l•K׮:BX߻1-XJJܠoA{ޘysϮkߞ.[[+oέYyt7:Р G8iƏt U4)jT0 D dBcw&$!]{ %YQu BrGHˊW/N?Yr0Af, ܠ=l1;ի$B%׵.P"J.,tR9U|'!Kiɮ3a ٞ1.PL@z=",}dKFznIP6i'Lv0||*݆]+e AFV$x %\aGe~r3UѢM }W4I@!5HiҖR(OZS)B.~Un % V]i9X&u%B3 $I7J^r므 `[",qM%HT.JԖԕx#b7Cma9Kil+k1V-avEf7Zsܩ4-;`:Ѝt  A'Gf`a-Km,3 O|VI~_c?=0}a}}vr.3~y8Hhv8ggzE&PoRt8-G{284X6H'{7؃>|rT0@xH(gH؄=@eMxh7:'q}ճ 8tH~8Wf8`h:g؆v3U8o$]Ȃ.Htq(视/Ȇ888oyd ?r<&`bVo/G6`hؐiWhipax xH8O׊؋A$G̅x@pRlxӉ ~8X8ƗX^hĨxy$IČIר({8S>V\hjGqk ɍG I8_iuF6:ȸʘD^h!Θ)<'9p( fH稇"xـ1) ٓՈ*)/ Hp㨑 )j$p%9>wB ^ ZHe c?26RR?UI?ɕwg`EɈ), yl9nipi7 bm;+In8𧖈qi)1IAd!-%t,Pi{xd9 jə~~iX}u:h 'fKwImq~~)}I)Oɘ驞9!TyI~9yi ڙI H&gM"$@$ j~!#%jĹ)*ɟ-* ΩTaF0}&:Er`HY\ڡ2Ii KMKyRTڛ79#XD:G).L]5*13J2l[ڥ80!*! 2Wbow +3m:kz~VJf8,0!:v'mCI`eꙥ*wIZn7rڨJڂJqt4zʬZfs &z$7;!fzR ҰʨY ZZe\Z}XoigCmIz ?m~  t/pu_a?woxOx0@X{/if˧ذA ,!N.AFZQb9RߗSV5TO/`dh A-( )=鑤flG3w,:?Wn}?7dtC>NjlZ!8 ''~(X :|1X~V2e̺} $wxrK$͵cKw-$䭞y%g߿D=!ˆ)'}ڴPMyujϭgN [,ٲ]?yz+6N> mԯABF*$  \u)4ױ^9 䴹-{~8b5zmVʤѡ=\s]v ]w17 \qayٜl ' _ TN LUh Q8sٽL .2&Ǔu&̦@1 dA-^D- "Cgm7N*]f/xN;(( *E:IgyU4TII#`\qUU~5c(:DJɛCIDD_L9&b)?0Gls;Ps> oQD 0Zi ā0KeO{IĄ~) M4o; }EIPLV?"Z 3Մt n5MnrJT=(=67wIJ.34.l#Qs-I*o|-98.>qkb)1WXP U vC9|ݦ)YY?`,x<*t!tg6K|ۚTWЄe]6L QKH+z i{R8-K;Y]T\&Eb['Іt"V/T4b0KD???Q̎w4 EB,2H 2%h4,N a Vʎ'iO~Rg,Bib@uOZ !rL&& )_zf0UU-BT䥿`sf6)=Fqbf#) ) Ya`67ɹ{{>I2"-x4 3)iQm-!F;L|#*ϺSZ RP* Q hQsX``'nӗa, hҒԦEIT4W|>SRi aңDIFMJէvL UTaMXAfi*T֦V@؇/VAI HXi`/jv, ۈpu8El]#B2Mmk_ r$iݗZRֲo 6cu#,W<]#5ԭu۝^a}xr.t[w쭮vA Jtۋiר MhWXa nod@}  y) `.$.2N7Cx4&q3R.Ba"'q;#-xd)c$kyUr/(]\._e| n5sW *p<& 6 v! M%x30'R3|=KzcEC JLPN_(Piӣ'aa 3  % PpB kG:ҷns^ZV@blP;{ے6} l7BhsUH 5 ]9Goݞ7tzl.tx^w,7wXes(0v(Ϟwk1{ߎyXm/sw{?yss_Ӎ8uTGF3yUNл|}O|U}5BH7&|~'>xk/:@4 ] >h'}gy%~Z}w~{~ʧ~~~XGMzG{0|}xsW~t~׀(8#Xt_7vflV7 n`wgyyׂ xqO~dqTdp^xx C}𷄝{M{P( .|"8 UWϗP@8kfuH[m($'@RxhׇlGBIp0`H0H@8~kȄnF}huzx-!YAs6x4.xphkHZgϸY}{Hv~Fx ؍pHzXwߘdыk(vE N6q ]R@Ip!$zx8| hxYhzSXq:!flv А8y~vׂTyVw$!q{ ߨlHhih瓔ᘉ7T]X x{1wp+YVOgR~{kS{M^[H0Tiշ{oU$+8cYT[14K4PyL仿O+HQE 0%tbILj&k-uXW˽iԽ摼KUQ}ռS%KAR@Z k KI۾WS ,nAkeiN>%byK%lV K !®B˺)% /0 2nMI4М GpQ,SLU\UuT`[]X^,c `\Ci`&1K 8KѻǖK jfL{ǁL$ȅ,,l $kCqGO^|.ȗƙZ}V}ʢWVP<씜z,j˷˹˻fU˻ ,́˾LÌḶ̽J;ON ҫiKQA,Ll\lq,,xDͳ GLl%l #̑j k|an@6LK| #M%m']s1(/ 073}}8.($lGGjĔUMY\CNBJ!ljt->'|]~:n=elI~CI&Z a4` XZNB2~CnENAԝnR' w`qK1wLZ|.΄^~ۋ&dF¸P`Jiİޡlgվ^¾슾zs ^.~ꦎꈎC=Q|m^n` eIυC3@>\|0pMߤ\^|\tĉ qV7pڝ׭(/><)@~/x.FO 9o;D If"i!OPO .o_WϿ 6ԶhZa/md2o>kpKς.CLQK/MQOe @no~ɟ 0L|$TZm??_|t @4~ٟTr T p0 I34$11Tůü [kI0RT]T 1BNN1Rp] R&H84>T,` ȫ߬OPBW4pPPp\q\:ośWU.2Y3~&ԹM|?$ZSP***PSL?e1[`Ê]RRLx`֦GoC-\#.t-dJK >][u7q1a0׷(^02 'ocҭxnѸ5}) .F#=c0xuTsGݖԶw>~*XibIo =Ο)Kz(ł`lۍm [Er9di4i@Ձ[:(_m j aވc>Sc3 /AB1 ?_")â0H-ch xWin^]s^Uw-[塈- F#]H *CjByWJ88 !rI@qtBTDфt0I֕疪 azrZz)쏤"djE'&A  0@:HB\"A‰躸ʫج^u)2;okBTI'SZv6ܬ+qs 4ګ^w[0^ݱfuO1@9j #A[z= =r4'_3n{wk6w~ :}2k"3"m\G.˅/\vH2O\V ;pQ,$9K6"8IQ|(IiJ d,t{IhQS(X Ԡ9u ʌ3\ӗ|' )ysV#* DW\y̖nL'%NO$p^ u(.h[%˧!.TEgPVxIQzJ-eh7O*KHtRΥ4TLXݭr^ZV)uiE[[Vdqj]Ipf8gBGjb"r+f7K3-r,hGVhtVj5\8?`e)HFlPIYv-h+\SU-ki?>Flqb vKl"jmzEl+$@@Gu%۽|uQ$ -HA(A6B`{R/+ doO'@{nؾc׿D`Όf6I$\o0LJna$P.q'?UD Y2 fI]z*pd'&VD"$pɂm/Ke)WH3=..sѼh2+z^vŚ7̳<>'_/+VzВh>2kpՑ5t]ֹ}kb 4 gP3vtHptag8oDїlppy˖feJ7ZҏtWa(Ɓiqd>amm /WW-ZvwwQoj nyM8yz0Ʊ]`#;٫6cbgz.s:sA'*WG.pN@}3VP,8]|]Hu8Gz"xji@ek8`s!^Jw"NxDS0hX\oH(8ȸ]їu|'ti1qhȉWmzH:k+Wxs;Xz؈(kX2(XE3Ko0#rwƷ[Ȍwx뗑X}Cfv@=$h3PH+"3(\p ~ ؊h_ynBi5yۘ(i.’ |%ƇdOY(cgZ)ׁ@q9gH7D'S^sm IVؔYv2hu6I|Hx︊>ghvaI@QhvQANP=")~q׋b{kٖo'5Q 妅z)󧊣2'3 bEil9Bn -X5sP]0TɀȎ9ys>Ǖٚ1𚉹ytf-.`"df"Q1xǐIɚb9 v{ɗ:H)*@d$gP%:(}ঢ়9Ri/4ƙqƥ97pI9jd wN hr5zW)Z5fHs cci4P %1a!d4~/ɔUao 1JtqOSJY*oq] I_Z#Z[NjP*i,ڠsڛ`^5"X ByNEI@j+j!:uYٗ}ѩ [% tJ\z!C*My zZ`{:jdRP#t:кګ!A=V5%UEB/mr5*Z F5lTAJP ~4jNRԚ @J rvbMWFpK*j y #"6c%?P"(+b4[6{88AQP`BN8ӯ  $PR;T<+?C`эg-MO[d[f{R{>˟Z:7%+G:e( axz;!FNCZJT[s;&:|۸hkʵ--w ۹[ d"}PQEHTҏt^ ~-R{Ȼ*U{Fzj[ ˼1Hz 0F,v{{J% j+UP5D-ۿdAK 7 ]*۽8ENr0+k~\78= @;tBH`,<4|!{L<:"E#O>|<4PtYgټ tl·.4ύS&mѼb+`[@J@$@5@@V D-0VIML-HGVSMVJCMX}W-^X! `]:]TJn\Csv-_OmG~GwMMsM~M=׏?Mӎ=<@ٜٜӝ]$5P0$ڡکڴ*V ,@]2 I5}9=AE`LPTV Y]=`I=I]}iYqu]B-{܂؁]݄؆FMؔ=ٔܗԙ؛ڰڥ٧Ӫڮ=-۴`HkۼͽmM6&Kw-CV]݀}]؇}1NٖӘM >p᳭5}۹۽M@HZN$3&`4@H @ Np&~n^n~飮n`nnn^.ꌞN>.>40CpHՎ׎ܮۮn4`H3I>B,8@b  pзAZN1د>ю>Ꝟnn^>n~^'+>.>.~ST^Ro"/k@ژ!$@dˮ #8(*,>03nĎ.?AoEonnN־NK/R^[8P_ok\ҺHR@+?_؟ڿ?_Oa@DHl-S: kX8=tӤ[!ZuhӗE\;4̜-{ߛ9kl9+ɛ/':3'|sӝ{~ݹqu0_|3x$:9ǻ_]yg]f.g{VXy0QsU>a#ĄTW4(}Cgmfjhfmx~p#q *wwIyMvMwPW^-ހǩ7_Ymߖހ.H fBHT@4qay#NVe'vm,ڊ֚&h6c&o>pGdؽ7ꓡFI$~^ܕe]zxn R`vRh*<]UWD EP ⚋.V 2[;o/ /{LNVd0 +_ln  RL./o^\.0Ls%0/"1 ,pHE5^Wz>= B4MK߾qn0rk]g ! 7 u3l2qC Ĝu# w&2L# HQ`-5x[걅 ,HE҅m4$ICE2rb+#iMNd'5yl&_b)h'rWYH=BNv3: HOӏ,̧?iBӏgA@66ҟ h;P|cjޚ@RQ*(mQp"QLdHkh 8iBQŜ:O1b$j>PtTAP:QFHHƳ]D%ZQ~p4YwR'J孕*N.=dMq8;rL ; 2e@pq'\\ctbc v’T=YM ǵ5ŅnRc(RW'qeWXSV~cHi {EԀu=120O7pO#i-0#0-900905Y-07ْ-AٓmjpO AEYY&0S9S QT9S0(5aYZ9]ZЕQ)y69A);e9ri{ɗm_89^p`Opy)! xO :y4m9GjEh;ɛw[Rc2(p'І* ' mY٩h]x;Њ≋͙)ͩ2fX eyiPeЕ*ПЩZ)\fie )#hi9ɢ-*I*jN0;0P*iJ;iͨx8I٣Y٣hI:*0Sp &w q*sJu4GNFN?0(?)(x|ڜ鉇c` y9yʜɜ'*~Yx؆+*Hҙ'`؉'@J:X[[|ڧ xNpz鬇Jк*d:ڮ(~abʯڛernWQz ,N`!zꮄzꭆy 0;s0x(ڇJ zxrĩ?{?ˮ]-s= Jdg;a?R#pWwwy{wVSd똵WZ԰ZX{!X{K:iK[x(zh  [?P;ݚ }8Y{PY _ >a+X[-5e#? Fa DkN÷+K'%SuT1iKK=)ֵ簇b!ҿKa>()[<lSdȼL)xyqI9v KXTa6Uo)Aa95p wQ9gsjS{6yʖ mginmo q-sMumIy{tq ؁-؃M؅m}؉؋M ؏ ّ-ٓ-׌mٗ Mؙٟٛٝ-ڣM ڧکګگ sڳM۵mv۹ۻ}ۿ Mm'ɭ˽ uMэ٭f-M }ެ-m=}ڽ.n= } ஽.n=}!.#Nxp%+-N)..3N 78N 0^:)rs@;E^KME~B~ Q%p P1> A<_>X.GcNi[([>QU^ `~0[x}~o.>oF扮苾wmqtsI9灞nNn꧞n^n^B~B>>:.mV踎Q^>׎gn!sWN^fN腎nJ^~~on ﰞʞԾ_!/#O%o')+-/1/3O5o79;=?A/COEoGIKMOQ/SOUoWY[]_a/cOeogikmoq/sOuowy{} /Of;free42-nologo-1.4.77/skins/HP-41.layout000644 000765 000024 00000047663 12110237246 017767 0ustar00thomasstaff000000 000000 # HP41CY skin for Free42 (Windows and Unix) # By Guenny. # Key Standard 1 - 037 # Key 100 - 255 Skin: 0,0,360,650 Display: 47,36 2 3 FAFBC7 000000 Annunciator: 1 40,22,17,11 0,665 # UP Annunciator: 2 61,22,20,11 18,665 # DOWN Annunciator: 3 84,22,22,12 39,665 # SHIFT Annunciator: 4 111,23,15,12 62,665 # PRINT Annunciator: 5 129,23,20, 9 78,665 # BAT Annunciator: 6 152,22, 8,12 98,665 # RAD Annunciator: 7 160,22,22,12 106,665 # GRAD ############################################################################################################### # # L 0a: REAL? CPX? EXIT ALPHA PRINT # L 0b: ON/OFF COMPLEX DOWN UP PRGM PRGM-FCN Key: 100,101 25, 90, 55, 23 24, 90, 55, 23 425, 90 # REAL? Macro: 100 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 4 # REAL? Macro: 101 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 4 # REAL? Key: 102,103 87, 90, 55, 23 88, 90, 55, 23 487, 90 # CPX? Macro: 102 28 37 1 23 23 23 23 23 23 23 23 5 # CPX? Macro: 103 37 1 23 23 23 23 23 23 23 23 5 # CPX? Key: 107,108 150, 90, 56, 23 150, 90, 56, 23 550, 90 # EXIT Macro: 107 33 33 33 33 33 # EXIT Macro: 108 28 33 33 33 33 33 # EXIT Key: 110,013 214, 90, 55, 23 213, 90, 55, 23 614, 90 # PRINT Macro: 110 28 32 # PRINT Key: 105,032 276, 90, 55, 23 277, 90, 55, 23 676, 90 # ALPHA Macro: 105 28 13 # ALPHA Key: 200,201 25,115, 55, 23 24,115, 55, 23 425,115 # OFF Macro: 200 28 33 # OFF Macro: 201 33 # OFF Key: 106,007 87,115, 55, 23 88,115, 55, 23 487,115 # COMPLEX Macro: 106 28 7 # COMPLEX Key: 23 150,115, 25, 23 150,116, 25, 23 550,115 # DOWN Key: 18 182,115, 25, 23 182,114, 25, 23 582,115 # UP Key: 109,036 214,115, 55, 23 213,115, 55, 23 614,115 # PRGM Macro: 109 28 36 # PRGM Key: 104,031 276,115, 55, 23 277,115, 55, 23 676,115 # PGM.FCN Macro: 104 28 31 # PGM.FCN ############################################################################################################### # S- y**x x**2 10**x e**x # L 1: S+ 1/X SQRT LOG LN # STAT INT FRAC ABS SIGN Key: 111,112 46,177, 40, 22 46,176, 40, 36 446,177 # S+ S- Key: 113,112 46,200, 40, 13 46,176, 40, 36 846,177 # STAT S- Macro: 111 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # S+ Macro: 112 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 2 # S- Macro: 113 28 22 # STAT Key: 114,115 102,177, 40, 22 102,176, 40, 36 502,177 # 1/X Y**X Key: 116,115 102,200, 40, 13 102,176, 40, 36 902,177 # INT Y**X Macro: 114 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 6 # 1/x Macro: 115 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 5 # y**x Macro: 116 28 25 23 1 # INT Key: 117,118 158,177, 40, 22 158,176, 40, 36 558,177 # SQRT X**2 Key: 119,118 158,200, 40, 13 158,176, 40, 36 958,177 # FRAC X**2 Macro: 117 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 6 # SQRT Macro: 118 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 3 # x**2 Macro: 119 28 25 23 2 # FRAC Key: 120,121 214,177, 40, 22 214,176, 40, 36 614,177 # LOG 10**X Key: 122,121 214,200, 40, 13 214,176, 40, 36 1014,177 # ABS 10**X Macro: 120 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # LOG Macro: 121 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # 10**x Macro: 122 28 25 23 4 # ABS Key: 123,124 270,177, 40, 22 270,176, 40, 36 670,177 # LN exp Key: 125,124 270,200, 40, 13 270,176, 40, 36 1070,177 # SIGN exp Macro: 123 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 5 # LN Macro: 124 37 1 23 23 23 23 23 23 23 23 23 23 23 23 1 # exp Macro: 125 28 25 23 5 # SIGN ############################################################################################################### # PROB CLS ASIN(H) ACOS(H) ATAN(H) # L 2: x<>y RDN SIN COS TAN # x<>_ RUP SINH COSH TANH Key: 14,027 46,234, 40, 22 46,233, 40, 36 446,234 # x<>y PROB Key: 126,027 46,257, 40, 13 46,233, 40, 36 846,234 # x<>_ PROB Macro: 126 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 4 # x<>_ Key: 9,127 102,234, 40, 22 102,233, 40, 36 502,234 # RDN CLS Key: 128,127 102,257, 40, 13 102,233, 40, 36 902,234 # RUP CLS Macro: 127 17 1 # CLS Macro: 128 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 6 # RUP Key: 10 158,234, 40, 22 158,233, 40, 36 558,234 # SIN ASIN Key: 129,130 158,257, 40, 13 158,233, 40, 36 958,234 # SINH ASINH Macro: 129 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 2 # SINH Macro: 130 37 1 23 23 5 # ASINH Key: 11 214,234, 40, 22 214,233, 40, 36 614,234 # COS ACOS Key: 131,132 214,257, 40, 13 214,233, 40, 36 1014,234 # COSH ACOSH Macro: 131 28 37 1 23 23 23 23 23 23 23 23 3 # COSH Macro: 132 37 1 3 # ACOSH Key: 12 270,234, 40, 22 270,233, 40, 36 670,234 # TAN ATAN Key: 133,134 270,257, 40, 13 270,233, 40, 36 1070,234 # TANH ATANH Macro: 133 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # TANH Macro: 134 37 1 23 23 23 3 # ATANH ############################################################################################################### # MATRIX LBL GTO BST # L 3: SHIFT XEQ STO RCL SST # MI DOWN MI UP MJ RIGHT MJ LEFT Key: 28 46,291, 40, 36 46,290, 40, 36 446,291 # SHIFT Key: 135,136 102,291, 40, 22 102,290, 40, 36 502,291 # XEQ MATRIX Key: 137,136 102,314, 40, 13 102,290, 40, 36 902,291 # MI DOWN MATRIX Macro: 135 28 31 6 # XEQ Macro: 136 21 # MATRIX Macro: 137 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 3 # MI DOWN Key: 7,138 158,291, 40, 22 158,290, 40, 36 558,291 # STO LBL Key: 139,138 158,314, 40, 13 158,290, 40, 36 958,291 # MI UP LBL Macro: 138 31 1 # LBL Macro: 139 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 4 # MI UP Key: 8,140 214,291, 40, 22 214,290, 40, 36 614,291 # RCL GTO Key: 141,140 214,314, 40, 13 214,290, 40, 36 1014,291 # MJ RIGHT GTO Macro: 140 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 5 # GTO Macro: 141 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 6 # MJ RIGHT Key: 142,143 270,291, 40, 22 270,290, 40, 36 670,291 # SST BST Key: 144,143 270,314, 40, 13 270,290, 40, 36 1070,291 # MJ LEFT BST Macro: 142 28 23 # SST Macro: 143 18 # BST Macro: 144 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # MJ LEFT ############################################################################################################### # CATALOG ISG RTN CLEAR # L 4: ENTER +/- EEX CLX # CLA DSE CLST Key: 13,145 46,348, 96, 22 46,347, 96, 36 446,348 # ENTER CATALOG Key: 146,145 46,371, 96, 13 46,347, 96, 36 846,348 # CLA CATALOG Macro: 145 37 # CATALOG Macro: 146 28 17 5 # CLA Key: 15,147 158,348, 40, 22 158,347, 40, 36 558,348 # +/- ISG Key: 148,147 158,371, 40, 13 158,347, 40, 36 958,348 # DSE ISG Macro: 147 31 23 5 # ISG Macro: 148 28 31 23 6 # DSE Key: 16,149 214,348, 40, 22 214,347, 40, 36 614,348 # EEX RTN Key: 150,149 214,371, 40, 13 214,347, 40, 36 614,348 # EEX RTN Macro: 149 31 2 # RTN Macro: 150 16 # EEX Key: 17 270,348, 40, 22 270,347, 40, 36 670,348 # CLX CLEAR Key: 151,152 270,371, 40, 13 270,347, 40, 36 1070,348 # CLST CLEAR Macro: 151 28 17 4 # CLST Macro: 152 17 # CLEAR ############################################################################################################### # =>HR SOLVE INTEG FLAG # L 5: - 7 8 9 # HMS- Key: 32,153 46,405, 40, 22 46,404, 40, 36 446,405 # - => HR Key: 154,153 46,428, 40, 13 46,404, 40, 36 846,405 # HMS- => HR Macro: 153 25 3 # => HR Macro: 154 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 2 # HMS- Key: 19,155 114,405, 46, 22 114,404, 46, 36 514,405 # 7 SOLVE Key: 156,155 114,428, 46, 13 114,404, 46, 36 514,405 # 7 SOLVE Macro: 155 19 # SOLVE Macro: 156 19 # 7 Key: 20,157 189,405, 46, 22 189,404, 46, 36 589,405 # 8 INTEG Key: 158,157 189,428, 46, 13 189,404, 46, 36 589,405 # 8 INTEG Macro: 157 20 # INTEG Macro: 158 20 # 8 Key: 21,159 264,405, 46, 22 264,404, 46, 36 664,405 # 9 FLAG Key: 160,159 264,428, 46, 13 264,404, 46, 36 664,405 # 9 FLAG Macro: 159 26 # FLAG Macro: 160 21 # 9 ############################################################################################################### # => HMS => RAD => DEG CONVERT # L 6: + 4 5 6 # HMS+ RAD DEG MODE Key: 37,161 46,462, 40, 22 46,461, 40, 36 446,462 # + => HMS Key: 162,161 46,485, 40, 13 46,461, 40, 36 846,462 # HMS+ => HMS Macro: 161 25 4 # => HMS Macro: 162 28 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # HMS+ Key: 24,163 114,462, 46, 22 114,461, 46, 36 514,462 # 4 => RAD Key: 164,163 114,485, 46, 13 114,461, 46, 36 914,462 # RAD => RAD Macro: 163 25 2 # => RAD Macro: 164 28 15 2 # RAD Key: 25,165 189,462, 46, 22 189,461, 46, 36 589,462 # 5 => DEG Key: 166,165 189,485, 46, 13 189,461, 46, 36 989,462 # DEG => DEG Macro: 165 25 1 # => DEG Macro: 166 28 15 1 # DEG Key: 26,167 264,462, 46, 22 264,461, 46, 36 664,462 # 6 CONVERT Key: 168,167 264,485, 46, 13 264,461, 46, 36 1064,462 # MODE CONVERT Macro: 167 25 # CONVERT Macro: 168 28 15 # MODE ############################################################################################################### # x?y? DISP CUSTOM BASE # L 7: * 1 2 3 # MOD RND N! GAMMA Key: 27,169 46,519, 40, 22 46,518, 40, 36 446,519 # * x?y? Key: 170,169 46,542, 40, 13 46,518, 40, 36 846,519 # MOD x?y? Macro: 169 31 23 2 # x?y? Macro: 170 28 25 23 6 # MOD Key: 29,171 114,519, 46, 22 114,518, 46, 36 514,519 # 1 DISP Key: 172,171 114,542, 46, 13 114,518, 46, 36 914,519 # RND DISP Macro: 171 16 # DISP Macro: 172 28 25 23 3 # RND Key: 30,173 189,519, 46, 22 189,518, 46, 36 589,519 # 2 CUSTOM Key: 174,173 189,542, 46, 13 189,518, 46, 36 989,519 # N! CUSTOM Macro: 173 30 # CUSTOM Macro: 174 28 27 3 # N! Key: 31,175 264,519, 46, 22 264,518, 46, 36 664,519 # 3 BASE Key: 176,175 264,542, 46, 13 264,518, 46, 36 1064,519 # GAMMA BASE Macro: 175 24 # BASE Macro: 176 28 27 4 # GAMMA ############################################################################################################### # x?0? PI LASTx SHOW # L 8: / 0 . R/S # DIV % %CH VIEW Key: 22,177 46,576, 40, 22 46,575, 40, 36 446,576 # / x?0? Key: 178,177 46,599, 40, 13 46,575, 40, 36 446,576 # / x?0? Macro: 177 31 23 1 # x?0? Macro: 178 22 # / Key: 34,179 114,576, 46, 22 114,575, 46, 36 514,576 # 0 PI Key: 180,179 114,599, 46, 13 114,575, 46, 36 914,576 # % PI Macro: 179 9 # PI Macro: 180 28 37 1 18 5 # % Key: 35,181 189,576, 46, 22 189,575, 46, 36 589,576 # . LASTx Key: 182,181 189,599, 46, 13 189,575, 46, 36 989,576 # %CH LASTx Macro: 181 14 # LASTx Macro: 182 28 37 1 18 6 # %CH Key: 36,183 264,576, 46, 22 264,575, 46, 36 664,576 # R/S VIEW Key: 184,183 264,599, 46, 13 264,575, 46, 36 1064,576 # SHOW VIEW Macro: 183 37 1 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 1 # VIEW Macro: 184 28 35 # SHOW ############################################################################################################### free42-nologo-1.4.77/skins/kacskin.gif000644 000765 000024 00000165411 12110237246 020101 0ustar00thomasstaff000000 000000 GIF89aLlD,-* "';=8sQ9"""KLJHA@^YKn)JnI”Uƕ_6cY,śd'hoVgY)Uhgs |y*j $rV$yvR?*h#}قjfNfirk) >2a,F WVZ i "5 ŎɧiRY9bz{{i#+d»Arn?2c):盓I/}ok= *$$n" G30t& ?Jn ,< VL;BJE}pfTL=ƹ`c3#=2=K:rjЅˮ!F--`*0<,X`YAe,-m㠀< vhA=[08pd~FJ>|igh.izuB䳣=97E01Í: :ܭ> Kz$:nF0Aֳ,H kF  +89 hAT`4GiR$iy<:Tailkޖ<@Af% #A]1p,':l/8 qKz2+n|/97Bcf7)QdcεgZ IQ̆2Tmb*B@L(F )?@pHV&~\ mEd gYF 0p@ x^%',*A3(`,\چ-(r,tyq==eQ]Ȧރ7Tp0oN T`N$ AZF& 3hTPpvG1`\4UHYD!i7c#Oz,x[6Qƭ@2A:ʏ(|* [@(D"Db ^˩*hQvǠg)L0 *ЁAUE(BW r0vj(pp*v*+`0av-C )@dn gcB>WpG `az@WAne`AQo,Ub00)}VC@T~AG5ܧn2#s$ P Gu@Ykf L R%ZnvU>Z԰0pQ"t  A$:0kReގ%Ub\Y,\ p uc.M#lܷ``7;L}%"~|0tFW|:޲h~Mopi@Z=A`@UVMyq_ #LamX&Amq)sA+C f-jmpG)BϠ[AhW8o}N tu-8+E`j}ƯA`{6ߦn 'e A Rm'4~ UBa G@$i@UwA_Y#Laa ӗ[ p#s "X:zwQ ]P|x$"@|6_lUsY]## ]ŁmVIfǁ. "szɖkh;v3(H!. ,q9Ϗ\ gWbwЀ,ÅLNA[tp*=zYkd(+}eGE$!ߕ+uS?+9g`,)ٻ[ڸZ/`" h`7EY[J0u^` pV )P`VZRsV`r%?mfiC0EPWd1hW8pzv0WqV'_8cuz/{wZd[,d?I6փ2V/SCdhfiI%@@n05SVP[wuW6JUf#Y@W5*04#y|dc\yudWWzv@t~cxz1LJ `nucLn2p08J0)@l&y#,`@0uu5i `T*hVq vV%vMHd ބtepV1wScfpLp:zڅfwL (ye'p?M_Z miqWB;VW?2@tvx#Ёh%hIxNZ_"#<8Ot%Z5Ô;H'f#WsUK;hh`,rnWIvᆂucdS#yVV:}E?//Vpfxߦf:3lM@Pa`Vq]08#gV=`WW0 oP|Uvw4}qMowTzecTn{U@gك AHyzl w%s#4xMSijX d `df4uz#{1[@6?gA+P)`2BJ099j"j@W AUER[ftRAj9,#C,8>C,%]R$`&#cr&34gr6'>9:\:<::Ĉ*=б:}]tXt2!@v%":8ГM3)T,-Ǧ*u\uLtRǦB}lx\vZǐl*ȃ\ȇ Ɋǀy\$Ȃlǘl5ɍɠ"ɋʣlɦxɉʦw{˕\ʮɠLɽż <˥LȶlȲˋ Ȩ,0&5se}*!#8&9EP(WcIE@0a0 L hap |=]} @]IZE);EjZ $9 <>@B=C  VP֢+[0 $* ~D bDD`b=d]V| ҚfVgd?XWTW #*[`ր؂=؄-[P&ce wixE0Յٜٚ]q|)cI 40@bxW=۴]۶-@ VVkq} @'*@5sI)T p=dnfv4ܽ2eo] M}MFmVJb} A_`󓆔iD ͧ8`;i(T24  Zj ~Nd^f~hjlnpӁfjq<4@W޹uMPP""@kdJ"ϣ|~Jԡ~϶kNP0덾ndꍮ"ꡎ~Nn..N~dbk`0N>NX>NѾ͎l꼾蒎~BsޤcOR5,~~lYWMD@pZp4/; `4b@ή~A?>^n.M\~.N_`dVXSŊ M sxj A!ԥ4CzllV!B +C$  Z``A QH{b˞M۸sͻ Nxo%NM$,@DGP y}laˆ/L@1Y۸˟OqB,WܼLj}EXۭ C=B=R{u (p a5[Hul@@ 0 aB :@:w#6PFr@x X] p[p fN@xXRPD`ÍhAc# @Y:tEe`$atz'*ꨱwW2X:DI}ܰE Z@#ptb)k)( 0]aA[lIytB1覫npɢ,8d[l# l{P(&¸% 0 b8,Dfp^`hx.,LW 0KuЁ XpYa$L7-%bG@83*lA lnP'NvT"Š0BLnaAܠoG.9mPs``B\=K,@IA l)/=w4Ԃއ!x: @!lgCY^Ps-6+tlPI_HBlk.w˕00 3]) eҦ$CE=q`nPmXcB[HY8l*]mE[ǘ7p3*7t)X\A(0XlYx( $fC)_]R@N ̢%.[LD !-4nV =5F{ TF&FS3 :.)HJb+ t!8dl@p 'A6ł$ᔿCKsfZIPl{ J_ װ)_g4BP3K uh!zCS qi@t{aV#WSFx*X6ٸl,;؃2pcB&s@R^(~=gcUUoA^7y}&@XD4~2XR`7z`Y* ¶*L1>؍up 5-u \>vB wS)gMBCLe(XRHL#>C@pf`AÀB+[h *0``a, gzռ e2Gba; *=N ;NONnJ?U9Lbi(5?a"t40@}'wsV@61uu7` x:ᇁVs(RFm$jx0@C  ,)R0GodkͷI}1%h z{!bWZL(Gh8idTxjkL@@+ P&)=cX1D];+pmKwrQ  P?!}ChG^ 0<}7}B_ `cX_('/]w9fH7#@dE&(gU(]0X*h(;H$#7k$bH LpRIHPfsVVM u87,b:ʘSr!0w+ h Q~PbE)LS"v#`,"a}0X&'BP5zPR8{7;@^23O0`) LdiP0JJB4L7p*%+U3Xk! @RqK9My;b nY5~}z-jv"nt\З~9Yy٘aW3{rX_Z%[s(Xm/ @V   T Ea P@P  v1` B@ Y 7Q{1 3q `U`0I@  Y噟P e!aP 7(EN\G#p ^+@w!l'" `4! b !᠑Т1  Y F M "` JP `Mq@`1 ?Mp  c ىY "Qq Y չ "It+u(.LT8Wb}!P1)Hrq P /TQ 8|Asj p ꤊh&Q 7hD), ]J ې [Nj?aI: \*P@٦;QakIJZȊ (~1vi\r ppw.02;4[6{8:<۳>@B;.+I.!0I$I$=`I@iFrXZ\۵^`b;d[] &I`U#C[0]]^i|۷~;|^4YE@@Uֆԇ8``7q/H{b;QC0uGA@V˹;뷆7=r V{ț,ppwpVf"[q @BCpƨ{ދC(RY(~Ap#VPRP\Y,";sB;p8426"<$l~`bSL `%:X1=P1ep%b`6R@B=D v"<7 }`Z#<7}˼\M7!;E$pM $8"nQ_Fz|_<CBã!mւ!P)np0%YBE$Y=ؘ=qC@I9>_ lc6ՙڋ;~K?XyU{:pLƛFEs+p*KI-adg^ T/Qw+UBI0՝%@$7 D#wAQ G|6}$#02K1gqaV^ PWa3Fe%0#3_rd~[KN(`@s~(0QubriεM( C&=%e(45p^wK`Q`>2`{ubj _w0샋uNw nqnڿ0X,YPB0< |_=~.w`W wPnn>^8AQF2L 9dL _;)sK>3J4?0Z0b Qa 0nA;Nd>Lo Q/W?^Kj/.F#&0FU"@_\/D.ϹO7^[odrWN͓aSuҬ55Swoke_.N_[N渿hnp"B#z#&~PF:D|2H` I#+Z}}:#Y+CC,,X[-#} |kbbX(APXų|XPXPPԢɮ㶾bkʥ VpE Z`$A -$`-h0IS @2xa$ D1,7+=y@16AY ɟ@ 9ȖI?" 0d&Xkw!NQFMt`Ka`ꉂ x*2* 1{Df߿#@t(2bE @`g+Xȩ{(YD:.d"vʑ |݇ư-JINmDP PPyV`I)٫𧍣R\BI(1ɡ>  YB :` Fh-`L$T1HD{%ᵀ}8X '0↴x@b \-,-XR5s)4`PFJ0,S$d)aJ4tO:p@RH҈PPS|@F6ՔR*TYeBp Z -@,@|(ZG`"0AXōf8⍦:J)&5X@$ڡr"BtZ+>4`HB$:xaG"h"UpaX'G|7BNdJ*| }*,BBA+D(` ` rx-ƴ⮭xp1XĀT@0 GT.km+/580| İ@K۹+yUo'Xl60Q7حRd')6N1D-01ݭ,"CAoWECJ7M/.-?5A\^@ALd^)8ClхA8QʠhH 0HDZv6}@LyOYEE7bxG mx0AcډΧ`x<'d=⏯Kav}߯uTޠ< AL7 gP Tys73С/>ºNP7pQcZ"B@f T0 k<U@ ͍.C/ p@1 HS16H"D\GP}m+Lc/ʐcZ^25;Ev Q"p"[| `ͅo_03Tc$g x`@1stTcADDn?: %09^pAIn{{4Iimod-Vև , Iazƚ3=X^`Ġ}P,4 xoe/# L}:Uhd)GnI%-0!H DILx Ї _S @9G "1a,TrTcAAJ !ޜV¹as$ aV٩NU9xK!KH%j! n5,(uROPiآtHMbQ5F Ǜ,Qyف4jAD Xh+Kڣ֖@@N?!.oH u"q@ja( iL0` 0A0 uBtk2)K@PžQ5q3q(i<[o*T@ΰ},v`oRr;Krցm_Ɔ *b3݊ѷ<*Sw)T@DM#l!\0ETL㤁0dЙ fP&0 Ʋv wdĸ;ڭcaCN? d.fGLF0 J8А.K* {bs` (>n+<; dbrNe@ |@+G46!Tmp<ԴO $&R(fm[p *qy(ݰ})/RX@QceWc]GhZ `b2 =`ˀBA63& jƉ@V gM ` ')Y?YhpͿ),'=wl?T?x5h; u^o2'u2z/p-i|ӈ%YǸƏ[g8@P`<Pp+pyvwU?[`;,Dx 0geh)gӗBsRr5#PaWF$_-nNK5r``}\Xأ!7.5vj%bzXG>(@Op_HT) M}>okI6PbY<{pG׃EpTsj0ȁ'&`EY _;OۆaԖs59gQjT+ҡ[t@sj4t1`)/jV6b@&ãnB8`"^$,EM $j.yxj?@OZGXSj$޹)ĕb[oZYZKtPb00o:%T3iX)DPq8zkf{Oږm9^'Sm0=h 0ZD41[SUpU@@8 zf$1k@׊Gax_hQ}idC%&3>8F}pN!?@`\ >M&'>X+špb#;6.IWI"qyy,qTGCXZGe`}:&:@> W%),B{o; ' qXZp=;E{hqǧBǸ9Pq_bd.ۂi܋<ͅZtC𵎩[+贲׃e[pk㘆hh7e55B}enr^-Tp"& ֭~7ӕ)d>;}qNT8hIC@7FKͻ+.!> S8{(5@ .C,[˧+uՐ3|Q)<8H9Vd2V@ kM48( @I 5=Ǖi=HӮ֡ZGô֮}ѕXc5?#59 @B /pK@"q|öLǹ4Nv=" &6\ie. q=i(gy|jy゙<A#*SB)0^#|PD=q#BB\}q76̥T Р?b8y"at:6p X=&(l 8"zݪyvk.xd",/J9޼N7"mQ〶^rt7${X*x:,a!`mϴ AǣrXE6&Ca>n\_3&c5h\6cPEHk.5\Z Pyݻkk؉`6==ٮT$&-}$ؼCN4jL:fVJ2S 3d :EvygMU^ ;!ppbsR=4vb8Z<> 7bZ4v?m8$32WSmݚSs] s۠3=11 1bpMO+9JS??!^Rv8Z<[0KхUw#]]+B8 mX3U?Fޔ^q%ra,ay N*TI~eԔQ!z+.2$2r[n,pPi5C^:b5\ 1]`A)Q?^c\f/ad;f|P޴ ~Hrj~y(qN5AС!CX@0zq8p `nTBf`LP[#Y( nnDA  @@p. kא ``40 o? zL!@U7 |0DC+,.02?4_6_!  ú X[Jt7LNPn> **NVP D[PmJ/hjlz\{(*p^h a*#o?_/A}[RX k`b(.?_ȟʿ/zLE0 IP.NAn `,?_  #LZ}}[#+XC[: 8} ~ V!V#E 8Y+- , :: [1ܥ* ,]8T} +;C*,[ =/] @HV*X&A$FQDž CI[A*$ٲI86 It`㴒@ D@C1jK)(V'Ntʑ(hʶmo!F@P"&YnҨ/@kOQ0s"ʑJc͌ #Y>|(i00fQY\sc5Qz[bƎ!s> Fi\pM}R& ffhK.VBa4U Ò%J~"E ['y \F5DhXgShDu&sUaه~pE48֌(^1⁧4%VlA=!"H_ ~ԃ=}Io5~g1 `@ bS۵14P.P8aI B@.1Pb!xX 2g+B.t 펠>h 6 1p0 0 $pX 4Y f d@UL$*U VrĀ `S!FYJUL Х. -Ќ0$%!;(#5O4*=@Vz 9=>2}8i UAY{BS$`,@ g8ř^TǦ*s!Qqj*meANM/xuZtS eI٩V0T @FP #OM}}DH0tEϙPF< It󠪌iwj `*Ww`(:W5[g))8BYX]!FpRLk^Aג^ @1  8Zή* (i#c! pA,8"ZԺ\${βԕUبe]I %glɉsZt5D;o^HayvI̶@ Q@AG)a X% )Xu>v#4ׇ` Pj[ @+Ekj"I8_ X`TnW<1^QpYN\u4mH$` x5JLKһ2=SLao"]@˲Zi=Uw:CCL#Ā}Y=IAß8@d(JW6݉}B~h\׹b# C|r@Yh)+)!zPk'vڵ$տ,NCk.0x#L=K=VS; ҡ,V8 \e5!c=Pt<*>A#i(t) )PaBhxIi  6؀Mj|gf$mDbFul!# €-E; 08@nt6р4$e]xXL5(y7645n39yVb @ꚛy/u^ ަσ>գOb{O80hğ(N-I[U?un C Aa XP-`@yT7/kuk{b`x`X5Fw||FU]et\o1 H qv:O$d`oho|pFwZ4et#_@`wpu&X&i}Mtp,X؂-W1bn6~,p BBvvv#`:;vurZE"p^(ad '/`&h ef.X1wgO_P+*8fP O`8(;)4P8Xe 8|4T ,%9znm` p\wrv}i=egH0Zhh00u(((hh[@,였hHC`}ph˜OH p}3`k3` Lu`vC<<+?kY3꤇+E *P.2S0$<}@Lh <ێjP jh}^ >0jkKHK{Ơ,Hr+<0L5ahxB{;R91%P.hԿ Z-9n{jK\?r0pOpǛyxu[%]3 L#BBeNQ!@LSUfQ E xKp0> +x$]8<B3PMmzH`&=_{i-= ypxUصw>hzR`BѠsv01[i=&m;[ʀ{aJx ((LLԆ :fFe|)鶊4;ȡ?` ڨMPI9ٱ&Ni`?)0Lǜ6ULxji&--zJj?[@GH|UOzYe191Hd)Jk`@<19 ˆskL J[_.m@y4}ug&}pNY}{wpִûl̸KWKN4 xަ-1oH` l޻L0gugNj.OQ @\F+u.:NUjg$ ʅgtEt^nN^vOlnbgU%_J 𲂎NttoB(h;h| hvph4WgG&1)XӖ3jqPg~2`)+|3rBJ(PcO@WЈN.WB@3t $g@'ⴠ0@H-ZӐSr ' F-(  + Mpz"z~l'NJҲaNQl&Y+XF?@ h)OA (?͗ztruꇶVB hY qD@d'GR`\e"29?I+h@݄5z .5%Ơʀ' +T`YF7`GNxvg5ZlZSQ(T@p!(-4)(p|π|r~A< zt !_6И1ЗU 0 }01ѓ?BIQvO} PPYC Q' 8p )oP@Y64Oٓ0P'~+ד[I* `"A_=p< `k 1)E A@ D7 `I q#RҐ  | 09G2"˘ Ɋ!qpkZ#(}R@C v٘1 B005ZJJl13#E<47V-),О D h 09B pQt,``  ؒ@ E7$qx 9}0T$!UZ_}1r!J90¤;zЙ ҘpU`>I`e ±6WGG1& p&n-E~#A@I阵 *aA)s j}op Z}i*ʩoEګv !ZzT 0Q y%б`*؅p7 GeV2V% T7k@  b;b[4:ba;nkn+>0r;>p q +۫*,;.|{ {aЫ-͠|M%kvu{иJM@a}4a!&lTw3l<~7Ef@.VG@@EzR+_  k + &j}}5@)`7 ˱J5e{З`3pb06GڻBRPsJB@Ns~:`Y@  +sÈ \* &  z-"m0A0". !Ķ03U z1ZDpoRBq*i䰺`{ 0;ȢqΑL 0PH v%_ [! < *)˱:tal& ʪ-"0&=[U;ZdoRel T@dp d|^]g j-< )к~m<%QMp-l<6Pٟ; %b{P0>p -Šm^<ٮ&>)̨_&۽-ېC'&i/`b&mЬ➵z:X֮q-cg+mz[[\ zߩ*u=R@  5PMМz4- [0Z[k}m 'хzʋ1=uݵ6ü :zT67P834Ep:Dzqq~0!Ҟ}Uΰ@4. 0+1jiM+Rx8l05P\aޘa h m TZ¨- 閐NI-Y%aG; EB}@f8J=̓I=)4$pɰmRu^̰dܡ h던iP=507 N? 7vM`.@NDPʕd!E4'AH,иӧWB3(@Β%*4- B(@Zh3*A:FaŚ)|=$P0eX98jϠrϢ9g?' g?L4Irt+D}̰4Qӧ4>UC M!^;xU*U]15jv>G`rxLQMAUj aB/[PF 4 ET5LR!eRlJG=ic![ 55+{QEBޓP ~}D{0|AEl_~!C5iz&:C0.AC$GŐK\p*HS[;xq$0B T*4 tuc-VT*OeHK,*$g!/d0LH)qT8E I"r2a*Eع" TvB|RMBx ~[ɚ+`m.Glc[*1p6N8 )0[B||l,̺DF-PUc9i0#9@ ,e"@5ַVTdGl Qw~h9;P](}V0a!Zd!A.DyؐLJTtiL[&SpʠN ]KXT:Q)bhI*S&fR8עE`I= *KZ@`m3; 0! 7TИ] 0ɂ^mi6)-عR&FT~QNQKAMRDh 6JʍnTӠSwDpI;Ӽq2d\챏bȴ.fΐc#[G 8a7dT(y2N|[#|pYx#4:3?5ik"fMpVs]l klJ@CR= "* >alr(SP;Ix:0 aG@HUPP8 I !0D"`?iT6T 5F=*RԦ*0  kMA#,`r`ŁWU` Uv2OGB\p\5^+X*V h"#2*p%* pr  a 4T()2W卜F,޿ )MjWֺ`dG-i8vmoY[7.E \ w"$,npdl 3@" Tz|߷\V _^\lˈ@ @2fI)};{w  K + _*X(NWϢ!j ;/]p/DHNK <:P TqA $ v‰L2ݥI@${y#u ! anUP1πt'N%DAhh? Ҙ+<@Vڕ3U L jհuBg(=5#n DhJ¸ZNg1wVGBjc/7z vwet )Bi\䉞XB$ p?@ H <ȷ ~sK8ߐo98pC~H97>\NUu MnUa@P^oO 2BȀP  c7.{~ !D W`t?x W7@?{;}0 Q \hұ? A>_j0 ^1a z)[:(UC ch rǽpKc9ծƃQ(. Qw c2Xp,1=c{ ;~ < p= g?$_V)С]zCae"Zt0׀{*`902`G~Gut9|fv}j4`qxw }hqtKGv||{}~w'(_'~PGu:epR :NCg0bv[S>W`}f}k7ouaǁj@t }J`'}v[gk~t{7Qxpvu@x~`'(X}h~%2] Ej] :pf`iZa~j)[l`G~v|{kXr0|ǃ`8󅇀7u~׈Gu7Xx vog{NXs 5~7!pe*@^Kdtdxp90x|Vcv}|w+(wofxrv8_~G~cpw WwIo g}ft a}0j2#2U6 hh0f @dnfWChxKp9`x~Pyi ~1L@*#; "_8@^=@oOw' G)I;PtL8Li r7S0O>3 0=805\v44hZ\EUZE0]UYfxٙ`ٙ^ɚ\iu<9.0E"Й9D)[8 rRpLVtPPP`$)W3NYXhtZ*U0 :QN9h9 )a)!44gSsBrUHsu 3+1UHPNADPPɥJ @&RNVXH >OT:METĥO`JaT!€5d u1c)L2PYNeKE|BzXA Y*}%ƩY:f*Tgzs wJNpJz0P P1at uH~ЪDJp%t?Y8pQgj:ÚCO cm[= 3lv`7* SA);|$G@@RIZ8T0@JX*# #z) 00) BCXFBCj$WkXE"@*#?%((; LBZ5T)73:_9X>kBk28bKðO$Q.06a JA0N0<0de0$`~/PC)p#5?rk`TA'0?"sN@,qA@'Cm-P h):= >7<#pncT T`?mDGU@N15h4?Le+p! P1&6 k+8}E0@}@.DC @86N?sB6`0 @a@+,ÌY,@=Cn=`zb RU +JOF?ڦ[. <9Q qUS/T} /L ΢"4x2  K,61GkN|T?Ȉ˳ LZ# r4L E +JQJ 0kp>C3ȅ0RF2Odz"0U@'PϬ``-\VúL\~ң9@oPF3 ػ aˬzKC#A)~¸ -9 ,@ࢹ#̐"4fl~"p& 1,X?;@zR=`$ce/ ? B32u+ Qm$[ j֗:+aּ@K0!}`XЍ=ٗa;~a* /s3*$9f 2!M7PaE  p. p>ȶL`HP ӽ5B&m+$C@5|RP=@iͺ0-H\'Ѫ,y-70%~1p:6k%;C^yxʛ;PKe0.$ B( p7 [5 ^ E .|#N/?PK78 , `} < "~BWaG}]n 1-Pn M  b@c:fa?4@(3->p@7ae9 @pB`9^}=6"U`䈌Э6aވ"BL2۽`S)T]/pRκTN4z.j ^L^*s~s@~fQV ._28=T4-8;PHR@4e1&r=%P$F0>$eQR oLX !00@,2&@4` |6 +S>K"rQ@0A"/q/H'_)_ =?ªI~{_JO秽@IT1ol̪5< <tm⏡q o8P0l$-aE$O?@eQ S~STT$!BGe/R%75R/6=G}}$ }bR&G}0}~ V!)[I  ϯ~#: #~ # }0C"$(FN@H"-CY"򬊞&5IF{@@R/6Y(1Z>5#ǒ&Qb` (c+A-!a #k>$@ xZAhz sjB@$ȝK7WVxM!jnĖ"8*X'Yʹ! 0(!=?}PI@93$ Hɚ`l҉ = a$ԧ$ m̓3 v"s e Pa\2At*>cF;5$ E@P߽ܞtG x_FW4TB[(A:tBV{@Ć MW#1#6" ǜD/;h 7#N[ϼ7|c p nC9dgO~C&N`kQj $o;}520LG 8 HApXTO07tV}@zWxw0XIh1~'B",p 0ED2 P%{!x8w Wf =ʇv:Ptz&W TsPP P|RxhT C䇂 F|bj؇~xD!2{wP.w`|xJ9` ~0|h@Hh؉ 0P`PK`9p~׊H0p~yxXhiO&g`}]P~ ag90ȎQwW`cPc ؉`W0~ppQ 0 yy(}WQx8iʨyQ0!y2ph2~vn*SR-px 0-HwKX2:ipWc~ 5yjpd~?Icىy``iX)2Gȗ| $y闁O  `&Fo:Q%-2| cp~Ў QIy~Iɗx @9$IphIɗy' 1 r1ixmxH a&{T+[VQ:`-9y{d:9iJYylY8d ꐄ8)  9:y '*tɊQ$)I|T!W,:P#X Kڤ0f9Mm hy'* !92cɥى ǗГxLږc ١YɧIPzB 1,`1 "*I`Yi3 |Ԉ'*zX9HثƘ9@/'yªi1 ^8agu%ƟZDn?ڙ-`.HzPoSoGpOr|w@ CD2[b/7pok)7oS ; p(7\pWOڲo{w,PV ;۳0)p0@ ӳ=60He|?-04@f`gcO'Ppe s#pr7seqG)0qr;sxv-p[wfc j6jeef[r|+uoqsqpty[qrV`;JPd1W6F OՐֆ[@Zx@:_JQb6m@ru6mۼu6ѻRh5۽ӆ׆[|E`#Э}G'gNHES+c*"#gVbte1bņZ V{*~H bjEl[$̢j Z\l`n <4<\Wv W)!! S# (d6}+|v%L&"Vmg#F0C T)cV'y\v]|{iKfg4yj|vs?SL_@I@!n()J/=/SwT|;Adi0eB ],)3$DJH$N=a`IP/֧1 BBL #v-6} 1 %9d# }Cn,/P3m- w oq¯@* +d\h[`0-"'apxӅ#:a0TT̕ER 8b pWTR[0U ^ ŜoPc4GFU_ #n\ BCEMk}A&@G*+cB5  I";U}ل| s!+H@n} 3KC dt-TLK;:1S;e`ֹ% {Ո)'(# 4$! ߘ]tVUI.Ynԕ ayLb-<=[_]3b5 7 #@+_ ě̴0cknmDV- 6P GLTk0]䚮z'}2 Q}$G[6] ߋ^,=,U:0'?J0  }ݣt1s!Qq]jgP<7.:NbN%I;'ʀ$pbP®ƎQ' o0IAUb`?[p$P[$`eA,_T N`0>#:`2 )=cre1~J h4T7&0A C@-B`bnnu{b-P1q Q M$8B>?l1&_P,%A> ՠD6zXؠ_:pn \MU2q$e`SEP Ten1< ٢6hZb T" €J^ %OFBh7<%[7e116 }S~ST0GB;1&R3R66}~ Vz#[#[E-0} )~L#_"e]I[E} }e[R&Ppq%$^:F&R%AU8tTЀbCÈbž5%E01'lY ) z8%R]W w_~6\%5Ja{]T &LU` |}'hGoMPRH41%Mt-AQQY՝ O]OR'~r! lI l% `:(:@`$ 0eSN oh T! op38 >J;ANrYp z T^D2I@`ـ(Ul2(L=h.-:<_@NC|J".9gx 8cM"M YE%\HCF6Ea, [?C1}pKI`@sDC ! C@ d S"o]y!8hHp7:}N CdF<$.b@ F *ޱWB:]sLDjѿD~E?_&\ٜ >`"X A:"|'1L"0-(`U!$VAZ7~]xckl >/z8))"&aR::Z@嶒M}2i )p4#xBC*|aTi&Pj(1uOMC0pD2t-dn _0:a2g$ZӇ&Lh\W<הh`c-ZD&AIqǜ׺ʖOѢ8_H-W@bl.h_mVJ1`OZur=A|!_UY҈23-,&^(p72 &CYպIї\u lcۈzY=[_T=&xJ0T`1M`%uAkd}K( / ~B:&a#2UJ1h E- lo@.A[Vȗ*Q_6TP X&{/f%AC!B&׳x*qsyf}g ^dq&p aW{Vvm>ZȞ W / HB >p@QE  7S0!tcXC.6|A(ae{UiUPeU'qkUXS-X (zׂe pqUw$1Fg#xiz#X@765XU;H.='>QF( l ^,Q6wU+ (Yh؈9]cRmX10( p\HctFZ҂yi6c_R?&(^:}p[( !"f3 h,؈#k+UUU6 `a0,%1*P)036!5xs0x8H]G9 xxy(Y(Ni Y ɏ9) 3-pwu*+*7 ` >@B9DYFyAg]̰"i}`mLQeH`b9dYB2O) fivBe ֓fz|ٗ}yB#g(d`@x闔YyWB@*1m **0=9g2W+}@X @[BPpuJٜih:IVP%@%7;#Y扙#_:*{! ٟHS~*q@ ߀:/C ZJ$:B 3[i Ph܈Xy,ڢ.: @Y"p:.:DڟrHE*@.p|6B p 'Z] P^ 'jKWBpp~`tjWWKWjz9 ^yp}zfzozJ :w*J2g#}`3E #^ j c`Щǚ 0 P Q~[K0کnj zZ[2@' Z `i*ZKpJWZQWP| ˩9PЭmJZ25)C :V ,@ Ιm* 6 + Qp?9`yZ?Z*봡J{yJ@kDKJ9`f)Q::@ANI-_^8вV`XZ< jڥ۬zQЩJU ;j c [cpu QQ0zqSA j+mZ r:i30`Y  XZ'*Y>KK륟02˨Lkؚ2+j;Ek90˰ W[j [#Wl "V"(BP yMc à pL BZjS;[z3LA{K MA+\ʭTB FG Us!:)𡜐yi"y=ȃl9Zz@, pɜ̨Ⱥɉ{ li Cye,κ )  PB!#R.,}:m Y<2 ϐ* }@zg4 w(;:L.p.I' .`|] 9 0ڻ:*8`=LHh$'Mв.`6 џ;; "  dt! |:P**02(1] iz_}hm ]@Cp#qq`U׀7@EP*6ڰ 6=~S0OcxAxf_i>9M؆Nzڋ v V - GG^ڭͅg؂@j4V۟ȅ!R N "1ȭf>vɐ: :ΘJPeˊY 4-X>%.X T ֌Jgg@B!&|Jh>.$ /^>.zc ~90WH)>vzݯ@i!:!@@Ny!x8{X%BՀf-<BDcd(G1kLc]ј\*G}GSfфzU>>s<5\{s}?h]AXRr2SEPH2/:8+;|Qk P0ggdX^F8ZtXcipfy& 1qS # DYÈKRT2a?TYGiamaTL. km99F;{m6$7 DGvHD|0200 `A"ey6DG)U뛄L`[XV.-D6^{'dJ%9CQ^ m wyC0mfrl>>APo&`0;ff$r`TTo V 3 wNKX0!hOpso?E( 3L;8@p+rVCb]~F/T0_o1uE Ow2)Ur6. _%ݑ}0B@[zbeů'ڲ:DR1=oP#^d8Vb:}P]JB0_:4e1G0}}0S~ VBV aI-YL*,)E} }$ToUFp'FD T}# ԔҽG U$ނ166/ պ$}N ڥ#Ap9YUjЧJ-4 z.]@ ؐnѢ^Ę= dي1% zXAa i*H 5\NEWa VDQxI3H1c4YKxUsV/؎&'u`B#Q]A W!@: З˓4 % 5P!@ A!ģQRd9ۑﰃ0u_RUӄ?\R4ۦ[&QlΎ< ҍ>4фB Ω G2p( NmyMs!*!A T匔ܝ$ Tp4@^$|fenZ6+ ThGv6*@c\.xu%91Ѷ0-ihsG0rEΘPH 4a7H8=7Bh<l"—0CpTMIڜX S0:ٴ- #h0;IXgsF-(|Q=N 40[9m@JVd$ XuBHb,Q$`H)b). 蕀VG SK _+d! 9VMKla>}+` Ё AƢU]d@`͠Nhi1Zt1@>H!T! *Ғ6GXNQ` [TB› WrdRi%b:1-6m:-B+ܢ#bC"f!`ugmsbz Bǚ<&ԚմI:*K,M5qD#c&h!tZGXD/ RE ؂=8A ; ŵ}*(zч:.$ @|%ic BFg;*侀 ĵoAvHwutA ?R 1$龉h 7&=z k%)nJxkC @Fl #P' !A?f4"ч@|@r0(l(`~[r2$CGH U7/8 Ft:h%n0 ;p:K;@ g` y2ac6U ǀ&R&AT SW1R!wGv!5aA4-A"d#u!$p/ c;DZS  mLxiWcfHc, 1rFt#F 0kצ[\zn7=F phFp( ؋n2倆BRr3$``=+O}=^ybNhzVqk$`NҊ@hC~0׌6:i Ѐrw 3Rk2OX֔ d8768"  `28T8Aw_fIE eyXvHOXPTXwTp:?  + st|ԥwTj@`Gi\0ԔxyȎT G[ٖysy1b!VU|1n pj}G7.Ee N`{e {'Sbϐ0yiIǛisP NzW89աwS֩sc~Pk_B Iπ,gw^cYX0`6=9vB. )CU@q7hBhS0jZ::I#S0EEUE5E `If, 7.PF)?B:#%z(#EE36 ~9.8>Jb*A t Pbjp[[p=,I8<'F9ZzPzO:@ڨ'wY[To X`&Q:Yڪ:   t$X8e07^*0 Zʺڬˆ^g7psQZz蚮#kog,&* I0}ͪ.]VC:g6"F:0*Հ7ܑ'[y L^P&G˱2;4[Ig[@/ /LP&e@*I5L۴zz7EH &qat8~d[fa*M6Q,@tz|۷~.-dZ w*0#~;˱ F:[+bH7U=Z9W'p{ W@k~ @ 'WP몽 KWp{~Pj99 າZ˺[W`ۨۺ[ۼ0WKQ& i=b9*Ӱc`cP!L' [ 0 Qнҫ{$ܪ L |Q@)cW'N,@P0,:8k2ѫ2`,|0¯ iE W.P 0&6pt;*P>,{ ?\ qˬC|3< P,K 9p \ɍ ~,\jP|ʓ\j,ŷ˺\Lz\ ;Pˌ:4L @”0~̯ðKz8PF/٦qK'm!PNPwӯtnamAƳ:lɷ0ƕQι<ϔl8\ġt|ṴW0m߫΋ 'Ռа+ܻϥ*Tn(;@ UepL ϴ: VԛϕìʕL~PDŬc:"Зl̗ܻ<L=oC k-E]-}G' Q8nX! TM g=Ԡ, S]{U-ϣ Ջ^\\2iQ@ٺ <ɻS|`؀ S-M`ʫٔ}E&8􃚞 !{< K9;Jܕޫ4\)߽ͨ-kô¹} нN Nܼ1<ݽmIM|L| [P'[eFY6~3zN=08&+8V>E7t1+b2jf0R^2Z\,qxE5tqCW n4-88LpLi@hY膾i< I*I:RnP^^Vn#mM8 L`@G@.4}>:YSEb4}@ӥޭATtsIiW3Jn#;z.Z~8^8r~z!^P> % +NL S@ejٙD y BB%DuS ɩPn9T?0)(wOIɛ*"O IP.|93DIu!p{7lP%8&@+&*Ft fAO:9sI]|XL0B2Q^ٓY@+BTr?5rs' `Dpw4O _0O]m 0`Oࠓ?O}G/t 7/^0&(ps##0&nt*C\ UڏTyF] @z ߙh'7 T  U $ $eTT}}S~ Ue0UUTUTU$U 0] ~S07$$Ge}0 V!7=*Z076+-L4A@GEaTІNI ˑ,%HltѤ'!C@u' hY$eauX&=,[4^OgVz|5ebRLTpş^MUG#s(P=p@ ܀!p`L Q7BC@`}U$.2eNp&rЍ[uRE0Glvx#$7P]xD2 |}8 ;7S$f8kmCo\1Gh M1b|&jP*NnJ[ă@ Zbp +X P`ep]T1I^/O@}I'(G TUxn_>zC&=:_ T@ƘmiXd'aWEa6")Yؠ>"i qCw>0NN<'=@Pq laAB0 e%Éz\WYr^껯(~[`wn>l S=ר@pD! Yݙ} 6İM[0%;Ud`1Ƅwt0U펧S NIԲI`0lmxF@ -\6̏CmF{ rz-~pdq ЁM( .:̠B%Ё uF *n/]$HwC]y-dHJ dC|H:H +YxFA1'FWדּ*(T|"-dSd6l#b$\@HX "lt5g BvbI"u %)th=O!p l IG@aAnn ] .'}UJYL1k$H}[ q&` N('OQ!+F<&C5r1 :){FRs[A )5#EE=­xmilGŨ~((CӒEӪCB @}5j`e .^>B:$<ƷMTzP)h ,쬁esYB*8c`<=)ay \8d^r1#$=*C|-L(y @zAu/Ae~% Tlf =@!0l5`0t^U*`b\5 P*Lq̗hש O, w*K0 j@[H#b|r"h4AfߞZ腓*)/x@OOm14BMEÑÅ/0abXHGʠzI_m EPf u@RٞnyĞ{..(;D$ן\aIG-B(O2 gVhJW[ pr8%TkFtٵpXx(I)Ξs+ |uDu7 D 1`A RЅ3308,VO-n+u #qv?c)ˑ4c7\`fH= <H?s'%Hq(B[x\ *s+xGfv@yO{B8BEN)-2R]^IP#u~n.n@3vK#YUd7)7|l[p4D33[C SX+;BBjmj7 1$D47`NJ)4G"_ׅv+=H7ӂ@=[E} $L`Nep5CH}MdV&`x 1b9j [{:z1Zā?v$2pk+[Qj!d31>q$v 4po@@x oik؆qh7pSr8z3e@%,PP-P 3SW!>:n0}kM1A( c!98U_׈wdZwsh79; x21LF P' v 0#j4*jrݰeэ Yw!{ @)PV-L$T^EfƑ 0b7!b#r} A)LbYC&F) 8r]@[㨘`2  G  (I$9DH "w=0T0dYɹ yXDGV 7!,\i"7 u_cEa/FҗuR$@+[PqE1Оeް& BS yy xsAII 00 @ Fձ@:I!v9P/bʚ [ΒB^P!: !aWCuCIJۆ@~BΠ a L0)='D!  qapd 6"$Y@d k@z&Ikʧ(̓yz)9p2A"p1JZuXp J90B7t F|_*?M\:ݶGCf N)Y_j&dBv. A## G61Zؠ}P+:v" /&6jWJ >/  "c Y"0A!4 ʡ J`p,;;0C@ˊ-L8e@>1* \ZM6 P ϱPxKl"`& e=`QΡ"QՐ U Z ZF0 G˱ g mkoK= `xk 7Ne6F;uM4ej9uFI8IB8E Dțʫ.E'ʫ z 2~ ʛ Y +ռ)I )KYv{2 h F+ + ` (j ` =84$lZS!.`E`&|(|@&fS%36\%.~@>/2, 0JLNO G, 4l `9;l~\A\|SF< hQ\J*-\e,ED8-i-e,/Tെ|%SƊȌȍȒ<Ȗ|ĐɊLɜ ,mJ/p/-@!`^:f` & B˺<\Ƽ7]7mQ=N6z`7xn *phPgqSC Ǽǜ !$yNv-i4i `/5y ˜T ]}XtLT |l -`y`)Y5 X , }<>8``c= ɀTeA8$G mRq ] d]f.PF gp6`|I[e@_4 hI~hgؐ<B@8G 0./T$ B=* [2 C  =qJ-ټ۾f0 H 6"b @6i|P,c./y0oB0<0ӉhPc޿Upt*m)@ i}$30 E6#LY $tPΞB@nEPӢERI0=n@^8/* PNӜL8=` @Ai0:[UL*KuM:ї=P$0w\6/"VB^fٮbV3ElowmJmٵ]nޡ{ -I@g.0JE6t n`nw-[i|0*IpMJ;`]踞ĜEn) [snSl[0U2-!S46[)1g`tUpkTi ߞ.nhip$p.,@؀GJ`gQgp^.".!OZȷa" ~w" iPZP kߎ[^?o $/qI $n ͰJn'3QgۂCF|9^)06@ސOP"qi ?~gpiOpOnDGtJJ@D@n@ dk"gydzy"~jӯڿ$-`.J@ ;}1p@:O\\,i$U U6/g/6)677/L-LXB)^,=:eenie igGiLnGnOnn˼OOʵ½дnDGDn/JlDJJeJDlϽjuDX2x4%+op3 n@|W]H(q#2H&p ?^2{B#4=m!" X!,@lIcJFglIȆD*IbĂ!/8!A!@1-LF_ ,` 9aӪrㅒ /ذyq84|#n`1inxI dOkƉǦT$޷W]:ə;_ŏ7K)GY0AA@Dxg!% Jh\S²'n Sz@EOjzʡPv MF;č(hԴviPx`,ya@XiA/ \Ht@M5Xxڹs-@zy PӁ=h(4r0y0)Dl ~ Y_d͂E)[?>% zhQ[\X̠p9jdgR7VKJ Hn td> E >Z6ٛՐTuA Hсli-( jJϗ,@BP!;ݘX|ܓ$k˝T U8R  gG†I+ t4lL7$Iy aּBI\Nl&`E\.̞Ɩ]/͍)cvqq$`bʰ~%= jBBU,78VqÈ%XN4p '@:)%!#DA.TronZWMOQO^^] &IIIookkQQLC<8!,L4557)/G4//l 4H/;/4B5B/5l7Bl_// )*555) ,/_M4*)45M7\)/4 \)ʄ?5_5 6 dBXڶI,E 4,R֬Al٠02NiË\&blD֎Fd9BfB}qyE&)2yt-t)\Gr)AȂ#.M T $\j 5(jƎzhPg;j("F`r 0AHqFw]=:pu/"l+#Ϫq`QrO Q}1 !а 660baz H0@]4 AIݽ ШusG% G1Q0z *1*$ 4pA[%\r !MR-$D..0 #.b1"c7Hc cF(J(cJ 1a< h%Z"[|饗W| e[Iq)fyc'igW82ڸEVxƉ $&lPf[t fʀ.* d5BKXk ႑0A4-ka&35ĬbYc/Fp$.,b/05ZJܶ8 Zxݺ-j K۫c饠e;)j k>k+6@K.<(d{$ B=mZG0LB*tl*am.DP;]|$6@buZ6xѳ+gC@ g/*p 6Ԛ Ka0+ DmRb@!kk =g͵>( Dφ>0+}-_ r$k 6W -3mc5d{Ί5 B|xW ~$+|mK@_/ *DKBib̯6MB5PM|Җ}/$ .%h|'% GPA,Ok^٫V:DYnsu'1ZphnVxT8\16gn\ۅe淖uJ_"0u)tlX>,Z2! #axs @#gU+f$p Yٴ@TOڳխ;8NZv9/8j,%HO) #x$8`$`4pw`gh$؁lH䵈sr0i_@Ǵ@?`dGAf0q 8I!!Y{lAn/!~oOTTN?Q&@ p 3.|cIil.x9:1E6 F/rp \ME 6LL`Li"B0eʂ& $Pij(Hp@7'*BZyV#+pv3[鄺lP_@QL,էO D =S-r# 2h;EIGm` X\Рȷy+ `*"k HUͦ#<-` %ܠVf  P$lp%< U ';BH00,,*I\a/e (a"Pl7@D ҙ7XAz|-Z\]Dx̑] ^`Wpb ݝ'I@JcV"p'g57 s9t-m\@ٔpVf`ߴAVpV ӫW~&Fa:H,YS-r/ A |`æ/ize;;`AnbCN<Sv\a^3W0#|  p/8ڬ S Nk/lx4[| AXo8Uxs@ PL#Mka7_%v`)pXGAR,0_0[U\g_PpA)NpT L6@p @f MTsvepqwPS[Nf6ubH ohXhWUA XVЌXZb @nd\ d'~gwbM`J! ;HNd$[p41&fFG0dbsgVespv&TfkQ"jvx&S9hgXaIG[e7kxL?@yK8ƌ$.  {;U>LZgD0Z,0hb7 mFwh pL@n{g^ajDb)}EMɂzey` z^qqGL=g6cNj bZ^L"U)_`YՉ`zM@+ ){B@`H 8"iVig錿H8Gvo=Ewf @ %TOGX$}VTU)@7 Ȅ(9V_ &YYc~2hNFL L`Do gu@8>ՠuZG,0V{"`bkNvRh4_Pafe^UXPX`-d^m$0pT#HX9jR}f{G`;P6k1c7`2/6&yd77pFmX6,g'xiЎR@p;an?w[^3O 7 aFed~hX_t>+z drԖ!g?kqELVXgXD&Y;?p>X{yQGg{%PuT. b@Иh ΘA`TFu6ia@Za]UѸTC_0JrX@} ' %?S`琻Ol6ZQp.d oԈ>Ug=_Q CjX6wXe\fYU6D%o[y#bY`UM+LyjUoXyoǂ~*8 ~P5_ʆywDw͆af=U`FfXqMDay~_Py6_5a`XD஖tֈYaaNƐK`k{Y@58Jf4GӒ/bb0sfr_uuwQYN%SeT rQ_GVo5[KV=):2_34g=d|d-ZNZcHOEs'U }CFr :S6$9@2+֔#%O$4$OB+-"%V7&l%Vc|,7JV+5SPD^;4GHYHBG:O`cDF3`P)_\)W V['\\)kLm\ca|Ǖbhlwd|v 4eg][E)LDž(y,Ǐ}Zŀ ǒ\)gņ,Ƙnj\x|{Lʖrʗ˚kmɠ,gȽX~ (gOM <$4)aCDpY8CڬR?h,h|a?\|=]]?ThbTX=,fA ) =$]&}(*+ XR1V\@@"*PaS U&HJLN -h8p+"oe4)\$TVtVX)\hjl 9\pÆa2u la/pm؂=؄] p|0aGp 2@`_Vٜٞ?QtQi 0&* q.k)]۶}۸}qlӑMBTVE8ϹlXpRdը-N#*EK$@9h=], 6gr*+$ `ߦ4ҭʖ /nD@=N^)PGm=fP֐v*Nf^!"\!@B>D^F~HJLNPR>T^@Nvj wh`/#:W<|: ! q?djY@0Y Y R!q!, vNp?@tN燎w!p.jN@畮닮2N>.p~~{걮꬞r.bY@.@d뼮d뻞n韎yHT 2ndg} \`)/,(/(b.5?^z^s밮sOy.@Rծ.yz>Rp@v*(..0o7+%{0uon~Roo0@n?Eqny!0 !,:h%;uC^ *|G?*,o_3(ON_g?W_n?z.į`^r#~zҮBuOqvv#dvjdbbbvbdbbj!bj@#| X7,$ 7 D*.V}}a.K ))\_a*D|*?jjqr T-GȄH8達IT5+'"O/HѨ̃ K1#H* P BŴODdΝ%#@{@[ GHA 6m5&X$&Q &b#HL˘3k̹ϠCMi2~I 1; 0eɊ$LHPD*7ȧKNO&4B;H#B w† R|z(kGG $$pe.D` 2 )4;L0(g 2 6 6,@6Dn)x qqC _@\$b΄C-Ψ\vYv#ܘDM7`^&yp\L '^_Xz)蠄zvwmE "f5pB{p lJ>Nh詨vykf@m*إeC`pA ar7X@6 DH6`[_A %B#4p' H,+d(<Ƒp 06`{I.@\zE}0+ħiBNAAy@}.S,Zn na7%ra*L /,3(B@mCKr!Ma&;̭(t @`k dz#@BGq@ Il朋f2B އ \#4(Yr <`;f`=q  .L ?p OԿw=?uh[s@ 9i^C ;Jا+vVzkH` < a3Å#JMǿL ޚѳq[B@@QZm?b 2pE)J qh>Z`X[l&%Fap8 z:: \d|D`84XB`Ix p أfJJ,x"`3X x  5a Y+. XALeZ+b38 `!}Sиp$`XȲA4\[ pp{ґ -[Q4NCqxy$VS\jP: 0ArHP;E/,xJV$p&c A&p䔱BW:dAiͤNsHs_5PQnpSQ xO,PHKmmpu,9,J \{83 ӵt@ZWPN4C(>oe9F=I, ` W%[U9VI42OAR 8'AE4(6cr vBb>['OB'?/@1M;YAU< š_izE#JuB>@ l|/ ,(`) 1#@4X()m\.:XO" ylM<biPpS *iftpS7J"#:&3?poֺ;weŗ )R,`zq$\DžNu"`,@}TROheIچζ)6cfjW08-/yi8KS ;!.T=p 9<>iO|qސ*Y2ZBRe0n>hvtK Łlˬ2rΥJ/L> bh~ Tpa0e@yZ纄Mvv).T/;Xq<㵿nDF>!W;rGuoGѷ#{=&Z2lp4X 2P@4Η,{t~o>w/ I[cޠv`* ~G ~E;7&L3Efgw slK @")rc!z!7-J$PG!0eyaS.+y7HRUWe/qJ0EE2*0 }KMAWj{R]^0:+@lQJjRD OvfWghWUo p:d6d@`r[|}3;[K[tEE]${fpiۺ1$o7)Q@ h@$ ;[9f6m2|@_@, |#śڻ}D@2&LPdýKi{~PKЂyh[|&;@mKe t!v1$ KL !0N9t tH5J"a<4\Q5"$@ħ5( "2\F| 'DT PN0i^b&f0FHZܴ)7,&R=s@ "GlȐ[n]Llޤ> boȂ1nM[!. 1 p7Ș-^# sK t1@rfLGNʰ˲<˴\˶|˸˺˼˾1g "7`k] FJB/p؜ڼ<\|΅,,r"Sv+0х'YP4=]} = ѯ{r_ͫ'0gYr *Ys\@f*>ү^E^ͫR,t:aP6=Rɯ!p'٦ xX@PDơ"(=hȯՂ>h"!6<*jb`4fiZv`}{)DR^7KpQא]k=EDr!Kvg٤!cQU]s+ڢ|!vz)$@Gإ (5BkV46` !r8FsۣkIo1Hf! \`(~܁VqPNIl-r7P=R⍲ P^((A@~=@}Nvhe=u]dzy@A/a̭+" S@"}<UqQ]df!SWt@Qyݲ :S|P5rema  |bKS0=o_^>09U]s4m|P @$0(T0A @vPbKrS SeδtS|"|@9crSnLk]jnz =:Rj2Md S,Re s~ak|n/^ ^g޴v_nS`tKKO|`Ƞ.0>&l]3@S]|26,]η^SZL j{!&;z(Ӱ&4Nfre ~rKb~~M*>—6O0% .)Y߷Q=gtrbaK.vp=;)2~#!' vPLF-ksHϷ^Sd;X/O % 2"}$~8٫(k%o ٷ!/Pq.}rp` p/X1eP߲ѡ||YYjYbbjj X \7 [0} Z.  $,\h?KG} ?VV0i??5WVaXyGy F"G[7,С`|@Q#TD L9^Lp"; ,]Y1 #4}$ XݳӨ)8@B,"| IC aos ۧ{J‌KNE *U+UxC_\&O)Jh/`vH\%[1B+(č b&D NܢWJsC'}@֗%pJ?\:+gS Y]1@~_{gYkXC |4SO>9$wHSz`G}D2@\Á\w p 1Ì @( Oru|/pG{G 9C@dq pXsz<6cf}8PpWgz &q_ ;@hWzIL|~\myYg:sbPB g脾z$x.G4Bu7p.,N8 |X `"A _= 1 Xjgk\3z l`E b `~u_¢ [/0@4pq`"Yl -[~8qs3i6чa@Fc yEy5'xF2@%TM'._0 |,m4@7&A3/P/|1"FmcyM!bc k|`\tpOiy@݊n:뮻|$ay1;XՉtBևqEh;=8}4C @~!HxdE4@`e ؒ`? D\` Ї-m摶zT 14c}(/G@ i>G%hɂT͕zŦ-!ܴ͠ad 0X[&=W ^|> > |X#A܀ lw` -+XAs@ \`A9`@88f*]PP_96E_;xK8Lj/ ^>|Q` @!PC%o:M@3yɠFNZr[@Fz3h<1!D}xAڠjJӒռf6L+ ' !VȀ0=@~0YE) Ԑ6S0=6v%Dw1nI* 5 ?l~$S{:vԨ62~,)ճZsu" X͋j^A"B O@[ Y1UiR*KRWG#d 822 KBhK,PK X;($PJ30\!e= * AʿDBbkR1z+EV0Q=a9V כ[8r,ȼV+0ޑzv55#f 8 8 0XV #D yԃaPh*jV @2FӊWX K(5U@(f0%]O 4GYK2lϠqxx s `fFs b0<'HWw* :'M Yf r~ryzX30U tbFFH@@ ;8 w) x6@>@\HfU"Ad!dRf@<@M ߰";? Yf1Arvl 2*Իʡv@Lm Tkr@ Jв.B Ĺ* /B}w=`y:b@PA6zo$la!jq\\X n0ƈk;@zSYr:@fU^ ^,#vՠ[mq>6wn _@9] ͷZb~" h = `(}iƦ|0$3TLͻ&*PXwGXLɔ<.`AKKXdLXWy8`R%K*{Lkx Xb0<`uy`D`@$$&o5w3/s0`uwwdRwmzF`:Q4DHms(R7p phR] J,0\&*;ح޶Th}lw!c-aYQ85Z95Ba)U6qmU#_+E4 m"_뵥@#"ݙ]Ad4tD74Lh"~@qNz0d\UBB3|tj,H˼6L,4Ѽ#7V`5Q\|jQԪ)yw|ĈJO#"E ^  #Ѓ"Eh?"r2'_|4x<Q2/T n @EY$ALr;=Cl=5,`ozMP17X29L/=m?۳B6B.vR\ t_7D:U$/&545-d9:ӞP~"(KBC>2'[(M5T6m-?eF1mC99FbPMUt6Wiډ<39vP*M41Q8p^5uL$ )‘@NK j)+QHk+1),Hh,q;K>0!KPuw|8|p1~Y`?0kn澋Uk*`Yug+KTXq߫kLAϡJpA\A]0Y >p@. کP~1Ѯ쎠> HB R: | @Az ?[ #@_P աAyN o(PS}X.` A? 68:H r+%*qb^ K@*t݁R4QVXZ\^`b?d_fhSEG@Q 6~?_?_/H,DiHRPP.E &}*?_à +CF`ȟʿX @V, J ?_O ^`kOv *`AYb p~ XY, _^G K[}}X.+h G$ $? G*} Ô * G\K Z..M,6G7~v G .Ϥ^|`/ 2xX"\.up#BA>HHŋ q\H,|+0dCCa[i b"~pԧ } Cщ-Zc^ :th0U- "S)S(m ڛ9wgavBcWPܹNȁ72q!^XaH O*,x ƌiP-RH;,-"> B:h=PPl GWEcrTC_?Z5kOqxvANq.ĕ.e!A.4Uu B~Fn M!Up"1mTT Z o]ԁpSW΄CL5=G]ea S -<^T>(^8G#6wL $` p3A, x 4fޏHU"on)PC"RKMCtPF@{oA[hXnCP/f9 )" \pA( GB , A  ITUցey!N6C r}ENU=Tj;]H] /,URZ G6Usj .h%\p@@* x@VcZ,!%R!\(0$Fh~ܸj͂'2(h̺9-4]akF @pKpV0fBAS"(P}/~]b @ xm8>VPL`^¾GP%/_hXB @22*@hY8+0oBJ`>(7>"|*I ;FnHFZ0!.S-2a83Jrm `7h d +2HK+0*IJ2Gq%_^J0`ಖlfQXj/LlґWly%o^/Dp \D_K >5@POR6 RD2W0qAX5]zӦ ѷZfzt4"*(U˛VFfM酀" ӭnE6: hUe>թӪ4jVqz xH@C|kQfFୄA#$` &,! J)1quE@ ְ 0F ⩈*;(ⲛdgM:4@19p`c4:▌r _eQ\!=M;U(66M hKƲ|Wև Rpa{ Q-;: S#h1P@bJD,>iTLeOKEn(J@( R[0d 8r0 A4l% + P }@ حr1jY2a>if7_C},>Ԡ@P*7m^zι*6XMAj7P*>1 T78 ma + c5 Q:_K `)P6m_?ЭNC b[6,ؒ&O9 (<8`du){M2~*)(`VPz_%ǹus >67HQ[#@ B Nep e\b[5 0n3@ֹ[1%(^0pʶ0lG wMkp6['TLLӽ>>&p,F^% {٫ ' /(xA k \Sh 97̚ڸ@d DB'l  }:L&E{KX!T2ue_n,4Xm,b}~xg~fzpsU @dˆ]fqop 5 K`rM;V0`n|c6aUFuD Wq(7fp~7~o `d7 `tP&tc+0GwZ~ LW ! \}:H=h~ 8`/@HjcWupF iSvp0M`p<؅_8>87sc\@;XBxNXqXb W`H~d(2ȇOdVׅWH E( >gȧkk+ ZLDlEtUf;cȌ/~k:x~/`EL,牆(鷀b2`p茢HHHCha8d(NWR }\ 0 b5|r;~8~8X${t,P-X%k@, LpWMf7i@ 00pHvf\PbfVvVOwȃsL0; 0vTb0Af'@t9x~ppjS(]4zs`P'fTp#@3@4  mx1::`X4(pHIbppL@@ 9@jF)yCIx IxY?bk`15V?L0y@ \`&/ٗU~2Dɔ: ;Rp'&9j~}Paճ{F:iLxp8[u  X_S!zIu}'T}):zW5yM@jYw_z v%G*CF6GUP5X e0 [H' U1/&P[%%(\f&p*hHy7(`doIRJUkxE >TD#1`\Z۽`g <{`810Ks0jPTMFc@S ~лP;Dq7QD7 0! N"Pp[}!Lȃ;G<&pcfwlYbi[Hxz}:Ȕ:@})}0@I _0fPUjDj{paLh,Kd[zuř#~NI:(:|p0h5yY.LH9L01e}A,ft|*01!ZLlj \ҫLЬgJ1bB}PPkKPA3LH9G@@07վnR\Mv:0<@jkˊ%~:0k3pj}!;~LhGW jj)U\@ܷlD 07``db$ |P%$Ȕp`ث'iyhHLlzg p' I6\ᧃy'/0z9*ЀpT*:'P\v'pݷyKW ~i-AxDY;Ԅ~~~v0i,]@0}bɁ  6޳ U|q(GG *`mvgM`X=ŶUn KکRBnse2{ỲgfzXGLLܢi[ *ǎ=w.NIC1qکM Y *R4#p|f|GuRfHVq$\Xtv.ƭHz[}ƃ'fLe/\3jg}?~]rGeuw @쁮@GId6cvg d}>e;c~5*bh #vptZ5&VR ZuɆKn`ҎTXkFu Mj^~@VNj:HVI&L%; U:OgX}ZHjiR)"W.UgXyxe٪:hWĤ $|  pa 5W%kqVu¯~+}Lb) Q?} [?=Q j*cTn_oUm-ϖ)u? n>e6\8U وaf?x{p UW W]z}5[}ݏ$iiUL1hiF|5grNא}X']s%*Pnd\x .0 p U L@ $@HP #TNGۑhcV\q5UbCPBtֹ FhM+!JiZ{v& fiH ,褊9q \ xpD+H ;E (ɤN@+kmUE@ęLHpjY+*mV+BBKQBނ.D1it: ɳ믽emP믯⺀Q6my*0j .D!@ $aذp$l2Rk,0_)t7 OD@-4j%, Lc)) 8և_d|\x$|*ҐA ȶ t$ @:ғ~ȤH?<)ȶ$)JSvha]lx ~ d5AXM>PdZ`)e41EBkA Sv"()2my: >& BiM`=hgpt-=B6!f66BhA>|VSLX@*ra)eM,&HDHXp/љpÁ Gl~6ꔧ'wx"Ҕ?}DQDAL~P?CSJML_A6P"X+FG jYs $&]2 U 5Hx!8?' m l%9{v~NJGEj9u_PPfS*~:ͺNChKVHU=jƳ *G j҉)G("Бjm"@A Z3ڄ@ 1F7u-0t Ҝ:2T-P{ F6ii*KǓ]DH ѡ,wENt,ˆb_HϾqsk 9NBT2UW)gW}0$P=֌(A=S_Ax*CTm,<:m+֬P?E4`s#X)Drðe"iH[y&Ԛes77S t=) )QP ּ~EsF}s ;-sWTIP S'z62 ́ X\`A h=@23`<^#sxq5Иpt}An^x{w,e;xtSAGp580ts,6HH@;Pǐ+gtr/>yt#AX.,6D \zVj4Ov@lX <r2<9eK; 2fV6"^'` $8yʜS,=Iuh}U#һ c aoX-4d>YtO( PBJBÐ^Jp(4:%Q V0ؓe,G9)Ju=TGi@@EL@z? FO3 az܏4Mh)DʗG +0"8(WA&&7c&a" Upu3rc~H&&MQ 3 X&H%x4&@E#"$hv'da!Gp)p.L71,*)tSxDS`&F*Raj"g/`%+W3~jR Q rU@@pMrg!B&:0}00s5` /{j!Hs׃!X /@8αt 6рC±01&aeW"owCSS \@p+ `$,RG='`p!0 ڸrPb0 `3 12gs P&Їhq+>a `@ G P!Wbp  Va@ `)db$2w@20p @@n~@5H!ݘʁyyA7$)X3D7 :Gi2WM @ `ɐr a 0pP 0p1Y| h!*P )m 'ip,Xu . }PRp imȠhe4@~I 9) ْ {:= ^m>l$`:D": T~["/P! s ڠP 1g  ☝/$"x pQ d`b} hٚyi(s2 jV  J&R⊑ ka0 u9`uY0@ :,Z h0$D`9mA1c G*)mq,h 0 z!s9 H 6G9:0  sn0I`k$'iɷ Qk0@jZנ9 jgi@&J)X9( !Eg.P$B*Dȅ[%Poi՚)ٛ9 a `m C!(!|¨}p1گa)+*zدq i' :گ'x5 F cQwX # @RWajPlY`PF[ F;8jF[yT[s"{l) ǻ r0;k ݨfkPl&@y@sy` :a  ~;sēl2'h@𒗼 &l {ъIhy|v1X ^ ĺI )@CkA$ 3*;bbxL&Еk0 [=m\ 3@ @4 ~ &:Ρ9T 0δ`\Pl'Q&''0-\!]BAͽkWm"QFK@9~%=D1]!b2@Pvh'` ' ҫ*1ܼ@:0L0P<:4Pzw M `&FU{ή9ؙ;'g龌} *j4ߊʨܜ~疠v?=#GFvZ.$\»y';ݽKB T`y)ػkd Rb:@hY0Nμ0ܟP&[1ӂLv O Gؐ2Xա1bЁ|}"Y^=Ђp@7QR(5=5 DDLp`0!CP  `N<BNLp M*a 3`s* 0+m'aׁݮU pv(0 { `Y{Ac*龌~sS# 2X@}?5-M`G&2%@lJTTn˼+ Jݽ`Tۍ븢`i{z٢01P P -]+asLxz*z]:J@ Qݡ p Mk{?}UN ߣ/A9)0ȒLD)D"PP=;v =KE.'/Ky+ m(*P8{}WT 3g` A˨ θ^yA }^oۦw>'4Pwcʧ` Pzm0_ )# XFA<$} ~h}VV2\%//Tv;:'\L/}%Q}%0є 3y۷uV} Q%Vk <ۺQi5gĒJlP$z'O90;j9(@6zQG9X. ?p>,Ђ\!q^:V ."BW+NX.- VV՜* 8D%^4cP ! $n@A_JVڌ$RJ' ;. !a K`^snkߍ?>$|ߵc0erH .,T'CmȎ ~ *d p)$XRA4 teX^4%FP#N.}6:`\1oh2"%<~ \F `X1"+] \h81bDG4~p#%9an-@H`C,fo. `D HCJ d~dgu*0@0)@QF,|RwE!  &piB!}Id6aZ %RV S\$[bV\1$(ZAtE0X1Cʩ%R/:u}@hl4&邵">/t(۔ @V{Z&(AB pq@ `P0p1  $ @|SWqEWQ Q %@A @5ƒ2(e- ap~(G+v'YPHv<06xkX @ U`/DU,f pͦbTN'&4zl=YouM}Hv# . ˁPDU@$$(}0ƎzJ2444Vk$YY3p2_ռia)+ҠZ# d*qG||Иxkvz/820 @SC5J BWj҆UmH=JeOpXk"@0,D. X.P@l,AXTh}IyaRCF& c5Ad >K Y 3zo(Fl d "m   7&mCd6`>,aW@pEq/dظ i1g\8pQM <@,A2ap A(ѳ \8Z).š+%/,*m)bR[!<<|,>3WƓy* e*) fr`T'\r&ysTS p, $P` < pA0JUH@TP\UYE3jэ^ F? ҏ Q𥇪LcJӚ`2 HPtƋ~pIJTF`WҖ48)Mo:Uz\f1Tzը!,)8 ҀT#4a͒׾ `K6Y5:M1k"V˄f7Wj,FL$8XpՠX Q.D@9 pK%m@8,z8@\1P0L@G8 nP#< xK7jX" 8: EfDЃ)DAP8A|P!\"PBz0?L!9dނrw` 1NU/La8Ӈϼ ~h[۸APv|90IiApP?`UJK4A6wn|K/zѾsL*1v|S6~S!|C4a \_z zN ?Q@Zֳ..lxs3^:>޼_A7tՖ{( \o[u7F\|v`z6|egyX|0Wwťu(n0WvUYp8t8GVP]Kc'r@|~)wW^||zW(P}fr{P/{Nm6׀FGerc0(vhCW4'wz(ez}2\k~VyFl@(b7PffQ|(67nk!wEyׂYz{&X\x÷soghuu96w{txy/yd\E6}Eplwy1Y` Oq!5=?ZXl8SrG9uf{鸄^FuMutxfh\c`J(}uu̗oxz-p|`h(\%7}]sG#ha$/)fF!IqCxa9(`awq8=amWlFZHIqYa` @f;lF6Rg V&G0cm/rxI6p [U@P:bu},6PQS8X|0~՘ZRc,U@$.RqE0Io@iW9Xv)Xv)ZYWviMI9 4L&&\B6OPpYIrk@NDMDN`[^DIUO깞RXh`Ğ*uYQٟ¤U^LYdhKʵi 8Z@L)p2 uM1)eUPqdIEO|YM|O@M([{ƴ/JKd@"WD"$3jK7V)ThAZ4:L4R9* 0= 1NBjCZREdTģ/!7q`K@-f%#c"TK1)pF:|ɗ):*Y:HQIڗ씢ϲs~$Q`KEڗ,DOYFG~DFeZOD)*OʩZM$*t6?kq 00]GXY0`vDt !˺ĤSN@%@%㪌@Մ !VJ&`% q0`)r2zS$)C2ᒞ6@*]DOV ' Ҵʟ0B2%0)1J&KM:/'б󩈠&c'ma9R!aF# 6B:nQt*P*5$04aRG9$3"&(!L~9`db@"]Ar! )P"j@F." F?w =6`M? , r븤 7i*G@,A QqVCz#)``D)&#C*ѳ~+(Q !#S ? b5$ }+2PB5# ` 0![%ٓ (c",.0{Hq 5l -s afF)-V;_!|%{_$CE{+_X+QF/(< *,QB}P/%Z a P1ǹC Rǖ _)D.T4X#)kD@1 u*ǩ$, $0$ã2wBl2@Ir9b25++j#;0m|ɽ<0ZYe0W*]*^JB0 "h,jV`] Lͭ0 6~LW0-.<)p1 ;?2 ܤFq)P1‰¶r ,pÓ"'ZiR! '@  ,Aܤ?k2`$$@4A ;/`<]a͵;*,77PA-B";\0 > B\;+<P8,Ι:m0 5ބG -\``7dKaĶY5^T﻾yM?a@`e+ r  Ԉ;mI6%}ᯐ^ d$$9½3b5ٵ + [ :p Lp駡Ơ9|å',QAdBAXPݬa;l3*==vd ;<@,r.N= PP$a`T n.`3(F036+} 6 4 Ln 1o=ڛ~^>A kө@:5a ^| @#d#Y3  * m5T y:`.4;ݔ@͢Ƴ^OT@|&Pg?]AZ-"?+^  Pm!:-.ƫq/ك5_>j87ziHJ)fĐZMC3"Р] 7U"y1v N s@'9!l!WP;⦅L /.~%$r5@&P0, {ohq!}//bT'4;F}0}~~ X#)\G  }}~$7 $~ $} }0!%&sی }VQUWzvត;AfpCKHL|HW4+ rb PJUШ'E%r:nU@x 0Ia^~p D @$[T+V)h$,X-Ѷ=X #[ҟ'J$qA N@/!ΉDf$g:v`ˆ<}HN>7ͼ]8gFԹN~׊o'_GZu. &aRjpE$J}A@`d0@tX%s, xYQS2#Pƒ>H@E8B]l9AE ! ,OaP(2)`a]pEW@8H"g(IT0o-vEqw!%cgȄW'%^W@ GBhiU|eF 1A p^4R9a+ez;:$P Ȑ'8/ ΘA. _jE+l?MO ȴ*`\+Կ`. _%("Ӥ(F R EE4sZ[[@MGx'.EOG  ð@Pzηw1yGD lRB%$8 "cs] (Hy?Nxq<٩7D@ SF*0?yfxhhg#7@>P~(_gŷz7O,`'e|؇6xN!?@5 0`qGP~-PcHC-p艜hy8 -@-P@E c9`~H}NJH 8XNjhEX0wNWp}^O~`89eS=>P> ؎ȉ=@ eSP~809>0(=АC"Iw0(= XC- 銚e0h= ((- /  @`w5mH(G jW./aGMxI] 8|8P e_ xy ~@0<(cЖ"P(`mɖxYy%  g~0a E #ymSh AqN  w_tgn7pyPwq"y{9>Yy9S0w YEЖ؛)}W( i9yYy89yHIuqzq4iD+\XpPv7@1ז(>SPwؒI!ɉIɖ? :w%9  E09yy1 ( )YlܘjD}  ,p7@@!He ]J `ڊbfJt p)& ",*m+ڝ /9jJl:xjp*X!ڒڨy@@ /,pn/ |P) 虙=W69ՈJYyvcpXe8))~Ы~@~׬y͚8ƺyښicXG 4xڗ ]8_Gu5 ``[wNV胯R0rG(Gootsw kr.Qp\U`r{oQw&6U0<[oolt/U0oƴB{rR[`Dv.LZgҵ^.Z70쀰BL6Q.@2@7 $Uc=gBF6p6Gwrc7qw' pgs[/[t6pYWb=%F湓&;H_w{g7pi K`p閠u"}}W9VjVi*lb9hs@;# yM!#A j}TM@ a z-2U e.-QlZ7[4@}0 vKW ~ Lv R@x}[KC@R P@U9S=34nýmU#r+GH? }`:PEBlKR©ީv3Be.#ZbmBmm)&A:"a^ 4PRq /p?0oN~4#! ],2|`*^o>n"c$FJru6$0 $ +0` `y΋FCց獠SYX^N.2?AYJVj U.^$rc^# }fP % @؝ k p` "3 Woxx@ 9n, }L x QQ.aHielkc~jE `(Eb.~y]^H(bp8Cb! 9Ɓ^O Q/`_$-1>K,@w *E!5'pԁqa T d_*^m@]. 1  'r[Z_,54 '=PHV_a/`7m |?B* 21/@0nQDT$`:P/W}}0U~Q0 !bT'//} $}/W5:&\54 UV0F@vʻ'T1T/4 Xz$\$\D.0HgA ?0,!& ^xȇ> fŋVAUPI( YqEl'N|4P kPi/!rE)T }ACW.aɠ@N20.@K0.!Mj v!ցZN5bdٻ$,R`k&`XBEDY@OŋIH0ƚR +IRce eR*X aV i @($S^HsF5}&NV7|ݏ%\:O#f)Ӟrq] 1@;`)KU W7 JlX J2!Y=q,G 6`pY) +$\pDpb&RG :wnQB ` n`:ZG̙_4Fe(3pfp~&fcLI_/PT,N/@Udh_bT&)\ Hq rB.f@,pDK ,c`pl qa4K%PeR:VVХ_JDTa8)+**+^(TR *T9ro臮n PB.X2Xʈ#n 7@REL ŰS4|#&,@@ԐjP$D ٤n|Ak';qX r܇_6"}\ACGܱ'"͠v\ԙnXQ-[:k _1\<%ؘ2QQ\-|! &fRcT0;Y \p H.Dt*$p5P{#D@y8ߖą&qJlp`8~rawd0~PQ|8 powİ2ۗ.<qB$&M4qXHu`P@حSpv0"ƌ,vG @ղF$,ģ־xYJR b0&x9l9 Ί6 +d~!?G@ 5C|P"'EFT,Wrft#q!/NC@/ȝvRbԱ":Dfp%G@pC`XPRr=ek %#Dd0D2-L vDIRtqx#@s,-b" (}8Ugu* `/Υ*mt8*`C[2o(x$"z%kFґF0aIE1!@ `@= ˣN% Tʅ0hv;S?'z@O[af1L&UF@t$P% _AF@m;,3 ) 0ZШ9 c3# @SS1 mcl8/E)hX 3dKYS ,`"/&hWP]Qe#šK Ii,w_O䷷=wu|aD@ZWl  @#4?0gQ<ai/Oн{Jʆ>Ii-4@ 80~TD#ڹm A2ńf֋r}*ό! e&pq AXl&`YH9S%}-]ѣ\. Jf@E*XBܙ0X̀`@`qQE[`80uMG -\lTy )Wx67ۍYlZj @N}r;E$ 6#1r3^Zp';N"LW?TӤ !^HC6CN6(S. =3uG~`pWg+SoKiܝد Nq9HUa[ض̱IxyDdY_,X ;3;r>!O_"@&88 Stt!ٙ2^ 8gTq`шV+.sЄ\b}z-bN ! տQ";>?  tE DE2-Ƌ:G>c?W &*>[x)t@ќLVXd1ʁ}[VtHmP@4~V1ABiGZ } 0U`|D@tj% P-Nq' _s%@{R !n}vAxB(A(WqCpKx K|`A_`9gMQp{b"ngtDhArTH(bKsmS :>0P:Sq9> E 8@SP 0>S "!" @e#['~0˱ ;uE09@=p ?-멙#$Z'D.y։ 4'w$9S"ڴ"p0,8Cв ;VjzSP-[e<~F8+?`K ; ۴*;ኵ70si^7`,pXHEp"PS ڼ;К"@{M - !(˹K> å=S {黹;B˽P+S" ;ڴPۿ+2P=P~⫨k۫/۲+lM*kl@P> C[s M<+;$Y]L@$#BD4(yÇk--|rLJj7+cP e G'\ÁK> KCJȚk B38۴ 1r0<M)Pi G1#p8Eƌ̷E˪-Pp9CGL:ܫJV\`ڜM F|ڶKT818*RB 4) YnY= WH݄yrioYjȈ|ZަbWbw um띇H\lk>km_.߅Xx} p)03 0bZ`ٺKA5x YVxL8VSpJXZX텆x*'yLW:^:>SW|h^>Q@~!\ȅgAX#VwUy>L(]ZᘵB\6 6_ƒ.QP`@Mp{|r'aT'aR=f2TVLHXnAau8k2xBl\΄|mExlf i'Qnqc|ԅW.Y~ݶnAUtlt2΄mpǖ z%;rs `ReE4/ < ØS=, 1Gur&g|DHcUpW kW`?_d(; k[NMSVb xD -/+u;Q&xRe%`RplVlO//P. xcd~7ToQ/}Rw;RG^,  9;x7V@/Pxn[AHxÕ<)'wR Qs ϱrbBW`o wY_26}`t^Ҟ.Y/@OR1`ʠ"6Y(ar;m0U Tw827]kO*$JS\_qc2054У\cb`oooofqfqBW}}$~ }}mb%//%0Q%})}@%U}V;ţҭϣ׏^۔}0@˞!VY/r`$W.r, 96pXBRE HqC"`0O +m³ϟ@ $IiD+ РQ걄 tJO Ľ+WTVR7 )"& .2v[p)ihl(/ʣWO>;`)j0n?+fqcGVxA, 8pq \^İ"8r0ȼ$AM$JcHI1-e$cǁ_p4iӦ=ڕ=>J0xÄ/>X lW( 4 1v@ CT0C)"um#A7VLPA TchVMs%gDi@9 WUb~Ș$qH % 7p B1Q-Qd% 6撿%uA,68QР}K7&!@LiR:4cQ@n̉MՖ7BH"?H& ^`@\HV h X@h)g&1|C`83 $: D_bm0vUP@T Hٯ AWSndP ` *j@bQx@KWh@\/ĽnAZLM]=kn++YtSKD6Ŧ֤N1Q+3 | ; 7<8lJ P ؀+X|b /OtFr\;M`! @ЍO|a<@Dz"M?7. ;i8`'ت-6H6@jm1x 4fA P%ۺmA[B%9| ӇjYrJ'|ě3]M DoҵQc^HPb/ߺ`-l@c0Vc2 R)Q<]I4 `~W9Ļ ddY| Y Yj4,y=rϢ莔q. § wʗQmLmT_e ,r'WxouX77UU;=LK'p]7; X/dά!8ܺ'&}H@@:w*Fl_aP:U7m$쟑SRGk1Ua}eg!4AV8 eHžgK{!p|azL0XcG ?[+>)Ux0"3~}rP} Py |'Wl xQ U `)`}`%*<{X X5pyϐo`SlaW[f `eE + IKv@-$N5p+ eUWv`-g0|ZbZa0P\wWV1 nh`j`>c   Pֈ{~$ !F@!`ˀfU* PA\(h0PQFa )8f x w!U6&w$pJ  \^x"qyT| ;NP`W`X; qUh @@@H aR n; g |F@PQ5+Pp@cn-B{X\A)GpX 82j;h N{F [maR H2+`# Qc`4(Af V biD0kk2#30DDw`W+WЗ)hfTS;\6~y5p5+VzV14O FrW*ٗ*eh yY Q0k՚)藓|yənu_2b$  0W*0^ 0Q`~G;רU10YܖUqxAI љ0Dzc"ڙ7 /+)3! ~zz[MRٙgin2 Wp Ppò.Wp6;~U>hA@*U0>zUN:W 0WsE^$fpTj p5~^RIFjlfTUNP*$@T @ZJ\5MJ"jmk I!#w P\`^p;,GHez ڪ :ZګpJƊ,Z\WcY+|7՝:Z 2p7@&q;*P9@S@@R\0>SP; @=pC0RQ<Z"<~ y<n>c0<-p~0Ń8- "0"п_e>[콡<{\ ^ 0н !#QȜ.`}@u缺*`B,4@Ǖ\,ǔ80f\<Ņ<9 ͕ l`<\~\8͍ʈlill <lƔ  Ɣ~@*C [g Ȓ 7Eι;-<~< ;ɪ'm R j-k,\ S 8>?)<+ EU]Ҹ;~թ<07* ’X+?Ė0/<ƇlҔ؜ۋƊ_Ӆ NW\S˾ς->x\Ѕ}`Za5`Qٹ:<’6P4r#)d}-= J _<Ԕ 7͐Jm r͖\~ ;M=ۜMȣm}">xߔR-M3(ݕdb= A&QGȝE#Ǜ}^ CEP)Ɩ">$6L!>)~~@\ 4C2>+^ P~JC@){Un))@|9 <.d6 KzHK˫)R( Xܷ3wpJכUХf`~ZDХQpWYswwq ^벾]*TX66KKiPíi <Ij `ڥz:؞nھnήKqN6@KFnt$*Lꨕ4F@,A^?`r;Gہ~ji7/$`$O o_%o<-,?67&/ǹ,Ap v[CM`# :06LM 艫.3 xa9p(-U!xYVM5!*$ꖏ`T#si_/m FdW 6OG q?#j$ZxMK#04`CC!Tx+ )4 ,sp@MX*kRdɟ`Kp0F!O?"jSoD+bPߖO ?"jɖ./=TVug`N_ SA?ȿ ǯc#_)\ V00}K..K`\$ 5*D} ~~U}R%%WW% W}}$Ʀ^}0 WVVVзW W} hW5 @E`>TQg v^6Yi׋[!B!)T}>ּlWY ־~ Pˆ /!\,8p*0l)WƁ"Ș`Ǎ7%  #`+-F0Q*V\9)0U98)Pg33)D+Ba_fysd̍dB< Ċ+#xdˮwhP~.K .O [r@#;|1E;zϡD5##h0L 4uPnlLg5,h)սA&8 tx6c`P+z )2) o״Pgi:p1!* x@x#H!X0^- T@pA#j  J!h|AVVBͭ9Ax HdT8 8O4G& 7'ahFah>֥%"C*:Vh :Px`K׬bNp*%\SMbgpvM #Q H@T, D*v$`DFDч5լfjTa<2FrwRYZLHS*M-؇#(8 ՜)nHtV,gWYTG~wf9i\*gN9$~.E̗3Txy˝ r@RX, "y&XؠaPj$ {\D`7@l:@NB+WAۅ eTaTR5qZyF:L@I-e&L9eGK1)@U;/ eLP!?d*eFpv̤QSۦF4x-p`(vT!԰x@ @PI.T,Ci洆!]v $;TP ÕJí ׌tI jA\ ?k&C 4 JLHX\S\hb7l1b>o2cfpWzf,f) /d$D`, $ uP#PŅYxxSyE_S3u_ZM/< v  F$rs,k4;wPLP"P_{ [,U=0;p5qܧq2'$6 ؀dՀ!Qz51H\` 1$&) 2"#`_@GX|G 7=2ci9T}<0M!uLL1wj l#bA]o`b;N0`-%gK'$sQ"vQΖ,X A# 0lW̑MC"kUQuh$`z 4"h.66~@?@;\HrH*x9$~ kw\' 5v$ @b}3yy~Bx㖁$P#nwRrNE2@veKmE<6r*1irve!v{*,@M. P7rnjA7xl0p$lq#HI#J)E8E?#(R Ч9WWM8Bc&EUz?$3NI#RD Mb,ȐV &cSkYy`,8VfY"5W}^# &96*9 4IP)u,@pjh`zG{N!bA]ؔKɖ#Ŗp)B|IT\p\I#I$F&yQu>P8$ٖYsN I`lIqы@P`;94oRq:hG+a 4%@ɢrr  ѐ Q@ /IMd09݉4긜fP4E8%rDOY ;m"(Q1V 47$ D-;/zW&Wls@@_&7 ork3wGHpB@~BP3݉rNz}I ŢsY%v9P @:zB1R=1!RTU E4Cs?uj@B#0%mr35'q.?'LrN@q[d9($)P,VA[7 W*7 %p-M & R9)%ib@dLs'!8K Dұ' \01}  I & a+Z"idk42MBBIR 6 бV  !a1:0I,x5 Wpv `\JJ` s A*p~qpuQ p1 =EP X++y?  +? a1 a $ j;/J!W˺9q[K 1A`2;TB g#_V)wqM6G6y<5WP pWѰ`/pbJ~11 6ʋѴ:R۰6R pp[\1Q 1,#{ G!+@K6@)׀6QPf@fNNKRl PLbU  LQ*~ fd՘ƞh#Ur4PH `)\0W p}訞Mc B]\hOsl=13FhK *GP0m-ɛ^l ^) `A 5 ps\0XuS 9H?g`vWp4wiplip%p~yKv`FH*p l@!_w݀n˼ p/:4Qk9o8_p޼7oPpp5QkHG(l-Hl`W7p ~d )P:`/iJj('oai!%FНp@B(o F+lpn!H Bl@n +!R' g?O='"NO.%iHl giK%iFl*5?|7;/G5 ,) ,W W4/g/4)455/K.KY@)_,;7**li*igFiKKגlPllPPlBF'PB{s ]G-4#%@F8QH%R.*AdExM#~f@B p0Ԇ?!x\8 C` 5T+)8 RpbKSW:CMW4$ښdI @^$hrhcX*hH/.HEd@'Pq9̒A 8/j˥i,ڵscFkAbC,h⠖C/)RRqZg{{v㵔g/{çP<ѩ3/<yu@ wag\p كer ,Er7p 0p HBVC_6FfM,P;pE4pY(X,p,Onp )pXoU wV/i5ad} y,}qPZw@ZIY|ְyёƅXM M,hq)qC(xRi}Q$}U(u*p-zGdI}VQz_ Eâih`ýY IeE\!%ąN?/ԢZ$w L ?D@b6,rL|J릒9+Xp39V(|1pABu^r(Gpb(]K \aD.tI(nL\(j i.zm'\|D@|,$T̤Z3"IA$Ф%j jeq܇.`(̵zoRG*L^b ܢA@.bm$r{A @dq$Pb1/'pAֱ8h9CStQ1݅MtuŃ&lp3x0dfZXBv_C-0X.>6f_H$rjDz-.&Z"Ұ .3 < 8`ۼbHضmd`7eF&goŚʽg![Cޛ@ @, p*AqښxYIGqV*7@;aNE/ưMK<; 'Hԋ.tôM+J0\-(ɷ:&[Ձ )= ͗<!a 0.ݙ1c"c]];4`etcC" $g  30tu-T,H JQgl@Ȗ3yAX.(q)Wuc4Oqj8|=`~'ԩSOEr9쳾-=  8.fdWS6D_!8BNVtYThMn5h)3%Qf&R*@B `sa#&~6`i!P P/0y` / ǀ~aA$q MAB$,/SwƁ1|$23sNH{jgoWFX!GfH,iA;`)_BB_ E\b phzHvQ_Tzɠ1bf7\aO(K81` Pyyːg`ol Es5"fQ#Q"@8d0*Ax yXxxHD H@@Z`؏*6P y=O>6TX9đh8Aȑ$ّ uɰ$)ʠ?1N*@;zi[0Bp.PUBp4PHe 0@ip[zXn,ylzP@Vԍ0 Т_0 %y@ll0i ` @G`e  וX@,qPO闟9S%:A$1XngBPmI@wFP@@yZ gߙPlOY0 !0@V0W 0y0 "!k Ye5r3 xg@H[4 F$Qibu1 ڂpBq9#yFЂFK@ u*С"J6`Fp0 sFXSyn0[.9i@S#@ P#cSF T,yFp%Vz @z0yp#h ) ٘4 `!QP g 0 q eXB0/ ߩ/p!႓hY9ZPШ ) PQe IPp@* FJZuڧg9QJ h&[` _ 01wPF٬/J琘9X*)9@'+( Q*ڂ*FpJ!qKq;:lpyT %p`9zHxdf#lt[v{xz|˷۷;[{۸۸ [{k[ {;zpۺ Kk{z;˻v {+īʻ Ƌ~[K+{;K ;[[K{;{»; kۿ۹ {;;|;yū»\&kKK{8Ü[L<ܷ6L{:LL˾,ÿ{x{Q\M`b5TI42qq[XgDu[%S=ǴN6=id}UKϣ]EMwlxafŻ4$"r߸kj|PcRԕuiwMKRFCC/ç~&ðcatSu=;Clw|׹`TYSSZhhqezj/,2^4.и߃F-.|{/$ gKCa`ikortװ333, ܚϺҤe[ܰ3C=3eVATDi&}VU=.=߾Ɨ~ׯvyĹ{w༷!4J+#su`LP[u\ǧp\ċ+[CO=BqkXlZO[VȦL4TQd~0-=  9 O;48+${2[x`<40;3Zϧʢ H2@zj~Q~z걧ڊ,2 1ʴ,%i%D? {J.ʴ,f6_6C,Id\m%ceB"e\ AWqU 3Y!&Kp#)JOE3אPDV\wCazo@OjNWQ dVk}5nMcGVM Xh}volw!"-! vf s8$yٳ9S `}{8qO8 o:bT :Ô^8}njQoPȋ#h'989tqC0HǴ8OM؄b9ȏa8::qLו64hz,6 6`3hcTOTL+L@ /, 'F`Q0K0 u3 3vs /r gc`CrOnpt6n&q]cqyqMs`x!"$;Rc221&\xv #w`W"w%wp $crrX̀tK y!sO M9r%DžVr7$r :IB…?'s^y7V@ uw6cv &OiN)`w6M ma%b pOc:q }#y3 0 #IK1}:Y8x}5i898 ^è^6{xL3y^19L:i:1:c4 : jd1PoK6 LdD <JKYm(<;;fP<1igDC5dz&ĩڨwDyʧ E%4Dzsф;@p ZQO$c@:c #CJB;ԨCCBk0zC:F4BtzFB *"P*~:B|zjz麮}J|ODʪD䮎ʪCIujsZ CjCՊC A7$Dp=W \ ?[+( Ǡ0 Cu ?@ 47ImJ` jj :;pI̳gF3Pd 6 < +K%Z<y5Tՠxyπ#0 pi:z0(A(}zع5)p Uk ˃:`.TW`%p {! _PPХ,_2PP,V@.v \p~@ǦU _UP 9 -|PJ_0\@T L$TTM 4`s7r`t| 032LR%#`p^Pu  P&%03 nMB'0wP~~@ʾp+,\L3_@%3, A\4 4 Q \hH, +\| ,3~Pʦ¤ Ť\ ;<@LL Nܪ N=,I##⾐ ߀} u.p5^(, ̟=ʇ@ǰ1` '4#YwRe5' ޥ;d +qwŪlnpr>t^v~xz|~ B ` 3 vЛ 1"_P:-^ p @>^~ꨞꪾ~~p`y ` Ӳ0jdڔc  v B~>ߠ UરcP13e1Q4d8 peq>^~{{ 1"`:f1!L0}4V  8[ 䫰^<U` xa% $+C@K>1pfO:^ F`? u$o6*2CPjn: T`~ 100="e1vMTP ; U t? @Tq[O4L#f11 P8e  `On3K8 кڿ=?L0de@i-NO8׫:pQ^\ .70R L4,,^(' u)=IRH%MDRJ-]SL5]>J E&L5 P"0Lxs.j'u*VVXe͞E}6]P&aP?.qG_ş>MvhǴ?YUTYIn1!A. (;{6iIU,;Yn޽}iO"d[Å ѫ_f[;'|jcɓx7~2=ɵ—G2qɍȲN7&cA$) |&c& p O2y愌&YD Lݔ_ݙ͓I2PAeEw<1Q3 d21n㱪ڨ…oHM`!K_ѪF .v&(c2n 0r`O.K ̧ho P; 4x&>;DFheR&a425xI >W 7d%X]6J A0t sGD`$`%&?BFjsv;t56*4 #"b|q"1@j @ST=.T#CM5p}c bm44E6N Xh-)jޘQ&ZA!A!W$BVOd ,@S(hj VaGғ"a$2CHJFL8D xLIEYq: XwV\cg=%1g͘| cDž'A-:J] >yAn0F#qRTKj}yF)Ą&hq?J?.լaID~INT ܐWdi:u\zV"-mTQS.ģ(@ [~B; R i<!6è /=+>3$ e̕mGj:=[T9 ]Qѻdt"vCq~Aj' B-=XF :n ~Y npj4 6l®p1\FAR PL.l۷v0g͋&&DR=|++PfEix 8=X8aa? 5D"$}q LkQ,P#Pb vad8L $px <9ϗ! ְDLX}tӬ/qUL|@8qIAŔEiU 7m"0Sl|JM xXQdpcjeM:m柈C==U Mum Od6An4d`~el[H.xcԒJn,G#K״2T08){s}@/9O =? &*;! Ș21tWx+`-P1 8P]ZU&(I=G3/H<P쨊C-(C` A1z_+xB20?A>pKD&=!r@!?TD 4FFH$HJKDDK4KJDP ELRS,E>AUVtEWT\VWDU\ETMDN EIlDDG$9 D3ӇNcN#-cn+9h4ȃCLȄ H H(tI0I>>A[)s)؆J!rL@?H :k~ɹȞD˺HG <˲LlL|L4ݤIIɄNΊDK ϒN=Hnp %7x2 { W4 y2ˣ4h 72YjXtn@S$S$9H$T$K&8HPJMLI.+]R42PlT0eT$URSA4(7I8#+1LX }}#7ȷI:tOLWT<|VTfIJH2VDD - G~ NM,-Kf5R~Vy`7;(I .Շ1hٌ;ك=S*1N`* ΀(*x`  s  `1AO`&_N`fa^ p@a VVa1!~ᐲpBp;x )H`Y!&N`;X-. ΀1XXpU`.a$>;B ɺ)*9zհ5 -Ƞ K@{-kdB𙞑4LYҎO&C)"ѓg䫐 !&;*|`>b2tpX֞Kk\N\)VIc =YdWf~p)ff(8g 88&x8! (P7x&`I!WBJ;Y@jbJ[^i-OȈ0J.dO6VidV.Ojc(~'~jc0?(k6(jN.?dj&Jvwð?Ȉm-cIkdöddK>썶dmPŶH-ekXIx~venNY-{dnу!=TuځYm(7Ff inHdцFhpjOij6 i̮Om6O0ڢk]e6fpkh/hoK.pjbpZk^p^{p^qJp.l[ fkk~K>e}qXeLnQ֎~7x-(89&WdbbWX;B8ܾ\N\\hH;/oksHdJ{Xؾ&oNpq>phoq]>KdmЦJ?hnplJjTGq?tJņAVtisx-Yzte>绫@n|(h|mhv%Ǟ dQ+GgW&r]6pfu櫸;s=?;IiK捞DiIiƞ64͞ώp oPTtx'oOqOlKuwh:g׎P;i왶؞i5i*خfxW1hjYz|k((EA:2 -["oxKN@vj=읶oBwm{Hn WnOЎdsu]hfOIoDo[?\yznn7nfne"GfO{gy9 g(d )fd*o֎j-xY .x[瑀n ㈇ ix6Q'n7jlh|u=Bvϓ5hZCd? Vq@ܸlEO=:oB϶X(b)R|p+ڧ pJ@f׶-Zِ+n{&[$S,8ruwMV4~U]5^}U^WAw[TM'EWteP F$ W$ 16\Ndob/"6],OfDrO+\8Y @dDh@7TqVXE颫:F5O;Wva5QX1nGQMoQz@b{ŠG >~zCzGq|5w/DBs~8Q_E(xՐ(${nd q X Yg5xWuHy U〱$=y(w7x/ 3p2>A TM -4H7Jp&IT|ޡ9"4^"(/afc72n|#F6ʱv#<ޑ|2.1H_GF.)0%pCw%q2`-I\٘?jrj,%`FRⱓ$&UҎ Ә[~QhB'4rT`n(sL#09`z`ߡBrΚ1F1ݠ8'.G YYhg=i,t0(Ae ,#Jh 2Z/d }2GP4hH5Rγ -R}MJ3 χ# Ȅ?/9P1@E%hE[:O456e҈Ԥ0O2!gFpOڴF>δզaE-R*ԭ <xru8?Ff\GjB 0p9 KzT A g_% %qVUkZ;PQ\"r>Q\=|202i@.t{.+9;bLʰ[2׊n'[Y+Z-y@ wz{[7xc{'14- tB7>,gf2A ŀ[զbg`VgKs>QD"͹ʊK 4HrQjr /$<*:>2pr9ij1A&2lS9II21d#Y]swd&!:Cbɲus#{=:|5r8D$Ίr8v~E 4GcCSqiC|0g:V3 ,w9H&2(#BTLߠ'U [#\A4H$Ke)V)H\C-5<P#XԂniYIV\B\ CZ~#M-U"&)GAl+#l@t5<5x%m_&D)}AHT: HBѨP^yYH$ \BjƂ=4L@y9JҚsRЁ,̀HBHB]V&f-^!|A,`B!$$+A9@,DP @ZFaB5h%5cQAC:b}[DV^.@!<XNbq)%"%)@!e HB' em %< (b@fesdiU.CdA-\ <Lb%f" 55( Ё:B,L)xlAxF$C(zA}Vaè5AC D XD X䡞ܘbNl@|APA8(;b^nB.\Ng촦H @<BgU~_Ff!8#$nNɅ!Di9 < H HfI)Lh&Ё=h6B9A.8\B.h n:05b4΀=5\dYwG1 $Q|C(WA7s. _2 ^}?=g-|XF#89g.;l@f!cVc-T #="?:nd1'v\VRAU7DhCD&e^d%ID7ly}۴".hB# he*8P&GVytGx",H%Mxscj16z x0ᬖ9 9nbWAP%䂼"H?wx?sFC=G#B.H,T .7:"Me[{.$=e<๩ay% ^VU'>՚kšs7(P"6wxrQ`Z5⢍+qԣoa9n} 07ߐxP#Yy0c6FgQ PٌKѻXg=%Ԍ"+sfoTGRݒtsDج6r9{m*-14*(G9*93t/9 X'|piG%Y跌Z|>߀ƗiAD>GQ}?B߃kyXgQ3CcߓW=ғQ \AY.Ul-;~k;zG幺~v0Ƽv@jpN@ DVF9.2zkA{a+(de>@t0`AjaL fB_P=~*N UJ )&|R~/KaQ nEgd1&CO3tǃGIzkRZP1"U.@c(I= )w92>_Xˋ(KTؤIl%hS$ %|AOTb5f$tnZ<6kjɋ3VqXxe2hu4I;.C#f`a2Hs$UHůً0j[jmxtimb)ay[3=;K0hΡJ#B(n,01NJxc -({*sL5Bj"C&""?ߐˡ/4RJG΢:L-r (n CID4EdY r)1"[7#dNB2U`В#K-(㲹9@ v2K,2-* L%Je=9ʸTS,55"PuXUQ[Ղ2H4!슫OjB 8cAǰL*W4HwR%##"aD xXGND.K) ޑXh`Tuw8b6<ʋBT?#գ /H ,4*> fg& §nA Dq {Qdsμ@B y))4څ#33#7͆LE7~J>lۅVBJF8{J"1p,THB^׿*"F;i GBf$eF8 aH2P4v*>%8|a5 wT-B,"%/sݰ"2(bUChPVc@l2 n.Ԙpx 1 j43a-,_vIxpqNCi/C~:(ڪj =cuPK8yr%5|P%ykq]]!Q$j7\ hM;qHy5G9 p #0Dӝvܡ7L,pÛS``m#r$$Nʇ!&g A a X J] B$Q^ mf!@r<oŹ jD02# ̏ /* ˼!#Jn ␌0J &"!ڐ ` x &aR>$P1m kƴP \bP6 & k>AB+ !pt*P#@舯ʠ2 ~:&AN@^ R,a !J(/PA J&0 zѹF _ټŠZ/ݼ^ MD^,b3*P!t8B[:\A tr66$p"Jl^qD!b ",F܀ i c20-0`ܠ QH-J %Ђ m*CnMAa6Q!Dk4~" /`4jF ʠ  P4!8$m Q Lq BPD#'$M^NʹʬI&qX 1X c`LA3K ( ҥ*n` ΰ ӥ.Џybr >J@ 5m" &c "^And^j> `F/ KD ,+L` `F#2q P":!"]`&Xpv`la ` "'H=K P!:- D P!!Dd \ L pU?*L:A:) / 0`"I ]A i53'3j2(UPHĞ ܀-3M4ԡ0ȕ_ @vvf` |tDF0 !@FaO'C ,o rR3 A% XU"&! r dR2D$ 4nj (6aVP\*3`.f_ f0*EE+o L ,We4fC=?6һK6\57? "IxәH»㚧!aa0.b? {@W'i; c9?c8 a yi'RLm?4gMѩP  ftļa @buB"0 L193K.xyư6k k ψ9e]jI. fZ~NW6 ` 5KaIڻqջe4|_UDvI ,oZXaN1x_Qk!^anSfJzFR_x:-DMNȈ""mwd`e0u)-9',w\m"fC(HHd\%ޣ'&,|ɐė1TȀ CTQhn4B%CH%Y(E%2R.DK *f%”Tt#B0S&|MnXHUjG"4y1$SqG֝:]ns:- !DȊzodvLH37hw86ݲqb!Ҷ*Y23%P:ar hpö _<|tHc2\W^rܺHEWG21ql-`d H#nHR&I dn4!TBp\S[;DvXո7aXH -e`{FhhNPZ$^N.9@PD'P8 $V E GNnENKFSV.higz ,I8?tJghȢgF9I`ؖGM]MY4A_.) V|NH'c4zy[Fi`gno v4kL I {tt[cp[ReІ#+7g _p w|SMz{Óa80*  o$ kXֶ7m*&cWRFJ@wK aXZ!҈$ IxV4mKX1pjtʖ‘h+ݸȞfBI*Aea@a1xe(`%"% (*8$R(GG*{2K, .E!>RcMř-Qb"M Sa2'/EA~"$Rv,()*+VVb,+MZ hC`F\:)JKƑM`GCˑ#*(6QNF7դ Cs8%1Uasb6(T<)F=Ғl'h 1bQ_4EIH nJG"&!d|u`X Y"-*qv|2ZIv/K)R+cCkџ*U|DA[:LbjtGQ,UHAt(JՔ/'q#c,,MAKZNT,K;fV̔W+Dpe&D!%J^Z<+%u2v[*P{:h-eJHebYUNVD:NIf(ⓓ%E&{ֱ LR E`Ej c@6Rg$-mEͥ63+b1lT[wBʩ?'+H$a5$kGEu"ZmdYqjK@} [gOnOt&%($4Xk)RM-fLŸO|b 0.$'YK"dbVUn! G~ f`)2" \n+aEp0EШ][u,H jFpl߉:ZNtj 8aq-j1M%=3udYL|$8 !#^( Wы+̀ NqvӡlA~F3pb<}DƖ҇ki 8Bx>fp0yM4 EϘIH~S(q@Ag{hCr'!>xAf^T0P|K2. ǣ13b 5j1qm<!E!w]s XP6)@ PGWP f @vtF'z!dSe 0p HtPPyttW  p (hZFVEE,0D PG b)etUtTs|VGf$6rI@ lgOFUFLV (3Ap Yp RRZM* 0up 0q0(l!bzKbxy ᐃ4G`pPGxY{{ ڠ w (>4 Y /HpGPuOJhR 9xlW]U4s2Pi0y|lFgE9*}cI0( /0F0 Fy  [XF_h/P Ҁ 301p 0p /0* > ndw`*yY i8p yW  G0&ju'͖m6/ `V//P@` K@ PF@`/Pi@@)FFn_1A7FP"yh@ 2F6P PSr'\huzGk(\-w `{W)Ƒ5W(s( p >0

Kp`%  tU`Ode.ey8f@HyukWGPd%c/_! KX *0t@h;vFpZpHP Pi0C:Di|Z=WIwiI y t88q/p РZ {sUcڄt|(pt@@ i(J Nz FJ4W[){8 @ 00uOb™l T PK җnFwZp4~v P `@И)p p 290 o{[Ulvt*3g'jYl*c[L&[ b}5\4'_ 0 9 Yp *`  * gP_'o &Hp @ \gbn *@ @ t@(4 P t A0f'fZ'  |_@ p [y 0 `  P $P2O}FL(M0!@xd&))X˫cvYj.a1{Ç_G5~{ow KPý t ip ОwZU%= /`BKY0 ~X/f 9anp ƿ9 Ityzf c!\|@GI;  n  | *Lf[g'EcT?dqç s L&͕M|)v|R&56uu\:cV%YvS$)[נ֠RtRw|,fZGb(opW@ , l, Y'p`/@tҭ: uf'b\Y gTA pW =7VF2^_ _`.d2W.4)%m-!*XAYYZE@΅m3IKr]7YdDԉn 1\sbwn~ttsq79u_``SAmsԿ@b|QOd&b)/Q0AazBmݶ$6Vj, @=ޏ-XMݼ ߒ==Zkb֨ݶQMϼqf!+% fG ԚڗӐڗWK[kllvBaqeQɝZ-1>=sfȭ϶a!D s@%,g5Y1[da W$'[a##T!u ['S.kdR{Vm&-}q5Q&/'=a3.ߔYZzv1ZE) *"/QfMKW$^q߲=Gn@1ߗQ@ԙO{{b|P /"a,:Z`rHrj<b.A߮=~~E,b'0#ma-! sa<1Q@!ǾUwdj^A bWԃ ! ^R.Mv#4;M'zqmQud=a"!e!z|Z`t 6Zm7ot>fs_[16,n0Am@a$Xzd>OM!1َd $&xaGG^b8n&&mxh&jU!,TBGpEO^=/_Tlv!JWE[XugIM]fBplvlE]TaqT,аn4%|Iۗ|`c=,ÂKDXb>Sח%py0 Gh$BA!.[%Ǡ{}K$ل@{rDjA *2PPbTU|Yv 8gt +Sm[w3Խ+؞G#IjO? j):GZYRdR1#||4Wif!<ح:FeL1n?ɾj7VE 12N0bW^㫗1BXi%>cӉ-: *Z ʪ̤ *D7JOÃ$*dmY2`=2 oLֺ1Yvx.AX?nbNy Nx3EnY&)mVnx+!7<$x웦o]WFo{%f>K#& 4Oh2K*(vLYAY@~pWOEd*ftv#q[aXKÚO Oa*"T~"(b]v],c#rb8T%@g~#@&Aku葥e,#V6ƐA/|P14 SeP0'H `pdu5ЄOMDf=zIhLHD&vbxF H+7(OyӠB=MZ{"re`cP~ C0+,p/.F8EvxE(RoP^ 'kHR0Yz¸"&+ATG!cP!PWEY|AgEB찇xPCbⷽo,G8Wʠ:t"?BB)V 5{x@(>Qd`ze(@dzoxD?P(]z$[S!>h @>1=*76PBU"obF15f(7u!/v@~c},~~]Ȅ )ş!0: 7U!! ,R6|Jn2ҴX0OI'Pڰgt94se{iϕX2`lh)FɌ2a}~4QO@ M(xReM_U $ 0$31ѭ4 8+c܍__xL52)[u"3`0<"0`)01Y(B 86zR-( ]BK@Q`nPk~:&E4T(;(p+ @S`h>;PK QD2>7PD W`#B=?"J2nS(&0c+>AuH>{^+tRhjdÓcH$[R/pED&y`S7!f ! Xp&00x;g2 +x+0Eʑ@; 4iGTp7 } ǵIgBOY10L@ xal(Q{|Ԏgwi:ħZI\>;I?%8B%6$ J27pU$ =kpIIHSFR3@g9qfβ 8g"pT=;f8x˕Ql85{ ~W;1 ktBa$Yx98E1p@%\$^y;5*aӳ'8x`W1~284~9O > Ma.=>2/h9 .b+uTfCDM*!Af"7NZp5,c fGh™6<˜@&+*CL5-aKT?p$)g y&88d9F j[%L"YQ;C4/t?\{6[;n[qf Z64~9me E룲qF>\\BNZn-^ւ]£)0ƫ=80aW`5W2i8 Z;& *Xuxhjd` {Yg?E!T jM(6i&4U$ޝi\ ;L3'VR8i 5m+J*<8(dhB.bk,X-BE>qq?)A蔷ЂcA^Nk=".j>n )8 h*-rcJ"@冑//մE:""|m"=N!-mV8ȨLsfwzi܄X{ADՂ9d&߮\*a(&b’ttތ1W #)!* 9-cYH2Zn qmIeOQ ^ }C2V1XAѢRYс=*l Q ,ߐ/. XzHGX p*mƃu8A48,1  5i<h\G&&&Õ8x䈒HHO(A,Hf5g4Ђwhw0 x5q0 0@].Sgw5rhvg34l_-vMB!k'Hi&6j'κȉ qow ϶v Їhw&wB?wH[TvΆxwrwv 0G  HoΧt0H [yvPQonX=X쮐uT()5h ڗxxT.%y}7Sa&`Sv ]>tCFZg]{{L^iSRuw_^O8 +9 i8yT ȏy[%z@g{0@$\HȉwA9||]'rH +O Z/6O{x~ݶ  4 zx@a V1Uea}R%DTJMF%0~Jeͦo &h_ԛNMn,<щ]FYqF="SQi1^yHEv[dRWTU|9 {edf$ŔbJOZP턚,eՊ FIPχ,8œ>NX`TXthn^VURVYnrbj%y)qج)1w_]\r!^vaWO 'gj cU$3ĜI5CZ]AIڜ()ڛfN JySdfYg9F-Tؼ]*Sy8nXV٨ I$9^&͞EWr).2run)m/};H1+Eu\n4ùh< ?G,|FE[tpn|;nF#DS}KJ%tOV4>X0g6yYٶla)%ءPT ]p՜R)@ފlU7cҕةy-͚IgG JQ6s6CJкI&Jn9V6[@)eWE^otpGM YK-ibWEɂMԧZ U$(dD@gb ;,,#L@D/iy $ %f -S[83>#pIq b:!.G)6/#r^*\-h+p7"װi$RLGh3Hd8Vh|H0]#Ѐfx#bF@07B;v#)ѣLD +pCL1F@E( m HCYڈR"ͫ`C-B %Nq,bbd ڀ> Y xAa0m`4`%&jQFSA9 W`әʹY.9"hs8sżF ! -&ډC SI1&$% w&;wt#m;HL'}G`&4W0 EԘփ ^ M І? &OFT (]) 3ԧ~RGŘiE+ MЂ10E`f0LqěE"pDd0_ Ld$%í)aB'iXK-t|0.sh Iƴ'x% k.TЀ:sAEC0eb=i=;2Zk#FmD!!}ae-4 :Ai({܈, t89 L LUAixA> `@1] h5@.|B\ @1C<#R-D=B] <A9C#AOe_#<@ P-A9(` X#JZ ڹE TZzԥ)LA4-)b.B d) A*@BM1B xxW7LVB&\uܐiZy\55(@H<@H2]5D$ZTי~\yU%iAـa5Y@6C=5T̜Sb4%^# ֪.@ d-54B9a-dB,-Ph l)C B,%4Uaա.jdmXg5@Y9 L@@,H&d)$1BD`PmFVÖL٣)#?0 S AYI6#y,E~ZQ^ zlKT<›>@@#=3iԂ6BAA!\X#QA8W160VhBM> xOl4Fl\E\5$$=]gt6\A,lV/Fq) *PVxa8J0FIDTjlweGxdR0,t$'#\@-ST{6A-DF !MQomEKǦgEVdi-QPb]XVDpNQ1_D%WKQDVYob WhQ-QAm -x(wU|Z o FIąYfEĚ # rF xf ,+% U",!% LfğpFd}Fn҆˕Eޭ_UBѓ@Yʁ oH3!ZU?wgvtQZTY4]e], w+wyWd+閥*іuWX~*ܦ"VDID3#W֒UMLZٯ0DF $EY? j0`?I-G=H$ƩQe"߭y= Ha IHk|w++i+ٳftG{GtFsW.HxL<E!Vq5k= nm4GGoY^l=Odn*@oZU tiF0TFtBKPW<#UF,9 A4Ep t}\HtYuY%]*vۢ zD"$YtZ%Ucy1CW._єc,bϦpt D~pJ_T)n|RE$ >1 i>M|:&Z~~)=_E|[zK "BϹEMepZI*%2aGde56F%e(cy;W^6j'4dibA&B(JjbB$#`İXm92c(٢@ +Ɏ*짦Ŋ2a!E-ȤȪ3i7&#( ƚj`Gpȴ:N@@O4cO+X>RXށ\&U92ih~0FfPn>2KF;/3ŔHY{̍b)WW~`ǩ>"օ(4Js)=JʳOPTP Q!&1k3`$w6 @BP)"BкLb/(zD$+HL, !ETd!NdU@%4nTtej_bI~ٚJ I)R2“Oa pšSg>Ċ9D7(_`/4b.5 pn@88/\w1`uJ9oʰj odc$EPAkq(ΜC!}7n!4@*FdN?!唌 V(ȪrYÕ> EajncIg-+J 7?>0+ CW;!s"p;0?BD$)_5LakؼME; Ox`(|0Y|,C:ox0 NC28b$/2"{V D'4Paq;TЏeV& PHG"]q:Yq!,$ŏ:ChCw10c XA p.H @  , 0j|T5Â9@Od: `n"3+:1Y$,!Kd1`0 !q>1 +P&"$`a-!AIj @<.~ fH O~΋vn3#0҄91`KЏG adLx2aoVrpף`7qpYb6t9K1]`pd/{(`6f-3  2W|H%7P9F g Џ"@b a?M!Tcs3p#O.7)G汁pԾ~ "J yӰcꀀ2lf&̺Q'dE+Ti YL.<[H"fA\!7J`) L3ᒳ%rb}Su`"CT e`;8PwYBH}B+xDz B0^o܀pSE(~J]di +@{ZH׍@,"N812zفa6up&/C?ҽQ!2+S4 x 8Mg.(p,e)0By,ȱpTWV7`T7 4}ebhZᖁ)DoT\%(0U 蛛Ȁ0`_ Pc!ЀX02`U]D7+M`aW=HJ@SuIXfY.]n5EN0"(Á=l(=()# G5PqcTÞG{@JPW&.4kޠ.k4utX;duv`0DV+<@XṅEh N-eD sTB43e@|̰)Pkۡp*w",hP;wiwvʨxzB j)e!(xuF @ x RgF>/m`Yޫ0@]F!P!n'Fe&eR"0Vaꀥ*ԥV# h(J~`&$ vZEAPaBV)iB L! .艘@H6`feͅ@"et,0*H§Ed$hŘw fL! ܀P av Pb`m2k0"m_@ %!"F 80$zڧ6m á"CXJ ].N-dB$N"kN%`[B .ާb@b6크 !@g {&%1H},WU+ /Q hcِd=D"|B@ϱmi ldFM!r\0I`(!&6'狊l #Z`#q 4lRu$ bl E @f@]@+(ʲ!g`7!Jh Nr=%rJ,,A p_@?'xB!aPR LJ+1 `H " DAa(h++dʠfj fpb bAmRap+WtJksRVrPa#  TJƠi=h*2\G?f!Xm#8=CrKT\Nj+y\(s(32RZe,1d6] P3 1:ՁhV zAdpT,QϒdeJiւjo3tcdHHi.*(p(hP+=e""Lv1r훘,h@F*WI ^t6*F-jg(22mC+c!f[Xr+U 2l%s`'cG?ʶHK-ke >`(`,) c)_1+Ct%?Q,Ѐ0"."A0Ivr}/C03JWM=?'\[uUؓ+vfIw!hK4Βk3>k3&."OQm% b#^A ba2m=/ J(#)B$Z sZu(Vlz8ikPvBr> a_4^tyQ" Dc*"d2^L^Gp(=*Af݀K?3_4̆S > RHwBrPF¡ U؉q tu#RF'!U:xKxI0Ky&*YRQf=쨘Pk`)Ȉ ɧ(,DatVk@x VHV+q#))}]xX6Qy*_WUP''jDW}DBM3<j 66x#Y;ʊdE&)C$I"8S__H>"2$4*7.+FEjE:+Bk'ZTWw|N \4p1~/7A%d )x,0i oTV71zt$J0G:=!:BvuZuGbu'8#=xDHdF:Bx'pb5o0pC_xkracO*[/mn ϩ T[B;m?~bMZQ!{kc5#+zos[*CիhMA~NU$EIါA0BAzֈ;3{A#; c2!40'p»;u: c[iA|'d9d#=@6a6VzcYg5|6X3 3\6T}! | @6 N#1G£/b;z}6 \3 {%A$1Ň{ܿ!\4;ʕ5 5b:'6cJ"fD,RI"27yGy/Z$ 蜓"wɣW1b=J\ι|#}TCO]u30J[-KBηK#7[+=[E[EHtbɅѣ]Zʠ}3ѽ\-b8ڷWw\=cl=}ȻE1Uץ}׹.bx Ư\޹;%ˁTe¸ß-|͛}"ɥäZbTÑ?\ɝ7~E2$URim=][P'< 7ƃڹ*1[+:6"r}yB'P'"{>ԯ|}y 'U7=&btcK=QW#B{Z,]{_$ebG~"?=d  ~e^ƣbA˃1+&>saIE>޲w虽xK"]SPy2}ѻZ[潟d>7Ϲ'>eu5A$ Q=ϋ J8P,4%z@aDC F8! h>,8"(E;&Q!G#2d‚.>㗍@b!B< R%EKcaE}DcР)3{Ds+-!•TI>`”/*,yតkK-c}&x$Ml ZTL5*BWc)[?~I  $mBHRːCV眪2!N\_7 ,F}|fY,vEG)+c}1"v Zfbn4BTUB`_xaEoTg)[iA$Iﱐ{< &bWB&|ImvNw8~+eSšQK Mg%EVd YQGf` ) X %D|_TqRr~G dE̅ST>JVj) y pWQ2PlqC`BTXc"07= ePb6][q@Q#p`GdĚi-P3 _0&TP-tq Y>MeV PEݹG.{$"hfIRZTGCE5C-IBkgRKKeP{Q\U!L sU[1q l=bF7p )+GDӱisJo2jJeRf{Yh}p >h6 >Ihd`RU}%t p0:icshc s0R蜗M4ј]ӍZōhH.d. HF7߬p~;M8/ݘ #!<H +<#s3Y(c[ _nׄw4r!n6$4C)@r FAi2Eؔ '$mr9 AЅfMl"aU@'bXqX@ P d+Jt cA.vDާ8m4q& \/ w<PDF.F-;5 P m`DT.Bp ] E9N4 6f1|a]JhI7:` TS0 |dz@P Fr%hԶd-0ra^3%@%K&L!PA.Bp ]b#&%o"x+g❼?kc$3 J E*|lcWy^\PD[&0L G -[/yVT@)F#P,L c-;* ,iY^PJ\r <,qAo:Mwq:m$E@QЁEP뢀)0%J44 *<"%BGabyk\BDp ۃD`:Ρ_D#$7ה ,F$\B 4A,#4@&&@j"gh9%C0`*Ԝ}m#p4,iHC0]BVI&Mdɨpnt RvPJ2J H-mDрSxڸ%h.v A/q (almң/,xao4rl%ʁ% Bdu‡w7aw\Y 42 M \šB*HA29m\Hza4G̓4 &>4& >hOw+ٍwBp sDp}ms =&n#{yL `>iK@jI0h0dt  'KEq5n>71h]0 _/;L{d9J,DIFY]3@t``7Asi0ڰi@ 4p. IpGN>㖳6:h8#BcO,ړ?tiZ:;:8>9dV=,x2{DR_seM iF z=y;UvAǐ43`УK3ι%9$ar%"o0WzsnAs O9g21r`1MzcpB#B qMUs"ZW kc]<ģ )B +&`1ZK!4B? | p`#qS{Ja2G_cOA p'?F#3nS5*bB8Z0TpG:h0UpU&P? h2v! M`ic5Kec"uߙdqsCb=I:3@ZXG+@:+KJ&P#xYر$Ù_:'XX#K7^r`%/z-c@u(@K|i| veOe(XQ ~`s| F]ZQ:;Ea?Mz`:|"Ӄ@@R=O!Q5J]KK|sJۃIb9a;c;:8[ۀt#=!FDBϔ2Bo@°Xہ`+KPMT:SCPr:[GDAu:[J a~!*Հ!jc']5KHY#PtC U6۪:dRekQrӐ8Zq%:]#8S"![.4`@"uK<'cO@~J -A7?ϋ$Ǡ#T%íJEaaKĒ;c“TL:`z knjTQC+yXW#yv# rs]d"Q 0FXYY@b']WBvǗ(ȑ++KŇ @hQX \d˧QcqMoZcńV+A+L@˾ɥbpY|)A?I@K|]'*Rګ ]2l ,ΐω_HiO;KyeP+:DpJsL:Tt-X `]3i wS=dPJ=b"[*]Y *MThP LW] o ;k+KXYcq #쨜Vn$S3i"! ZtG-atMC10cGa[{PFlqM \q#ɰ[FZWٲ- 4CA'_`) 81[X?N*5|B]#j 0Jc㺮p $JC+& {qշMp0}T@E24J7|` ~ iZ`ѭ' ^ܯ W$Eo T ,sɪpȪ@9"-`X v:))P27pv^nvP7ڜa_4Tt+=V>Dߎv=! M&Z*|0b Z+Rvp c$y7` 1ɠ->N'tMwPН7ށcPtSء%b 7Pc0 em ^x>WXb5]SA*%`P n w Vp}:e X}:nˮq `UT(j y~D2 r &TPs0?py Zv:Q)dR)4Pqn 8_` zA1cv@0p~ BnB |PQ~p㮧ڭ!p nUnc 0% u02_ >Nb .yn1FMu@ &2Ӡ0unADdo 7/5_ _M@V ^<,-!|_0_A^Q UCȊ$b 2? a XCEB4%J4tnZʘpcd̐awc'T}1FfL1=)K 0`PrvX%C2&d`2,n(SlvX 2PRxݘB_*.>+2b̀xPQ9!PaƫͣW|+ "' IbeͯWÚ:I>g+`_28" *(;Qp : b'@#\)+Ѹ$h Xe!>#ʌx3~X¨(aM)WQӛ*H$΄>衇d0IA]ɛW{ ;"@2J0L|'F5oJ Z> Ku> c3LRN5׾#96X3ϐ㉅53oA #_E/fa"5.,\̝*pT`~>1Z`h~i)MC潹Cx$"(t3Qpf`92 cӂcF!"do=FM@*|x™2% `#䆥-R E$&` r56!C00(ĩW [02ݠU, Ts 00/.^4d +VBy7 zҕT'\jFM@b-ҼAKz9)Ou=#"V5 3W3P!jUm hP)PTYW1##ig1dԐR9st;<87GyH'B\tUo89QCy&) F21EOM=LSZp K@dnT 1,GBcgJ7A` @e㋣]Yg^;d0-Tm˼Ln(@E A1M% &#Uc&ز-H9@ǙkdcȩY[D> UD $aYVHvN^;* !B{\tI9Og,c1T9&$T;CPh(4,ªT@tЀx&#+ӛ vB*!1‹ nwʔA09 2v w4ܬg`>d(aDC ׶ʒx. ID$Z;DzL"32 A;T}N۱UpɲSFBPnJNu 6❾٭7TDuCT`o@FHA|D`-ٗr+@gǡ)F{6`Zg Aф|g~$Rc_o%30VTOVFZrڣ7P*dԞ]4^㈻:"Nifʦ}lK-0`+|n\x?c@-3҆yF UIfpYV(m̎fiϖt5=0e)}rۃx"ZY ,1fy 8ƃ `3P( p c:+0t>ˉs1h{@'/=&K8f82ӱx0r؃U(sV8cc8@2ک :j4#rC\;rUc?1Bn 70SGxY`Qӂŏ38Uꈕf:U(p0]Ēc A:j[8: x!ND40y+a9T6<DҠ̼˵k;Ȓ،* r#jIRœ1̑AZa|RS@GR8/ͼCB +X/iP$!7i&@)(hk,U@ X ;b$FR/y.شE/U@^~0hap螺0ϤtCY}w_iS-#x(7x(P|GB]+Dg *2,8ЃRDc8q81׽s E`&K.* BbWr#`?E$nc8`-&XX WSMQI3W僌e9m .>@X#%|Xy5$r @:Y(ep;45X-xSV e< e؄S]؅#>L@7-$}Nr172Rۮ}(Ӥ窋>ڨ9իת*RMz"x[[Ԥ)*Ȅ푏@/H3#\-M܊ފ 7)*pxXeteY|,8[՝9ԝ9]ܰ"Ȼ]--^Fpy! -0 \]|d"M]%r.R4L%Y^!X^F_EYRbn#7;}A'HXX\x\XV/7{PAi:i١UX@ '>%*OuǻYEa@yrFrĚLE |W ~M?B نȻ/7A: 5X-*[J 7ٙ/AZZt% *IC6d0s=,a $$ב$K !3WB}] 7rcQU0kOT8*NOeH;z%fQdr,}0} mRS0dS-SPTv䆠d9 ̡^HfmVMuѐ0XQS~gfucQAxGA +t&̑G,@ m*s Uҍ7!eSNY>ZMG%`qF 6*;B!ƕ+5g4o6C=|3^-hћ\hT=&ٴa\U<}}\UL,BU0'gO %zn*+G.mJA Etv>ڒ~T!QDv5sB iVfG!u߻^SPUjmi\x7cC Qrc}zλi^ ` M(ǨPo;0W*J/lJYHaz/cwoV4+rb,Qf0nOZHpGFzόsqn߽lX|xOZkMkFS(Gpb_f4ܽ{oU5?g8qQ~!cF~FІVFH|`+`x|m 8(c> ;0֋+Zg—kjoi-Es/f4Ц^9|"ZTnD"7a&SLkK"P0KFh x[ݤIx[Bu1^x#{BiR= D If/R.usbehD ZoybGF.pM{N8bSDye-Bx JFD# AQLdQW@C#K9Yl0v傔ɦ%TF%+(A  BiPAt86K ]  8KP"stHĺڈlA94pj2QeJƈ#ob2f._4` bfUB&jKU:kBCdDy@ Gc.0,j[>gqReեM8AԂD9%2FaTbPፇdMB,C9H@H.АF#AbY*w_I* H .y@LD/OtLa \' _82F mقs6  R aK4 2А HH4A,SGhcim4wrM9e1) rMA1\3>H3d\`+C/Χ@cv ֈ /f@ M[ wh=̫ 8E%8* YA t P%F/(YK('PٍN&q MP$$taAR-$^0L H#1@Qҷ (2p#V"ø.`IC/0`b& Ԏ0xH0n\b(HX  66]"'eU4 " t( ]D`H |mSG9 a)0P=оlyMfHWH@ 5G& #t@=" ..oFnj aHYn1EnHc$L,( kU[ Ahтk@ Ā 032oJQ,S $Z4$lVq \V%"؄;ɸ|5dd&'`>dE)~U[* ȕ&@98">X6ŀ1 u]'dem&| a T0_ƹ%@) 4r|3QG.w \-)xIkA sр%,A>(6B-.bG|V&D-^ PB31 Xy^L 8CC&" )8Z@HM *3c! P޾mC iq[-  `xOB˨[ʳJ13@CB@OHF,O EA P➑{[94vZHu8 A #pc]Eh3}AXA-Nl3AClRzhBB 2?NAeD&LeżPY{L)DZF,EG,!  )$It`Fu EmQ$DP0DM`.l%-`ЂS BYFL)tE!R IyT45F4Ū$bTA[$K0v)M.\L(N#E Na$&iO.\#aO1aJŞĠ)ffbRX@KvA.{P (eDaUDO6A&E.@@Bނ(FbAd#uUہ,XQ5M&`khNG @XddM&UE7ZDTGXwIԙ;AGIEN`ĐaD&_ lﱀGF41[Ayl2O XRdV=牟_U7IF#FF8\]ճh,JY5AY!y[P (F^OYR V۷ӳSUH#` [a|EEs G%9^R NN ~9f_Box$&' I%dQ끨p c =j[Tg&A}V{'p}'* LOpF<FiTk:(QKPTQ$=V')p [ҙ$EKbIg^:Ydzݙ_#ކzZ'~Χ]!달1P[=k\')M۶wL M" hD.|"Z$lku@MrE^<]'ŴX&d ZBKVMR&nď`$AktvbvDOƀ$[chP4EmZD߮ UFމa ve@dͧR$]v0WIT?$BO*Ė݉5Z)`ch٭PI 6FKVAEAnm#7Lo* =) qn&A&lIB&U oKfQ<$*}@=rЁH&02VB Agq81 "pIhQq6xH6fX$$B Hd8`*͆bKSoOC)gAC#+ l}dr1P3\*31$ @p8SW\ҦY5(c({3|*3܀+r@Ig2)@)hAq42 [#X)O1C4q3O ]r $*,q4'A'@5$4t'TE6MK4`\dZD30W(ccX*A1C A8OoT,SATl:6)a4D2 CP??L+԰$'B>'1}S^HdC:+*C7>P >BA ]qD>3ogB Np@ [x8B?$52[HG(Dځ?{uw:{5*m+Bd@uly(,bN8@u0C:xP/bxj db,#0E[7A?[Ks&yIf[x%cv4ϱ%#83Pd;ds77k7`]58d+3ہ7\8{seO*?«x4SAA:3853rAϱ4wCO4𲬔M6d@qwq5@8p1?wK(Y!`2y}ӲDu 6]8^7s8xC84-edk` 83KXAkSOQA?xDV{3`dzoz}s63ă7dc/{} Oc/%zp6 Xp3܀p3W_o<+ONd{:!T<蓇@6Gj iU<(=DmWR$ V C̠B,"TZ`ldDx ` \FeG'~0+ ]njԴ%!@EDO#9*xU@d d*Tݡa.AX#@-b4]g Xvv)܂ !i\X%,LnZ .31րŜmTD6iKA' xQ1$/lCF!KL1 H>y=F)K1 "%b*iMf @S#8O}cP3$#~&1i X&3[YA\j򏁕<hd+b*d@Oöt ;,SN5r s@F*L^XS-p$:.Om qXaŁaƻ[dR`bN<ԘUm\=?ge+\!n/Ohg|JN=mr,D)-5L:A21nмEO[R#? G?66_dk.=8-^0c3T 766ZY$ՓAUne`swJR=U&Ԥ wJ$n-ߗO+{c glEdS|š 2nBć/jlkoVf&5%\+6X Z#Z dOb*&X ^%dE;X'(aFlT\L/|lhcC;د*XB %Tj&H 4Rc~æHh>x "dž࢘0*oclf('H c&@oHN:0qRV$>^S0`oЀca hc+JH3C2)(Ȏ`4/-4a` nX"5054 Eb F @qVh* ~X ;dC !fd`E80$1  gJ Q+͑`dqE7!ĊRH1 } `[VɭLޡpESJb%W$XS/I d@мq%}%{\  |bhp5PMѬ )Ec2%D6L%P<2@2Azr,M+k.`2A,@G1OԦ$AX$&W%:$;ia[^*ԑ.AD24 @Pc4Hs6i4%Pm]XSdk38'Tp,q2.99e,E"@Rbi< 1r(@'UB $(<8[2,> $6CX`)`(VS n%@t@2 $g#<3@ӳ'<@V%@6Ԧ[1+JњJ% %>ɓR SFg0fh$@% 3<ד@SH{2X + &*+XI6NJb{V.Ѐ QMɔ3`E$qX$2q3N!"Ȕ;@L@!6Ѭ(E (>sBҊ.UM3RS 5J 03pP;SU 6d5Q@V#rP 'S@܊%$!%r1RʊXN!Nɫ&;*:O 3 Wd!-`5Dl++2q,]ksWY*Q3O1Z@J`D33Xfib^6Nп4[[X@LM^S;* pD&Ϡ`fJ*]GwPON@XA`">d5reU*l0sWS2g!@.2.J`f`Rf^g^ѕWWbߔICLB#ߓ@4d%yvuv@mm[2AAe R"#6% iMHtE澔]=s]t#ESSQTeL1dajt#hdL!hwe3UbucT^`a-caD Peն"m~g+f@|[ǡe ޴D0-[\AkmxBUHm$~uI V%4iuhwqXB[tKO[bbKvB^SLwO&1j}}g!Ka6tTh{WDS+Ơ*Xms+8Pw ^5u WR vk7__UBQ s]#]ġXv*&S+lr[*R|EW_ yjI+$xd5b%EaoŔiQ[;Jb3 71@K;rc @%190HOrwJ\ D[-"YSE2Z}[@&JՄ%1lnnP_0us!uq6#QN#QscQ %t~$8S!)N+TXOH ޖ޴cPh99{q52|!gAftcy^֑:bW3_Yi 5Q* fPya`vne-8q2Yv"K~ԯŒbPb]U$QBo[[@)PMLp $t~'N*P2"ۈe8CriLyeafYJQu3Y™J `ihwga!U5.9q-ycY ] sV%fhKyպ djPoKb;bO{y% lNok í>fjs )ۯ' 6P w[P`[ԫ3efkaa`5Ǘf ]gEV`#DUU>;0UdàE(s#6^ %^ %FraXē@P@Jz<@H,s^]Br\"@q T6}^ %^`> RġU {Xm02Jɗɟ_?\@SAD$B+T6 zSd #a':+66njar!@ar @@f@@AjA,1ɱIBl ^A.O4@ Ar\V[\Xt߬3^ s!]5 A' ! .^@~TxpSN_҅BR  @*`zA4Ҁ}  BWm1ō+=[N7Sq5gm5Q@f@`06@בP A  ANAjB DR T !aR l`<@"@&f`A.a||`FAc֊%& Z'@. ! PH<` X` ʡL^@ y](A>& $a aʁ (!(  ʁ#  >!FJaR?F,4%8ȢDA0|HaćX$8ė5>tP!H] 4iU. yrthzAxyQNZ9pm4] ńi/ hhY҅-#,5}2T"dmWZxEζt]YOF@MLUd K8A ~&PIPH )j/0 YP%2%.[ tb#ݾPKǥQ&! U|}oDHJ# 9с ACM4 %*6W4Ayh7}¬42G!]3E/* gKTb_Qѹ-@3,؋%x'Á%iBEǧ4&8R.`]h #SHZ fhAh@/R@ H\B_NSWNFt]xC-<1^ Әh41l @.4LDDP(%r@^#,-QGa~9`'Zsp*@FA1Xe)L  '{qP ZhK@T^jtk|7=(>栍Ow|p?C2dB 䉕t]=tr~A7&\x=1D& Y|ADhO=`81 _X=`-]Bt#] @^ 䢥%@2r8 U $,9E?dAEL2Bξ݃t d09ȳ< DMЀ<1 .j18@B|g\ B0>pߋ\/  P/Ԣ@`e=T,ڬbѝ1Cz%Ǽq@8 .7qg cGhOpovU7 6דH|:?0EJPp܀ !8DD7 G8 1Բpxl\.L/^ ri rokϸ!;+=G| @ B Hf|jM5Al7; 0 ru|.p&X ߦb}j_s@ڼǩލ-0yl?9)Ewo('yttz4Dtxc 耽Aw8C"&x+Hd7 7}՗}x}cZDd/;ǀ ,}ׂ5悒u28_oB0nk6 |c}s@+Qc$83DZ0h}BTRcg b5YvׂG}`Gte};jC|gεd(YGWL |p<8r~H5U.{6y9rclk,8}}؂6fm#OcL5׋T}pD+IcȃՃhs cTh{tׂ>8wC?dq#}(~M@Rqh2c(,v}XÏ}RWD9Ȃꈁ 8wx|뱈+ޘ} :h8Xǀv~CDc2sӃݧc2rvzI a[|6#};IIK锠 2oZ!y`ȇV9I+BW4H77}99/~GiDg?88u,03d 4uxox#Vuz zyivx'dye/ Q\b6(C♋ws94D+pI@Bt 0pIY EU?$D8^`WR%%v,Awz7DwDGMWVDp,%0GDffgZQȨw'n&kZLvYmcОyx#9wc2춠zEX '`fR+6xA+`ڀx! H`78*r`-;K5ZPuA7Encq ʠqTmNuVJ x(A#|0nJEH2EePzcr>8#D#sbLvqoR禠 qxU0qTP ڡ@+7x|LzepoڤX4xȄ@dcP[&au y305FnwZV Vcj TcDsꡎʡ4mn` #&܊owq7`owx.zGڡ3W?P{nzx TЁM]%0,oҪc  LZID8Rw|ӕ1(yJK˴M˴K t'350bywo" voMʰ*π 1TڡsovЁٗ)}*3v0@CePg[ge01A_jوJZްPn;npR1jFh`|evĻm\;P`X#,AO( i1KM 뽔` 0T /e੽;+˶  (ڲq `;\&j_.zcR|PAT4W  [auVU% 4: cTP7ޠ v T²;Dw_ q 뭊 SS |7W<-,ѻPchR1 p ; o G06Z񄝥/M|ڶee@ zk` T7{p@G#+=\UV0%ܩ VQɎV+ `;n;7?0'0 j[-[?<\vv=|Nx_u"*+ٜ@d W.U bp y ۴K*v=%O-,VPP ",ȑRa \7/,I`̀k͞sn ғ7 '-N :TC\: '[] 0n͙,` v VlmQeӝw+xe_*>8R|~c$괛 ]IO'M VSڐ1 m5əl7 yr,"~Pu ̒60̶nQՌ GVӬ7 n<q\ h \6/Sw- Z ~TP=SLqXqϓo9H* !r e [8!%!.!֝@` V7]  ?b|2ĞZ}p' >B+Oۘ\ cӼ nW=TP 0  ii}A䢖 8 cpͭ -ߩ}{F+b7=S F꙽H -p *ȅyl2 'DV" 4)[뢿Jeћǽ n@c,4@GaZ4,帒)Bkך`S5D3djnA.48ÀbYɐԱ/BϷ AO>a:ꨦ+n쌶2IIޙ x$X{ƣ!h2 \ʈQ1Fֲr]Ѧ:($ǣ7g\FpjK'%B +@eЂZP , e-O.]< ;D [=w_Fy#tU&@Uy3oLbՈGc D-fdl݈UN0THb j _@=CR5?i@ GdHI2I U'D'cv8Tn-00d/PUy@Fņ# =)0#@P}\ >88 q \$;,āx|N0#?21'CL! P[BIa 2 *jXcdnFW(@erh!*Ѣj.xܠR"  [Ed#SSFL&\`l`f1[-L OhMBl;@ $V@&"X"6x5ޠ>$%xŒ1z2Mxrsa 24o %QT_i҄9GțΠ7 V`uʼn cεbYe"@gmDr7w2i 7Cl@#ŕb` 68+ 4I&iծfM0x( !F-| c̨50|8aHIc ϖ1Ms@8-GQ)1|s %CoJr$xˠ`VxY**!z(XjScW~-ḁAAm`Fe"d"b1eV!BVX-W[2THDLg*:MM<ТHx J]Q @mkSxm݀/(MQn1NV <>@7,p9}Թ$FF͕2an\9֍ r/X{7nr.Jxbf- a/$&;]t޶h8|t^g]”ˤG8fn}}IiBÑtHӞ& U%D٪nG$N,Fnr9;Xϑ[и#9˸>J-H#+C4Ř3 $̚#?@ĸV #IL٘@P &Y0œ D ?+{ -)P166 e683+;y ( 0ǨFkXC)̽j+4<uRJ,JB+~8~k46\%ܛD CSd;u7߱DűB%$\2fDXXfBԳ)cd; w#C`\BF2bYw-D4ǂFϒ.gK5GXA2,#Ip4j3 U{=M$ІGmGȅ+P1p4 PZuSm`C$iF`h ֆ+^:\ȅZЄ݅*0$V]\ @0mdmphp*Fp(d@ F\*88\mG2Є-C BN$:ȃrXAHAF(#  ȃkІ"lH`-Oe$s4(@$:$(iƇ` K LGІ`0X-0\Z.1 @hr$ЅX,HxX^H 8-0 uD˘4miݒ #,@4xmPr]m0 h MG|tV"^0BQ@:@jx^<haxH@bm`k@a9]XLmԕ_"ҥ{ZX Ar:^]`:JDOa]$^]xZ(,8~](x|FjMx#X4`Ch<-Q^\F~^r:l:#%8MrUI!u"Q׌c8PV(S(A8%0QHG@iZȂ`m%ЀZ4%+Un@ mx4>u^<FJkmxkxG4\HHS!YT/Hȃ$H h0oMh/v+-If29Æa6~EnKH X{$`F  4օK@{x:4Y6|:@$4QȂ΂x 7# H|vmCj\RHG`SІ#AnZH=JX] dLM[0"^Pn]Z,$n$@G ]8Y9"=` FlIAjJ(J r(ȅwESS?fmrP(n@^(K0 &z@ASQ?WUWiHhEŠrGhn8^rp#P  0{ Ip@LxS<Ȃ~mU',]P9xFhlмek ^Є wNי+P?Ax*eLmHO1tՄ/ք \khq˫pFPo4vA,m. <5H+zfc:.LqRAL57IjХb HaB m(H%+$4%N8y *,1U/.IR4hjq_9<&lы74x %-ri0L|T0MlSވe R$Z|)yҤ>@-V`)D*X1B )"1H\@Dp h.%`eD Uun [Dc.*ؗ lT$o+`|Â$l!6/y%-(VbN! #*h`&L@. `tLsEW?.yJʒy#p*zMڃ$=` bO^ 0#Wㅕjm`RhI $ F{Rz%aXi5)%A ,dD(#/03{M.I s >UJ.iJ|1hRK-1#IGPhHئb2{0c V|WVjrX:C-3\H`Ki>Xo6 tg%d7낄t&aAh'#tG&\k2K?hM\K̑p0G.hDBk=G,KQLh^ [ ,Є(m~ِade>s,,|–4{Y4=/;N!d9d0Zd^$H,aNuћi>{a $ZJ谲$i`X@+!V3habX`9#G4A %Բ$L2 Bpc11PazUv L p<@ ,Mpa IT6` *1$bL!DFI/7cRaf?SBݘACX2PAtU@#jQ/mԶ"&+ѕ  "‚pS_D4r(p, 3 JRWB214+/\J77p;ಏ8;2e`F*haR&=~<(CKZR{G{R`xmʌ%I_XֽT!Cѥ`/oឹࠞzu,A*cC.4&HJ‡) [}X.|Z1ЕQ6(*"b|n?6Tn݇" B.%]+yj#dk @].w6e~;n"KpI0,)IY_+r+.}k6{0x7L=YD5Q"y5CH, ȣU t}8;on@!j(n( #XƋ;ܮ t)P;~xT;,]PV =(Rb3mL9t#%dDbX* &1Td6FbA"LmsP"Rҥr#F5+2 Mx EvofhxE[g+_Tk\\J*``z hrwHy R ΛpԲlgw.6w(ht%$EX7#r7ǤmhE]i_uS,;Vm79xh $NFEq^icāRI^W o1K9T3.{:VR%y{qw1\r/,. ]o6(.};mƈ&xtlkn7 ̂Crf:NwDF ,HEx yS:d?d;t00i=.,յy+o\XPcMw5AzI=6E/ݫgVxY)TBН_7 @ DW4`hA^ LI =šytETY lVL#X@lDUMY HT8W T 8`) <T@QU E1LXc @ r |EUTB}LLW=DďGH)|)|#Y#Œ9`ĔMI$, ܢ@ْ)8 <-f#\ /UAW ɕٮdIB:\V]Vf,FRnL ƙ4A7;1RZ]tI>v 7]SP0IUx >v|KP)(f@`5RU,v|db T0:V@e''^U`]SXX|kTlD hQ*B1P+"y'TLrFb&xή?쁷_=}!c 숑Pʼ|W@'iKXA*C'S(15^uaGXEW77++y芺(pgc:h_r e |7 |+LB5LBdW(e(iX3Dc8@'ăqB" Ab`Um0Ռhhr+ ?^RBz1TLydNDYzt4A!UT0J5/x10@J"P%)5f(mf7H?XBcn R ;dA&+,j^2kApA&hNg:^攮zCrb^'7 %R~N.%t*X d`"4J)]АƮbWP*(銆RN@1C5Еzf A(&m,ʼC+* L*L *<* *'H"tKLdAX7xChꨊ'tC@5B-m>7bC& dr*,"ހ7 (R-6U.vU|7h&5?X?|ف7C&TCal.Y5>BY ull*Rnҫ"?֨7d*P/hxXKTAXCtZ4|B|T '=BvDlV~Q] 0&i*'W<ϖ@&Y뇮R}AWs>+o6Պj%Ap 0zCvE~Qe@f!kR. X>xpA1Bj8l pGࡹnn!NJ |%/@-"[Jߛ d(B3**tBl<~B#h-I0 !m4ReLlZ@UdmSM2#-2ejUzyk1X6-Rv$͑\}ATO=$* @"8PAHA5 ALUc@27qp "Qx:c`@!A$(" es)&/U:7ïüYO^A@8c t X*\n*XAL]"( `/6SM0A<$373mO7K{3l+0)+E"S#Nٳ@1eUXA!z?гrS4*C(u0KhĀ0 qQ&nGn|9X:;t3FA:vA<3U{3ضZ tm@'d? A|\'-qlYִb YPsTO۴@LY0Gny?u@W2;Tc>LDC<4ӆ7t TwFkP70{wP=QV4*yC03 @4"l0hOLÖp (G\8Xb7bQwOj<45xGx1x/dxI E@'tPqv>$Păo2T-ZnM!tC (S"S'3L^"/{YSbDB! MbA7pymMƀPu3b7ZǁWK9H{*vԁ"3X*CA*Y4wB!1(2p" -qWˑ*)'%TCt7a ƿB-F x2 5$:Vd?ipGONI oទQ+Yt< (?ă$a3H$DX7OB'TA5zp/U'z@Cw r 1j+BԬ'=#?C!XCl,ͺ][$(w+;{z4Ê3wpXc ɑ(zԎPAdçO^*? @43lEO!Ȕd)QQ<d-ffRyoPRl̘,܏n[0'xCl$A03=Oóv`u7| >DUK7Ro8l*|SY;SID~CP*_(oO޾Ae:2RSnôzLba;KnC.]T@+,0 h#O>h ∽16 9(:JJꦛ1pj/p[<b+MMfv++-,KK0CC M N`Ix'=(#112H㹡톨j?)01+7I> q#ވ!g(ㆺpSi 8 )X vk7Ŵ"x5I4l 8zx"0'XS 37bO̮k/*f-n- +`6 . C> Ӻ+0 8] fR2nL_ҊÊ1bԓNŏ jjc/;۹0$/Fw ' 񚤯1>fJqE1[IC:+=KOұ 4,![*bE{UN  QS; ͸SncA; ;. !€;;*rJ)"2 b;iLñI>5zrdb 7\jBYa PX_ڳFKTC > $:bɏE)p!K 8$ʉË8CJp%U%ª㍙I1cS]13Nw3@&|e`$<a)S &r3 x f]bRFHN]2>t@duMSNTds/ *F͡ިF躚$^SʐH$Y}HvēU r,Ͱ7;ꗿA8np1X \`2!l($Yw v$rn֤/;NPNdaBRbMXFj5̻3 i:nm{ LO,aAX@a[0ُ|~txP8ڍ&g L&9c0 yP!5=&N驚 RUR>;|H}bM+cD)}DJN:)tj.g6S ZjFNuEΩYM򊀁i,a~zו T!%)A5 o<L ň-̡Zb>8󱛳#^$8U`p`hA D;lb6a c1K"j{l$ݩ}ŝ-Qb$qL4A+-n7Bva(-eV7"u}G-n @׽Z.XbgZ@I@dAK |0Oaܙ*} \av+^~TӇK Kp*Ç{{eLFyɓ`S{ےÊXi^ԐEnuGDsu.Pm OMmΉ .tGز*?,a |yk놊g=kIܓPYIܒY 0󔙛GZ0;+2G,Yjߖ84lmÊwmt㊈`ܒ e3]Ng:|x5/^AE!©>9{41 wVg_y?z;kx%rU \}z^Uc=CFuhF%~qCO mo{T9 ȕW+9SՋJ#"4! [^9J:ŔcxqL4UH41NR.?qwxs v8h~w\~*#_4svwA+ɩg$)GBd٪7I&d!YU/?J4 ։q7;U'E %֤JtOkֽGl, B ~ HohډsНR1U qC`ZljQmQt0O!+oP1OpGx-;/1%AUH ¡ b ,a."@` %ZήrNL F.lU,P($Gl%q ?|qO7P,JIF lLKH8(%- & a/ {To!a rR2c2]0PG4rQȩPO5AZHd֞16Ⱦ&vda. $^g2: p n "aK?>PŚoxq<W61h/C){MS;wo5X57g+kP0}SLvJC,Lj1a:bJ:: N,C漬"g sђ!! 04*HG8<1Gë1H[n?Ѐ2dLL Ha>nÓ`)<]o=nH1Sm+әnGtGv4!6QQud4!۳>oVdwMg)0ĪHoH3#pjtO<ҕ,x /еXOPג϶l p/5R5XUt0:D`̽A%;T7uR ذ?`+O_2=#6#P"; )Զ2oW5{o _TY4$5;oڦsSdJpK hj$CpjBO:z6s3pH?5?V޴3eA dکVuRNXնDY'vcwj1Smu=Y5lƠDk, dLy.[Ȧs5<7nbQ;55UXw59aqojuVA3m5Y>-vAvcVd]:`/KA~~_  NVީVQ0s=a7vyd?&k#(z;0rPv} k٦A5pl?u[ijp]~#ʹXV+x+ B])=T 4˲r5J:gFbW;;}yN{VVS%z7IȬOZq4? E/NN5fuaVmPL~x+VL$C≹X؋Xǘ،Xט؍x؎X8똎X ِ²XKи9'+ْ8#/5Y!9y>YQ7[ٕ_8HYXQYuY/y%|y9 9=yxTSy9YKyyٜyM;c+ w9Ź빜9YљM1ٝ٠ٞ7ykYٟ#Z9EYyYXW/Z#YYE'SZY9 :٣3qyysڤY{+ڝ ڋY ;٥=Iz_ڧZ8 ;free42-nologo-1.4.77/skins/Khor.layout000644 000765 000024 00000013561 12110237246 020127 0ustar00thomasstaff000000 000000 # HP-42S skin for Free42 (Windows and Unix) # Based on Michael Vogel, Erik Ehrling Skin: 0,0,311,489 Display: 27,43 2 3 aadaaa 000000 Annunciator: 1 043,25,17,11 001,490 Annunciator: 2 064,25,20,11 019,490 Annunciator: 3 087,25,22,11 040,490 Annunciator: 4 112,25,15,11 063,490 Annunciator: 5 130,25,20,11 079,490 Annunciator: 6 153,25,04,11 100,490 Annunciator: 7 158,25,14,11 105,490 Key: 01 010,141,35,27 011,144,33,24 011,143 Key: 02 061,141,35,27 062,144,33,24 062,143 Key: 03 112,141,35,27 113,144,33,24 113,143 Key: 04 163,141,35,27 164,144,33,24 164,143 Key: 05 214,141,35,27 215,144,33,24 215,143 Key: 06 265,141,35,27 266,144,33,24 266,143 Key: 07 010,191,35,27 011,194,33,24 011,193 Key: 08 061,191,35,27 062,194,33,24 062,193 Key: 09 112,191,35,27 113,194,33,24 113,193 Key: 10 163,191,35,27 164,194,33,24 164,193 Key: 11 214,191,35,27 215,194,33,24 215,193 Key: 12 265,191,35,27 266,194,33,24 266,193 Key: 13 009,241,86,27 011,244,83,24 011,243 Key: 14 111,241,35,27 113,244,33,24 113,243 Key: 15 162,241,35,27 164,244,33,24 164,243 Key: 16 213,241,35,27 215,244,33,24 215,243 Key: 17 264,241,35,27 266,244,33,24 266,243 Key: 18 010,292,37,28 012,295,32,24 012,294 Key: 19 068,292,47,28 070,295,43,24 070,294 Key: 20 130,292,47,28 132,295,43,24 132,294 Key: 21 192,292,47,28 194,295,43,24 194,294 Key: 22 254,292,47,28 256,295,43,24 256,294 Key: 23 010,342,37,28 012,345,32,24 012,344 Key: 24 068,342,47,28 070,345,43,24 070,344 Key: 25 130,342,47,28 132,345,43,24 132,344 Key: 26 192,342,47,28 194,345,43,24 194,344 Key: 27 254,342,47,28 256,345,43,24 256,344 Key: 28 010,392,37,28 012,395,32,24 012,394 Key: 29 068,392,47,28 070,395,43,24 070,394 Key: 30 130,392,47,28 132,395,43,24 132,394 Key: 31 192,392,47,28 194,395,43,24 194,394 Key: 32 254,392,47,28 256,395,43,24 256,394 Key: 33 010,443,37,28 012,446,32,24 012,445 Key: 34 068,443,47,28 070,446,43,24 070,445 Key: 35 130,443,47,28 132,446,43,24 132,445 Key: 36 192,443,47,28 194,446,43,24 194,445 Key: 37 254,443,47,28 256,446,43,24 256,445 Macro: 38 28 01 Macro: 39 28 02 Macro: 40 28 03 Macro: 41 28 04 Macro: 42 28 05 Macro: 43 28 06 Macro: 44 28 07 Macro: 45 28 08 Macro: 46 28 09 Macro: 47 28 10 Macro: 48 28 11 Macro: 49 28 12 Macro: 50 28 13 Macro: 51 28 14 Macro: 52 28 15 Macro: 53 28 16 Macro: 54 28 17 Macro: 55 28 18 Macro: 56 28 19 Macro: 57 28 20 Macro: 58 28 21 Macro: 59 28 22 Macro: 60 28 23 Macro: 61 28 24 Macro: 62 28 25 Macro: 63 28 26 Macro: 64 28 27 Macro: 65 28 29 Macro: 66 28 30 Macro: 67 28 31 Macro: 68 28 32 Macro: 69 28 33 Macro: 70 28 34 Macro: 71 28 35 Macro: 72 28 36 Macro: 73 28 37 Key: 38 009,123,37,15 011,123,33,14 011,124 Key: 39 060,123,37,15 062,123,33,14 062,124 Key: 40 111,123,37,15 113,123,33,14 113,124 Key: 41 162,123,37,15 164,123,33,14 164,124 Key: 42 213,123,37,15 215,123,33,14 215,124 Key: 43 265,123,37,15 266,123,33,14 266,124 Key: 44 002,177,53,12 003,177,50,11 003,178 Key: 45 060,177,37,12 062,177,33,11 062,178 Key: 46 111,177,37,12 113,177,33,11 113,178 Key: 47 162,177,37,12 164,177,33,11 164,178 Key: 48 213,177,37,12 215,177,33,11 215,178 Key: 49 264,177,37,12 266,177,33,11 266,178 Key: 50 009,226,90,12 010,226,88,11 010,227 Key: 51 110,226,41,12 111,226,39,11 111,227 Key: 52 162,226,38,12 163,226,36,11 163,227 Key: 53 212,226,40,12 213,226,38,11 213,227 Key: 54 263,226,39,12 264,226,37,11 264,227 Key: 55 011,274,35,12 012,277,33,11 012,278 Key: 56 066,274,52,12 067,277,50,11 067,278 Key: 57 128,274,51,12 129,277,49,11 129,278 Key: 58 189,274,52,12 190,277,50,11 190,278 Key: 59 251,274,52,12 252,277,50,11 252,278 Key: 60 011,324,35,12 012,327,33,11 012,328 Key: 61 066,324,52,12 067,327,50,11 067,328 Key: 62 128,324,51,12 129,327,49,11 129,328 Key: 63 189,324,52,12 190,327,50,11 190,328 Key: 64 251,324,52,12 252,327,50,11 252,328 Key: 65 066,375,52,12 067,378,50,11 067,379 Key: 66 128,375,51,12 129,378,49,11 129,379 Key: 67 189,375,52,12 190,378,50,11 190,379 Key: 68 251,375,52,12 252,378,50,11 252,379 Key: 69 011,425,35,12 012,428,33,11 012,429 Key: 70 066,425,52,12 067,428,50,11 067,429 Key: 71 128,425,51,12 129,428,49,11 129,429 Key: 72 189,425,52,12 190,428,50,11 190,429 Key: 73 251,425,52,12 252,428,50,11 252,429 Macro: 74 01 01 Macro: 75 01 02 Macro: 76 01 03 Macro: 77 01 04 Macro: 78 01 05 Macro: 79 02 01 Macro: 80 02 02 Macro: 81 02 03 Macro: 82 02 04 Macro: 83 03 01 Macro: 84 03 02 Macro: 85 03 03 Macro: 86 03 04 Macro: 87 04 01 Macro: 88 04 02 Macro: 89 04 03 Macro: 90 04 04 Macro: 91 05 01 Macro: 92 05 02 Macro: 93 05 03 Macro: 94 05 04 Macro: 95 05 05 Macro: 96 06 01 Macro: 97 06 02 Macro: 98 06 03 Macro: 99 06 04 Key: 74 047,147,11,19 048,147,10,19 049,148 Key: 75 097,147,11,19 098,147,10,19 099,148 Key: 76 148,147,11,19 149,147,10,19 150,148 Key: 77 199,147,11,19 200,147,10,19 201,148 Key: 78 250,147,11,19 251,147,10,19 252,148 Key: 79 301,147,11,19 302,147,10,19 303,148 Key: 80 047,197,11,19 048,197,10,19 049,198 Key: 81 097,197,11,19 098,197,10,19 099,198 Key: 82 148,197,11,19 149,197,10,19 150,198 Key: 83 199,197,11,19 200,197,10,19 201,198 Key: 84 250,197,11,19 251,197,10,19 252,198 Key: 85 301,197,11,19 302,197,10,19 303,198 Key: 86 097,247,11,19 098,247,10,19 099,248 Key: 87 148,247,11,19 149,247,10,19 150,248 Key: 88 199,247,11,19 200,247,10,19 201,248 Key: 89 250,247,11,19 251,247,10,19 252,248 Key: 90 114,297,11,19 115,297,10,19 116,298 Key: 91 175,297,11,19 176,297,10,19 177,298 Key: 92 238,297,11,19 239,297,10,19 240,298 Key: 93 114,348,11,19 115,348,10,19 116,349 Key: 94 175,348,11,19 176,348,10,19 177,349 Key: 95 238,348,11,19 239,348,10,19 240,349 Key: 96 114,399,11,19 115,399,10,19 116,400 Key: 97 175,399,11,19 176,399,10,19 177,400 Key: 98 238,399,11,19 239,399,10,19 240,400 Key: 99 114,449,11,19 115,449,10,19 116,450 free42-nologo-1.4.77/skins/KR.gif000644 000765 000024 00000016727 12110237246 016777 0ustar00thomasstaff000000 000000 GIF87aSH@@808H (hhHPPP888000(0( PPH,S`$dihlp,tmx|pH,Ȥrl:ШtJEجvzxL.z f|N~gybq~iX|pdZ̹аԧؠܙ}v+f HLXp!À(1Ŋ.bqcӁ Yn$p&OvK2˖^Œ&sf6Yl'a> *tѢ!xt)N)*UVZ!׮(vŲf3M˱۷pʝKݻx݋AHNwp|i^lI#{<"8`<ͨx>ctjְ/qڈC|Z װۮYHmȤ}f\ 7o=;ܫ]ҫ۳>8ae]{V\b1G&PF(!6Lh^nx ~XC"@4Xb *Zb#2ȡ6 (CBIFh0أE%SX=8\v\肖_.e.n& igsvž|`'Ly\2gh:htz))o6z(ڥnI6)[f>ꫮ.𪬰*)`kj+(+d>@ ښBlVۭc;n|[Ȏ:⺪. 칷.n{ l , pG@lW0/,2$[ 'q'\%\0tZ,107ܤ/|(K@s2LpL#:tp~ tH5. Hs G7=u<tӽ@vM7z}37ހ>/>m}C>/q>7ޛm9㟗^刧8׎ꐻ:w-޻ߎ#?g{߽yӬw x/ o_nr+~p߾矾_ iOKOR oT`- ߠB:p +a I*t![(Cy0W5 s({ڋa 7 &:O$HE*JKxk9h[☾*qMd,#Ϙ$W4G*J ʣ> I*L"F:򑐌$'IJZ̤&7Nz (GIR2Ad*Ur$6Iu|ɏI>g}yS?oߤb$Og?{W_wO}'~w~PhV~؀~GIG~gzW؁'SP#I(G}'$xW{}WJyS XU H, (w4H}<'xeOHvHLhWHPׄRoXRUkd؄wH(j}8XpǃȁxR3O.FHз~9w#8t}Ȉ%%{zg}8uyH8o7Zĸ!ww7#Ǩ#Th%x^w}nj茣x!و{GD[:'[Ս!ߨZD!O"ׅ]ux\(&vWhxHe\h]uD*e&Y_y___ɵ()'92_xbE^%Z.&afc)b" af.5`)V,79,&U8+<7F%b+6.?)/pb2bbTc3̿~ټY [fi <\ dI8lߩmIk)|»&lY+),3|æ$\0-=\;66E,(7n<\?SrqV@T &[RrAd b\D@A)Xm@Ni3gpL\lp,t6Ǣ>@,? ɟ{u|l3O7j|/ɓɝv`ףau\vOʱlE,Uyg; SZ|Ȝʼ mJKdjͼxX\,ԧIk61F E}qz~F山(ڵ^KJ<ڈ̚Ě/G,-L T'~R~"xKH * ?˱G%6ݬ@~gˈ6{;]Gm4BM|eJ1! =9۵==v[=8R}]M"em]Zˆx VrECxllTM݋6_믇]sxیM/`[V͛-ڣ-ͧ pͬܤk˶-ہksHNj۵˱oKhuKk-}H-&-B̹ ܿI;)˺+'}''+޷Eޑ%_h)[=)^+߫Kޓ+Mչa]N\K-ˋ[,n+-K܋ ~^>]M a ;ૼ!.>+`%l+fs{L57>*.t82,"KI\yt9`xȊck,AZ,s^fe,aƣ鐮꫎rNq<ŏŦNǮ~ŬN \|^ɣlʘ\Ȃӎlz˲v ~gugʯ\̼}~ܭ]~a싦}ٌb}MlT,(Ϛl /φ7E]ЎULj=}}z팅8hT4o ~ZCk2ٝn讔xE5ӧסr@.[A K4?PEUCsQ_j:'Ҙ݀{ҊV.Ub/ bF_w<ªRvМX!]zooؿW:ԢhRv-I]G?m_έ/Ϊӭm>[OݽϷN:ϸǏ"ڿۯ^ؿ%޿\m}b( [\ #)B'+4>.9&uޯ ݱtR(xp /8>-9n~md;_Y\Ԝ!" e!"e%b'O'@#jK+m-mnmoЯ-/*lbuk˳*rvs0xP98S+y7>3}9 Ǯݷ_0hHa IhѢ-RxQǐCnHRȓ*=.,$̘2gDinD8'\s'OL8u$ΖG(hJӦ IjժZRz5װ]J,٩ҲMTrcJm[dU/߼kzLչPMmc!;p”bk8ϟ@iӐS&~+޺Ptms{[7l9/nqԷ#orʟS ַ'9؃/Un<ӳo^˯? 8 x * : J8!:p!j!:P!z8"_)x"b ¤BL3]ӎ<#9^6H#G*7ژK=DdcZ IB%[ %i饖A~_Qyc'ti'i~ (_&S (2fih's&Z`d>)^2)g "BJ &j$Aj~ 雇$&]>꧗Yc2YdΙkp^L飩碂zj:+'zk颽;2).٦+:Lf fZj˭QǗH)7| $H.1w2(,sͰ݌s=/s> =4E#y!mK״Au{RwWq5z_4`K7vYkml hvq/gx,@~gUQ w68Ox ?9㊓7 ,戣`$:升S3h^VxU5Jqs&凜퉘S h4߼ tL߻!2,آAK?踟0 ? ۟7v_A>QP}1B?h͂@@50#1x0)P:Emijӣ52ujL*թեL5*Rz*^*XJU2ZT{TQ)TUεr +^ץgUkYJU.V5_Zӻ"6re^#+Yui_:̾]a1X,jSTJ,l[׵jcBVy-nwY̲Ulhk[µ%co RWƕ-\{]bkZJU+ng\6ooϫ_Uׅbi^j0[:Ҷw~0#, 5;free42-nologo-1.4.77/skins/KR.layout000644 000765 000024 00000003652 12110237246 017540 0ustar00thomasstaff000000 000000 # Color skin for Free42 (Windows and Unix) # By Jeff O., coloured by KR Skin: 0,0,276,327 Display: 7,20 2 3 688068 0b2327 Key: 1 6,72,44,36 14,80,28,21 14,79 Key: 2 50,72,44,36 58,80,28,21 58,79 Key: 3 94,72,44,36 102,80,28,21 102,79 Key: 4 138,72,44,36 146,80,28,21 146,79 Key: 5 182,72,44,36 190,80,28,21 190,79 Key: 6 226,72,44,36 234,80,28,21 234,79 Key: 7 6,108,44,37 14,117,28,21 14,116 Key: 8 50,108,44,37 58,117,28,21 58,116 Key: 9 94,108,44,37 102,117,28,21 102,116 Key: 10 138,108,44,37 146,117,28,21 146,116 Key: 11 182,108,44,37 190,117,28,21 190,116 Key: 12 226,108,44,37 234,117,28,21 234,116 Key: 13 6,145,88,36 14,153,72,21 14,152 Key: 14 94,145,44,36 102,153,28,21 102,152 Key: 15 138,145,44,36 146,153,28,21 146,152 Key: 16 182,145,44,36 190,153,28,21 190,152 Key: 17 226,145,44,36 234,153,28,21 234,152 Key: 18 6,181,44,36 14,189,28,21 14,188 Key: 19 50,181,55,36 58,189,39,21 58,188 Key: 20 105,181,55,36 113,189,39,21 113,188 Key: 21 160,181,55,36 168,189,39,21 168,188 Key: 22 215,181,55,36 223,189,39,21 223,188 Key: 23 6,217,44,37 14,226,28,21 14,225 Key: 24 50,217,55,37 58,226,39,21 58,225 Key: 25 105,217,55,37 113,226,39,21 113,225 Key: 26 160,217,55,37 168,226,39,21 168,225 Key: 27 215,217,55,37 223,226,39,21 223,225 Key: 28 6,254,44,36 14,262,28,21 14,261 Key: 29 50,254,55,36 58,262,39,21 58,261 Key: 30 105,254,55,36 113,262,39,21 113,261 Key: 31 160,254,55,36 168,262,39,21 168,261 Key: 32 215,254,55,36 223,262,39,21 223,261 Key: 33 6,290,44,36 14,298,28,21 14,297 Key: 34 50,290,55,36 58,298,39,21 58,297 Key: 35 105,290,55,36 113,298,39,21 113,297 Key: 36 160,290,55,36 168,298,39,21 168,297 Key: 37 215,290,55,36 223,298,39,21 223,297 Annunciator: 1 8,5,19,11 8,328 Annunciator: 2 30,5,22,11 30,328 Annunciator: 3 54,5,25,11 54,328 Annunciator: 4 81,5,17,11 81,328 Annunciator: 5 100,2,22,11 100,320 Annunciator: 6 123,2,6,11 123,320 Annunciator: 7 129,2,19,11 129,320 free42-nologo-1.4.77/skins/Michaels-HP.gif000644 000765 000024 00000251774 12110237246 020520 0ustar00thomasstaff000000 000000 GIF89aLC<8QQLkkoo!!!"""### &)'$$$%%%YYZWXWlG3KGBA<7QQOQSS&&&XYZcda[ZZhyhVVWF=9WVVUTR385 B=9685^^]'*)'&%G?;SSP77354+''&*&#362')' D<8 562((#+*$ 8<8<<6+(#d>zhZ~|wvD&,*HB< TVWSTU/0*?A=;?;JB>AC?)-+GD@@@;>?:14/*,)+*(н ϐt0.(&#BB=πg.10Sӫ:<8J *0. -.,AFC,+'NNM  D>954/ "'850.-(<=:972782993'$FFBGHE-,&52.JKH41+KNL.*%/&!/0+20+'&!22,=:4=:8PPO<61/3/($ .,(-.))*' 21.9:7/329;8053)&!A951.+LC?87244/A;74.)"&$.+&%%9:442/343/2,661<742200,(" :8578582--/.S4%!%#+(%10+-,* 01/$![\\F?:#6420.+$ \]\ #!H@C^ > `" 3IgI2/# PЈ< @qHQc`,0Ԁ`@;f@9;qm#EP  \D$Ab ؅,דJoņ? g" p2!W()069"<#PM f(G(b!8.l B $ JB̕0(<p$H'; 3uO5W1 V*"aH7  ,`C+q fԃ0Ṙ0Ub$AE6ac!BQ l"9$ˀxH9D6@ rcup$2ܢZ E6f@R" F ^ #0l(p($BE` Px#HE JA`D p9W"ڸ 1+T#xlЁTGT8 w3JXF"E**d#w1^` Tp-W('3lE828h I;Pe@ 5`Š @\P p 2P P Qpte`6P`4`XƂ B05hA F`g@pݐl`n;h@ B؀ РYTq _ @ >SYmp.P}gPŐ 0lmowp  w @@ pp 1m0m Q AP@ .   чz@ 2 Pe7APpP`  X`Z P e@XF C?7pPT2'  wAXA ue`.5\ ?HrUFWw` ? ihPR2rE \ PP`? @` pAP  2 Q WF( peP P m ư @ @ ` @ WP|BPzF` p vU `s ` @W` PPPp2`8 mEP w `F0 ݠ 0'y'< `@X0m Y` ͐ 5@ yPpe' wpFpp (G ` `f X  .\eP P 9ϸ <;PkWe  QA Ͱ _ tV PP "PĀWOG f w A@ @

L}fMm xK 0 Lj6E6@y D'Q 4@ D@Ճ}A  F@ }X`rP]}P YE@ZLm k ` P}~ĖPp EP hQ . g ͐W u'`. z0` _}˫ Dl z @ n\wPkX_ @' f0xPpϠ A` P@ `P P{P 2 k W ep IFP `՟@ PG T p0E``Z@ڮ5p0pX@ z`p5>`q`؀Й• D}jČRغQR3;3`@D; l@RJe&T3Ah)eL Jm᪄+KBJU#"bAj vY2RrUR<⌁H]Č"i E2$"ٰ)3ՕTEʈ("E Tn"]!L(u㲖,wȸJ1PmBōKLW;X2d,( Ɉ,) Qbɰ2##YugPgR: f5 蒌DXĥXR"Y2E!슥DXv1-#_ܻ ;H]X$" AĺH7_"VĈG0b@̸$3BhIŐlRJ3*H PH2Z_.Y[Lvp,:ᒍ&@B$ ic2x K";r ;A #D#-jE(Qc9/b` b"%g5…n>JjȦgHgPsTj$zaDQ #by_0"D\Xӈzx&la&lIl&La[y&DdWb IdDNl`ƌH0lb%UDL&}}&}KqLL fyr0c=^DL^hd_x[m1hȁc^ fijYfh!G ZxY⛳1ChCt%g R9Ĉ2/!e!BM#CإvC>|gDpxhR?xr-oQ}uCw׾qwse7p9#f0KHE B( ƀ,bEm{[($`D@;p]jE|x!CXB#a OX p3d"jhP;a}C QC$bxD$&QKT9PlTP-4D @ F!A ? vжH|@\A2BD  H&Q{cG@29f`A . 4ΐUC%*]Ab*A4d2t'X'v4C'd`,నc%kY 1R`@:Z@_H"V!B蹂Y {X 210C ϐ(93aԐh(.Ae {ZԦ5,A ࠞ甒!t vZ(j{\.6 E  [܁fCpR`80 W r{.j@B$cf;`Yfl7 2P)P%A@ |`'X fp`GXp-|a gX60K:d6F2$J*ԡB wT$ M>v& |@܀  x#._`?Ô1)C"b}enPy󐅌e1w؄#\d$;x>3gk|>Fe 9ѐ@ ɍ󜷼x7!'8!ɉ zT@Nr?Ё'@6PKZx47:hb?G~'tF5:?zˢ~3#C+xXxF7,0`I"!Kt4>A((T9O+7>(c(xPqe<|N> 8a!x~ `xK,@coDfՏ_Q_x ?e:/y.p$yFd<؄5vrn\@;{\!5?'D@@ wJ^x]4֐:e6;72@K/W D:>*A1KFpb-pq(.(&x/@C@@q^lXO?&Xck3;b3#Ak69XPpK(RDJEfH,EpF1f;eA2ܾ Bel=63C=c{8b#d4 :r܄C6C2'(;?UsNk:ҳ;+Mx?nH<ó8CG3W_D|88s#B #Ac#8FG$>8F>+ [IIIIIII JJ,J1h3Z,b[ %z,l?tI("kIZ \KlK|KK\ˤGTC] FC7f3.C]8.(ĖԅE1KLL̢Luh.PöZg0p8(HO(1X] pb((@ȱ`=L,NZW(E`3?R`i/B ZE P `W`Z,ZpgPP;m:+`$HC*(I_X a/PUCR梂G0zC"R QCH-b>^^U+\`"0AU7Ʒ.8Q(Roȱ.cNEO):(Za5)1_`Md^.^Z%ox:-WΆ/P* B Bem_QgxHh[8@E8J|+mgL[Ρj]`_Z:\;}/p\A@F#vu3e~hL[ QWA3iX+h們`\,&hi4$hBGHfP¯jQVs(bg ]iLМ*DZ(J c5W1KuEVU:XB@ o+#w7]O8W8 (b[D7coD\@FCUMH* Iα;ks_^%PpUpqA(@TNЅ6DJ1S/!2 (gGe֮/Ww!hI(gh. ,sv@2'^Փ *)l1R("H֏/tvz7y5T蘁" Js"u ( LnzNluGa E|}g< QsdS/k倓/(ho(PJ_b*A0;𱼯/Fs<V@RI9ǾowR@03袞 ڷ}ݏpWv]f`(<1Z(bxt/tQN!~%YhC>tг!ԨY DAr5(ƌcRi"G,i$ʔ*Wl%̘2gҬievSƌ9TT|pLZ؄,삂(Rr9 y,ڴjײmfΝz4O2HQ?MJ*/(pŅEPCE r36r@A]P8CR r~V)(%3!6$IRҥADDd2\17+9ҧ$=H3R)J%#R LLݹ@\n̒qƙA)} 'h L܀5D O"Aqp#7> |R"0/v7E=B OPP:F1N#70=nu&|5 'x$M(Îo@MD, x0юߘ:lHF::aqc!?M@ pc!kщͼ#HG֨D;*KA ! doP gpE6AmT؄& kD@@"TT"BXh4AZRMn Iƒ>x)7r?% Pc̬|ml$/dOT/槿~Q 4@"WM(1'` }B d 8;&XƜ@0,<@dTwjUg!P/}鄑b[D$P ,, fm0_$#]$ K Th E3ю~4#-ISҖ43MsӞ4}R<E!ql䶅:L]P"  &׾5-a>6d%wx!,P"<QJ ш3aG`Vj>7ӭuF] V-1@>@BCuHۻ3?].Q LXnQ )~p\ !p򕳼.W]A C E1!^Q J4 KtH E^3.o%,A D4 eThGTI2&цT1nv K4yxlV#(])A CA!߮3% ,;(O@j aBA-t{?=SXjP3lxPĿ 6Q89Rt_v)a2< mHF l[CȆaI\@"H㓿裋Z.*0c;-ъK!X`*(C2`X5`C`',1*^(A3\!8H_ ] 190 +Ԑ [ST\`@B@A9KV]Bx+%`;l9`A%(D+L%`)`D!]D+@nK%\H"h֡#>݅"_\DB+̞ `؀4\`B P $"+]\8-֍"zY+@ 2.#363>#4F4N#5V5^#6f6n#7v7n]7< ? U A@% *BB#A?#@@$AA$B&B.$C6C>$DFD:]XA((FM%þU 'R(4#`P >A $LƤL$M֤M$NN$OO$PP%Q]Ȁ+BX( l;*2(C3@@!B>c+%YBȂ+Ud$B*$Aw \@ #dYfYޅ93/(|la`BޅԀ7@A!$ A;+$@)!AJ&%'>(  ! !`?с)}:p$C!@ c%$$ho3.-PB%@_|B|B$##\@2ځ8@^EPg1t3@xz(9\0C iAsT꓾#e\-¨2$,9*(pA$!XJ߱ `AB!,B $$H,J*~\*$*H@ $ԘLBg.+l*xjB9PH @:#ATMN+"l,v+*TT*ҫi B0;$Hv0@<3+r$*lT*s(ml mDD*,+|Nā۴*.+Th*PvĢ$qg*1!1atB׭|B ^ndj2k~tHJHTNԭܚv-x-$.A"-/"B"Aߒ"oRl)BB"H9;J'2:|)X2b*@NJnffB/Eެkv/lx+ "kzof.+09@3"Ȁ.Dr.-Db@B$XLPspe8k .۪áH#ݶ[/ځ9,ª:rĦAR* ;B.)+d&Wo#XA&BH: B Z4 ۓ/*t+1"ej.+#201*(l'ۯ`d(S-Գ%#_11X-Ҭ)"bAz.Q&-5({`Rrm,%wj8pNP-`A$gAd,\AX3lNj/SKJʍn.J4mtCPo&r,ﲂ#(@Xs:p(4þ7.?w)DMHR@$b)F)edO76{=rIh>@{#}zA'> 0)?A[< 4DC |oˁ<pEr@О=oy C8-id<8,""@H#Evȣb@dl!FG>,0\؂Q 5b Hd̈2*bÏ;:t,*"i5idTjY3@( Ƞ , ( 4htP.(IL E -ȹeϖ&,(4ǰGC(8$XPq9͝?nr(\Z`Nb 0{Zܸc>Q8޸a*D4l_%9us<8,`BY`Ѕ14c 2(tc^!7@8T&9b,i :0=-1,́-7A ta,`{@MIrhhD P\&t ^tC@)G)$'#ш"\;,X PFq^͡cS2\ 8@8[ 8pQl|j 0a)a`X@!(p4@hЂ'@iPIDhAyop9r.@Pahт-ȁd P Lș0 0Ph ܦDDD02:xn0pyșvI H&2BQ J 3y36! EGr$"ư (`s"+ QL:be@(w piNM,B`!/D!D%Yz ^P,bpurd"$k9QC ql@, ޤ0h!Ds13z0x:dSz̍n2$sЂ>,`MkPNC@/P c09 0n؂D@$ b>5\@ `=?'Q$q'B,@*H@xRa@̀(EuH:u *>hBv@M0k" =b [+%ߐe< (kݶFO@*Py$tZa HBtn@RT L$B 4f @D`&@bOJ1*j `!h l: ' Q֤D娈hZVT!0L~a fxA q . ] W ҅R4b+Ġ޺EIJT# r$C(@`@bj6AXn2or5\A T N ' %@tAjҗfeKA @ ڡ.@&n@xs@ @R)/Q( !wfP,!pA AAV0dFA @ l0&ѶK(&\016,sn4 ' | J^%Js$ N2) 8" 4(qϒ Xl"Z' 4` Qv') E pdv*|! zB!dayet.*7 "&Of, Zx>6e>kdp FAD F! #F#cl !9iiT.:袟 \We>ܪ4tnːA(,U84 L0IC@ `뾡 R' n,A.P@T`m\mɉɩuw뉎 ƬGP ' luB4|B` p)(d2'et dHJN| IsN+N8 n!ڡ V.[*,)X/6d$D f rYiSNh\r, wSFA N  AT0 &i(ti/HI>Nj @̗Э 0죝6N`_e ZM ( F!J> @N([Ly$:@MhQ( &4)B&c-]Onp \@X (*d)çIm@[&!%`Bk4 V-#Itj3O$B f~ZV~QBC 8^[X/% .CbL\Ɨ^8ɕti䊪ztdeS@#;)#̼z66r@?)FI4-o䴨(4cS̡E a~: {(VE~ *5"<98{ $5]C::jY&08N\ ,v+mfzF`  {/w47)#/I7XDP% cUi\:H4J4),bL<Do> ޜ漖ܕcj&,PSb9I)>&{(,#TP|E,ZE|-$Rn5~V`ΜZf6A> c\7Pj9zCހ@ u`'BAUVi]!ɩA@yW h/w#Di%  K힩:áwnSXsCB߳z^)=?i ncPtaj8Ccrzo<>R ! ډ\]:4.B*DA7s>ɅGUB[  @AFv n JRZ &AoBJ7gJJQ]@d>9bRpFJ4NF80&Y_ 2%!#E@C f惺.LSa#btAREƧ#@A3?$: @C:N8DO(8f@`*֡d a7 $oπ)X0  ĉĈba* 3`BIYJh,$ ֜\ʕ;BݞQs3ꍜ-y#S9rF`,ϊ -d`Dxp@[X3dX*:ll+ë"!Jd(27Dp!/茢 8.u=`z Tf`\h@CەBC6jթ_.`W:7pW\yQCȷ>W?_Pli_#`vq&B{BEhkpȱr(K;UW ܁ !H(P;`B `h2 AP B>dB*J!d@6{\eZneTRJcJU!d6qNdtAE`A"WHf.h> iNJrJD%ΌXAv5ԈF)fLE*1 rGUJkފk:! bRC8;:m0Dd($Q#D"nm~ 8SDWRC=!$E@L,RC*@ eDcV`ELpp /p? qOLq+oD( )ɬkb)WP4BPI+X'93s> tBMtFtJ/tN? 59fYRD1dnA#X 18C-m(bI*7O wrMwvߍwzw~ x*r.(\#KDe4{jQ2NP #6;4xz袏Nzu_j @K#$BD!A?X+ az|5Q%D%%[ Ep-;@ $p a_mZ&laRERo(!J8" 5AC'$ }9{E&xȾt(YLCC <!FX`U6a K 0@P$`L | $pUdBG# SHTJK g#@tx<_H` kH)Ӏ%آx*PZxE3raf/20@02 QxE0Gţhe'L4' J'%@6)8A!, uA Hm8Ұ4k CA0 7r G@C$P2AiP*J upCq2t—{CER](& U4t$HQ4A x S{E@`B Jht!*TK(e@e 8i9R.!xR#q("b =|AB(:qG|b )1#(X,0J £M 5(=  T 3iT,= 4nIe}<: U1 H,hp\A(3p!߮5,*$*[Tá)%햡uED ai,Y8xPT@@@`+AX9l4CӰ,!&OQZ]jjWpqPM8D./fZkqhbQT\4{5$6 h__ hE;I(!K`(O7(!H^+f EEb7A85.&!lxjcYd9֤Pc 7DF]H_x )ш(#u#LiӕB J2h`|~+W¦!&\ `dZ'(iHS:n=؛Po)#C*X}WI[WXf b:0_0TfbEQc0>aX\- 2ɣ4AC]:F ( QY ֠\<jJFU׈(ݐcTŪʫ7g~5wSC2]F_8 R W0F!* ;UJA p F|NmJh C-PPD^FPo&KPro肱Nmj' [ԾzpjP5~:*A 8C;ϝ8ph0y\^> D)e@8 =_P@ bF(qϰ 8oeB8M! $2E.q`B#&%@K /.p_.|j[h Ë4E)uEF [ DW U'c&E@ p΀pLd7K ` ،LP 0@0P X Lȍ0 p Xznj S78ȏ،ȍL ~3 Ўȏ7`t`Ȑ0Hi{ 2p P0 t*T@ e B@ hg0|30 A`\ . 7zTz0\070 Bi /P 7= 0|1xSY/P Ѹ7 5gi< 7 M`g-0L Nh@ FBD8R_ ``ד`0yOKLH61}@MPl70 o`&g `7=pQy0,pTvy78 Ai1rM|qe0z󄞑 ٝCy9vpQy7Spr*`i167E R ; =@ 9F𚅕3=`u@p0 Qqr٤M*o0 rr0vpFr |S*sopʘ7ZMrT*o$J7𦃚Jqٹ[ڡ- 7  -Tr9|P**0ːw0"j@0 IM zɡIZ 7p6Tp`~nav, P E@g g <u֠0:0,,{@-p0[0 x<`w ":7 77J U*JTY䰟Z:! 0,,:Y7ZP{oۉp`j{NvIt @:,2;{6 0+!k7S0a :x` 77[ ,jKCqiZd1ș32`V@ `C&#,2WQ p /@3u `.:`[_ojq +70@ZВJ70P-pڷJ7SQ @7@ JF9qS `7 7Pf;[t3֩0`P;oj=@ 70  IpfarEp ue"p\ 3tw3 `k+-0k2J |S3b0jK;-KڋPrep4{[Я,|D7SXu@0 z - {˴oo@ 7L@]jc[˟ʣrlrPi7L@o{*{k;,f; 7Tst F2pwP FQ@ @0`W` V ݫ 3Q+ ,ڲ ]`[.\7S4_-p,:[p+;p kѿ^/-pc7ar3 @뷵:𲑪[p^LO*"--ԲZ*Ւ|*9s ಄-*ZJڞ {)p Q`w 'r * `  I{ +³<0$ 7SPJ `,| gMk<ʧҳA<=5 0  {k=5u _.̍ @`;g=оA< rppΰ6,,! GG `.Jl ";(6 Eur W@ &0p0lmf:@A\9]0;P̉>5L_Qo Ppop @:O`MQ0܈ P 0cn#x0[: ~3: b: MPn=oJpP @@Ss)e0P#_R^A0[ 0X 0~p@0|poо$:s3[p`0 ^=>>Q0@7Pp bP_ NO7 {<{MP l@(ӿ[^7Lspxoo0_۱bpp| `)fo ,bo^;_[ Q_B F`3` @ps2^2g`B;͋|ӰZY+x疝$3=uPS/|0{( XQ")(peOh! A8iŃPÉB`heb@X044ǤD&*V  .&ty3P4 ppíPPg7{FS -S0+ހ]K &ʘ;-Ĉo qΆ^H-JDF"gb3= I=@`B,@/aD#s>ڡ /X0]P(kc2`!>m8c`n (Jtp_'\$ x{G`H5x`!6i@ [ h ($84F -!6Qd,MY0/H-(]U"j1XG#p d8Z3(Ha)+P ?/ψ†C0P2 ҄%eHpfD#E&p1X@ X=4JX@氀 `B^VKL`/4U8 1 * i#)(LPNAu` C$&'!Q[ZQBYiBQêԄ** _ d ,`#8)h Ša[XF\±l+ A0 ܡ֜85B BT!QB$yC00| DxSA(Y 4!7Xhy:sћ2&&yhY4W y,R-|4h(094 2lR 4piOqR)"NMbA^h ̢=6jHCIܥEܠlv =M߀, ،qB]%`B@ woP>WqhcׁFX+!w'Ȃ7& ! D$A.0<ȶϣrQ|aͣ+(a`܄@P ICuwe[`{@P 8 & MF5Ȥի#H$÷7 _ MkN3 [@sR*Ȁ a:R_hfh# L$BsX@GL+ D1GXa"!x0q}pX84a\w `8 e _(0 ހu47(ʑQ hu`-/qBBR!_3 i)~>*@Q W$3Prљp;& (/q6z !(ð!BlY"lt`W P1Ѐ @ \ B0,p3`8:h "j(@C8yHiYcya ΉAgct &k  $xˆB@; X*lB$xhp 1W br9>$:JBURO V"Ђu])^_t/,ءdHj5(/?+u *Cosh8o E@TA(+0 Hĩp^tr9g*90 yEj`' EںpkIV,'dlЂ= ҀyYlә td*>uT1!#Vw̧q22aЂ ؘJriʯIXpP#3'`iι`#Ϭ#18g8*h(qH&EhJ (3 VXKğ !8Wgse6E\BAbh/JBl:ddr 胍!Tl,48ԁ,;[L$ˀ )jWؽzȀf$<;q DY-GX2`H@:دOB唗R4L h'ZHWb2:j0p-`KXF(CdpH@E(2Y8\(k~,ZhEEhr s"!xG2h%-"0Q8?ZJ G3RAxp&D8\(,(fHaj3h>r 1"HK& p([JbEh>6')J)mo ,)s .@(h"`Dh(8p,-./0/3_HpB+00-VKDAV82q\CGDWEgFwGotUeJKLt0KPoW JSGK[cZ+RHK؁!n0`!ps@X0cGdWe7icgv^yxvii8Y(vfoa0@)\T_80W8vW58^|ip؆gwgmbw7aY|􉀁owll'veU8j|ipY mwv'atAW=h8:@\ `,"(EAf؆^8Xva؆HkH#ka w{:  8z8%0 H4P]84X{SPw_0_Ȫxph{ mNHy g/ Ow^JH#iw8gT?#H7pa?Ix{/Pi~Xp{px^axmPQwh{`؆D|:#(_J'݂>(;@!C)#gh-Ŋ/b8M4$HYC"#hNуF $6q&Mx5i<|EJ_U(9T5H{XJ%-HS'HH:ivIlMf64p`l}+tdҸ. ֱJHC'0R#ý f (VRtfqTr`4e;v[ë*]вU :rԑ2c!8CN$KmZaĉ5c$ 6nFL)3G 2X WcI@Ɠ@XIGhpB*D!fRF8% D@a][ < ȡ<1yp= <؃< zB &t̀=\$#@x p #p( L^CBkRl3or2b0 & jn] E'*ڱ19eV3 ],ctp@j @@ $~)[Z\_gPZB!ja+PPgp-(qEB:F.3{73MrgZAYC< 9%N0݀B Mf9!qCs]FĻ_yC7*!)lX}KX7|3.M;`ġFHEε]f[x&&}> z!yx0ayC-=d/cn_\fW&ݫ+>7 h-ȡ i.{ {CVs3- y9a sw2>Ld !g:9mu6WSJ߂7`_o}#ٗ@{I&B ,@7褿` #f B qBiA <@(d54[AM ]nĞ^9^٩@;@@!C ܀@  2 4]Q܀!êi@m($`B][Hd^i(@!B A^:!R\͕+`a^.<(Ѡ`]M t3%A <2P\DA $")0A #Nh1 l-P@LPL @#2@<0 <0X9 V0A$<K:d@})@u% ԁ0C0Hi`@(5a>@=Ac !^ʩdt"4 (@Q6$ >YR& Vޙu6=r:Aj:e!@d.\u"!4B/5e PAh/B\XnE  (~| Lf iWJD $jg:29h{N(r*Dv)4b%NT ́j$#MdlBn`#D6(CO. C,4 Aq6"- (-B)F+? z88Dm#mr\vIB Ђ<)P|A0B 4r`++p8 @:64R&|*e"2>ʪ0@f#f@K.oWh^P2䄞c춍L@S6Ǿt*@ {6oZqR\-4JB \WJf.iJ@V-3 8lr/v? ȯ0kc\piff l%`k[Fԁ-d/"' C|+ "X1lP d&jfp  {ft:f.!E* iJ{:~6!fRSJV5( o1*9.9.6ҀnUPu& BF2k&"gbd.k*k̯c\fm*/3*$p (Ŗmmk%/3,I|2 B#0 BrB%#P@(6@{*|AEW82 @ |<:$AIt2::4@DEIt(hj{8&_J5 ă6AA5r<GsJƽ2eBx()X.{f&|*3k200xA9fv_}|n# _r!ص2gA, kF(u/;_BBk3@7\P|)AND)%jf'i.&,q#8|q^ rSB$AuWxv,ڦrtiyCTC7 @ .L,:$$ DhހoϺ2o5o2 fi"D f:)p@yʵV5BhxG-g>bsOڵآ#jPg$_:wtOA @!xR P%"6b&&4qzffo#hYM藎(Xj/ @͹:r tȵ Bqj @ 4 dC.XaQ%#j&_m PA,&pqs )d#h&AAϢx @8\wf.dmƵ# \'jFe6@ݵ99g0 ppb*J!dFɠķ&) :#ylD$i!P&^ ¿BB ,(*(lbY;t".<4#i)FA/w9\<},]})%`HYN2&(#vzW1W%Dȏܭd뀉&ֆ&|t́ȋ @xi6@zصfm)PAL@xlpCG9{AyAݏ&Cg7ռB3 `fUo @%0)\(yٍ5kNmzS@ypAsh@<3Aw @|@$G$tЀP:7kpg&8:||߳,#@0 &V`<^ @& ؃a>Y@48`/PF8`Ď10(-hYKL P0  (kW,yʁ@1#IX(kRr~VpBJ6l"%CH Y2&5yy`<|texY Ih[.@A Pɨ+Y9F0(∢=7hX xFJg2a@93lQ ̀7[訐{(P Rh{ +)@2)h@X*>cBP 9ټQްR-BR|AfJ?˓( 9C B9dBH/>x@Póc=Hq?袵1h9؃T#< P` : $;Ԁ nx@ (hg  &2hh=`mk51Fa:bPAT`7 D@1Zn`Rxc T= p9{ M؃)PJZ>W$ ς[`xQ\q(+@D&at1!"KTqFP1p%V8m H(8ʀ<* i'Q?F,m6] 1EgmSDМ48N @BNn aj$C^a96t '2P { @u`s/ %JS`P|! uFUB_(P*%b(䰎\a$$ oMPn1ff+^3nT &R QC E qw {qYm0|! P Kb0Aױ 4bY7sB7i\ݸ9h:E]Ȁe"_$DN!M.Oh rxC97 /^2P v):R =3To=`@PEP`X+Ocњ[)|L @kH)Qe߃aJ(w 1 ,@ej8@Ip*e.'!>H,.`ƪjV @M"ir 'RxFnn`4n44XkZ@'Ld vB^SxBh#x)tO$X+d,)@"9@$b ̎o@ >F4MjF^#pX@)8fH I@-X qDSʰ(`d@e T>&XKnװ:87pop Adi pb & j zFa  D. iD/&`lS n@F9MmSЈQщSD9@f𯘔GV#F9m F/U` ڡ~!9 EpC$' E8֐o*A#"19er֞ RL$sI9&#-Iq crac, A N /\Le%8ftM8FLr%/M./r../.Yor @"Xp%@2W,!oA`ހT` `4ۡBBX 2N/*s%0s TB)o'a,BsHsN35`Nc^d6@&3p AJn%xqdcX*6W`65T@ہ 4@g@ @@#Ate"B!t: QX!va aDIDQ >DU4jXA!ɏ!{F?14/ b x@r&aCCDMEStEIE+9'&*K4@H}^HH^ AN\a4,H.JP- P5QuQQQCT:@`P15S5uS tR` Q T+R9U7SA5!rrTKQO5UϮA~a,*nL'X ~dAbZZ5[u[[[c:hA3Fr@0(,L\__6`[RpHG]2F`-^H`J`ZcA6dEvdI6B@ *aa%Bf` `UPK6hvhh-Bj DfGvfT!  \0ahvlɶl) (! a`j`davj1l p ph bvt1^BA gqItM7d} j !=`w W:<@ L\; ī=gaŚӡ;y1ՕW }]{[Z]>o<R}Z݀ț zѻ9aѮ p AdH!lbVjK\&ޝ:! $;aɠ~ \ ^ߡz %x!!9>6^;g;GcݑG\|c :{&~ xaK>bG\u1|;jڥ+9ZV,l1L]aj RA;rQ? iH@#A2p04dX%$;F, K0CISF2J<ۆrEGҤXP %kHԤmPUhJ9V-ܸFli܌$c]wuXFu ϥ( P )&)ޥ*cF 3 (DK*.BY1; %ʝ"2QzIE 20WXeM\2-' 4!s54psDд#n§1F5ݶ)W)w. v˟?nQ(őDd~ q0ȣw\)K*sPRy>e^L#0cL{sE"I@,$Q:$8b/52RH&\R6/̱p .; 1$(FЂ.LDS2ER0Ýw2( H2#8JB4 ( @H/ TrD*v+XI S#P  񔒉#b뭀xJq=\*rIbJA-FF2~e5Sr'.wP0aPe8 D W.n&s"W>Iyfh<A!N8uG2o1#0 ZKc P"pR8A#B3 ! \p+r@lL/Y j`Ѐ pB9Єx !s A hB_S#40 #S! v[`(`B0Q쓆-l &T#k'~qSz`NZ8W9 #;' ՉB94rP*8)N >ϑNN̰o`h;X%qf 0e`_i!@`avp 0@ oh\E-B:P rPH ?p0Bȁۃ1GA `@5Ą @@ X0Sv&6cY k\(rř:<  LnX6. (Ї("Q;$N5>G"{@(CPa yX!,hn{&`)Z|Q(F%E: j 0,A(.@ g.bPaE!8J!P0L : 0 XO0AQ,b (L ` 4 @@5PР PP.-jW[I h) yP-pg 8` h; yB@ `m *([9(-bexF@mrP7 0[e@JMZ @y ![9x?hYZ5G-S۵x])T0oG}q(g˳ݲ;dgB!t }#80!XYa]p VuiC[ ha2cvՁmWqx,S|95Q{ws8g`Sl9qшN `ȁ me@ϗ7wVF;1` ^@_‡qb|GLT;gG6P0ee}7~w~wvw?S4xHl\PƠxN`0`%6T 32P g`au*0s],pi[@d,@o'~t7GG3p0Etf@f~`t{7r7rrgGs wq'W/FxX@toL\01'oTX|Ft~5wesuwhzfP0$ʰWVcfDgx[ x&$p m@ Xxp$P `0ap@ `W K-&00[hpsdVD^ T0hf~IG?D}9P)pw@v`$}0,0`kw qI9k cv~'S@G `  h ~{` (f)P9ٔٴj)Fhxu~֔:w:ɓǏ;i p̸?1E `/p@ U@`  Qys _-s7w)^ @f<b) +Z|ZF j) }k;I B9*I8`Xpm`߰'P C_e PK_`·~70Wm(f4q^|0֔ 9fI,é 9aVʉ#n  4ДtXg*)0#לc!s1>iꥦp@a& uUyr:o8}dsk0Vv@}l0 TgsU c P GD]>GqV~'%~ )f@p`;C(Дv\gZb0 h@v)A  ڔwǏo|0 z)}gv&~4lC\zzǯ g@@c,0iuƑyq[zؓvqd*r )P|T&g^u֌%: x 5$TJDF0@ 0@=&?7r)+ bf3C(}}shMX_f4Y~8)K Je&HBty@'z) TR9q)Ew 7ഭXZqY)r6TFfIbo-p1zH E_(?XPmpPC/vYp t Z A EB+O,9j+ 0w `H_hs1I +* rWlrPXgVj~)`ZmڇsA}&Irw[` c Z +Q1ɋt4 ffy0dz: ?4VXEEb[loǏw & _oJxy 20J 2Mm EP%Cp:QP ep @ d9 @~gÄK 9Ǯxv*KIrPsh~ot _@+} P@t @Q 0 yeNgw Z')  ~ cP1iD{R(p`e LF TMg9.,PuP Z I$`m ' 9V #`(3  A nt] M6jww:[̚J)k6O 㧑gesiρf P>w8=D 't͞{c-o@2[e#W~eLgZ]~ ml\CP$)B.=0mZV< E TPʵ8 ' 0T؃A\lg+ liLLS~  @g 77~VhعT}[ t&^0k oputh+t|m¹*`^xqWkHEᢉ7Bd⦇!ܜ XP 0Ag@ R# a6`_ WE@ wKiCI` Mȏo%tRf;D{7r9rc sժG+H{KWQIqq,|FN-EG:5[;p`RqYeb p"- *hNc PMk3wPu>HЭ T V FD=jpP w`W== K[(`0M0&`b-&|Èڶj8Gf)4CJVjZoJDE70oAj@Q]o~h9 B_ >f=T Y`189@ }`Qr@&E A@ rnK yev-  )'eM /C Q~wNyp׼*j>-@KLgd  Mt9z+i(L[ɷ{\/ل\_bZϓNeV=T]&Oc! Ebŀ8")X h.`rc}2hlIM1ejZAZ8`0g`<%Z,:THj0 ap:*LV(Q@ eOD /fH.HG Tm,E2v0wG ؁ *#`FCĘL߀s`}Z/Ƚs*V)(BQ%@HFղaohX@-]3hr[0E sp4L@:Z@=,0( 2PpÂF1"NɎ< uLrwATe`R %}R0dTxOZ } #ATtR0C8ärC2& ,w~ _A Q: @ pB2@!P2`!Z08rhK+2( j(rPr PMf2*>)?InupajNdȸ?@NL %ghu0 dlmoЄM׸t^AL%.8z;݌b2O ȩ70^sc5cV s-'PJx+G( @37`bX3qleKxXE > 3|"ĈD @]w%VAF4hD&v܈f ލÄj@Oapf4à83 jT-L* F,(p\E&@gAiPYssیtJa+X/X7"O !B!P Wf|ldf>Ìe3^OAm2/7CsIԠv]m|V̗Gn} Ɛp@<"7N(<\X1wpG\x KA&(%@ 0 /,3hA 3ym~sj(%Y6tc;"`$,PXؠs~ug][E ;090` < `au]{ͭf E7q" !5kKAfewg^󛿱՚CbWd砆^#PB#,y^lAFH Чw"6AU(1}g_ۧq1+ 8A 2*`amB1] ?`8w;E0 Gp h2:)0"8`HA B(_pLȄ( ?n@ AA A=bp*, x3Kf("KPû01U%ȄW L%X%(5(3H343()4˄R`(l:L5+x0[L6 <3B9@09;61PCB,DBSP[hF4DBLDCT%f 8%0GtDT#0D39ݸ@+$LB0,T.KXcp6(RpFh7Sc>0PF@J;D [P4@48pftcH2F4'l7mFp m t I X؆YdiІ#IXG&8yHGqa * UpGIP5885PsaHaYXa`HHaԇYh@@0alXN#Ypi8i`I%PlL5Hla oGpU8nHaȀLaǃɐ$ YPZUdòգXEG 23pSLPl<2`  ` xTW7pLʬLP2L78HfFtkFfXgFI؆LmyXXG4X$@7H5 %p2ftp% L <4L44HkL2 YNNL M%@4 hFl@HT4@F,m|Lʜa(MdFDtF48DN% X KX\D E#8yRBЅ;8\(VLk%<XR4SxLȴNNX5$lLH2tPaQN0dpi@r#lL Ng5ll<4/M,R,܆ 켆 `L#M2%-5Hwxϥ -EONxT0Δ0`QlPT'M+,@s(;x&:8"@0tb+836ʉ$MO%x {QML RM`p5M̴5fMlTp]tXLx N=R5M7pH26V8=tHta@N<2攄x`HO$kX fUIńWh psEW4B5o-5"hKV(D8<:0:EP]jh( T05A Su8hTֆQQ2pS4x P BpmX(t@ȈDlWT#EL[klΊFӷS'%΂TXH>gMfJD HX`Q VdTTXnMܼTz ,PmV )D28kRWh @:HƤE4@O%`х4VRe]l%V#MNp$ <RːRUN44[f,PIR T-MiWp%}Xtj%ӅdL,6u4!`LNXND-5J\~\4IUP٥L_6&_$;bb8  _T`@,H5x8V@ȔFu`4`44$X̴)U׎xaGtF [4%GF*CG;˄DD,D(n8Rhfi6B=a `\`pXaXX]dpiNZ0NU@pY2*82kЅ*]P.X]]PZU@5`NH50`Ѕ_ tKpy(rPJ{ik~wi3 iN}Ȝmm|PGqtHX84a@U^IE@x H@x# RȅN0+Qݐ)p1ckVx80kg}J4q+R(S0&(=,`+8S)x8cNJ®jxv1)FVf*'3v0Qvц1n1nK &ZAhC,n_@SK[nn_IDP=E!KX'2D0*8&)xdЁ-9>1A]310;A4)W%## #P2 o 2VxE}j=@xpr&-@c##Or%Pcp(Pq"KH @qex1#l=؂79@;s$׃1紷$,W+pE8/p(P0_(Ս)x.Y7|8UmVRouWGLW Pu [vR$)a\ b^X*"PCH!Ose0(R Ae胨Yzq7x>P-9؃><ȃh@cUJ0Qhwh? =(@b>Ș-Zov* >h+9gW6.yʭ?8vX ohe؂(y\ Hx*y'-@OWn38_ N,S8H(#C@Lt1[x8p&78qLJ خ"/jvw~>x_8ŗШ`|iH z7}8X|σۺw>P}9 P K Q~*ѧ9oH}8p+zQ({؂s߇/yѧ==yP1`@721#ƺ[QH9W7sV|`S!lDs•AHi4H= tv]4@  dE{y+ `2*֙| ȣA{#'Q{i2jԛr4y Z}zFPE=rZ4ae o(wBRhP 3x@ 1[-XlZpiwn :( 7hoX1I=ŁǑ'W"X@i ((ujXHr%EBE" ֙B"$_2I(4LW-CA #opOL9DR{RL6@B.RA$ P hp '۔Sw-v ePO], @9Y8ЀV4(@0` A6 BpD)@LYڕk^D (tjI{qΩfC{6F`CI!E` ؼuR,HDIs,mDaLXU{8H}8(wfJ1f@A 4, u3d ()}) ){vuYp5!7;0qjq,XW衆-JW 0'N' pjh3 P!_W0+bt\ZZ,3رW3\aD!9C#\q H"Q`Pyhjc &(# H((Mn :!gS_Cm&sE-h!"` HpX`r#,aK E840Y&XD(DvVe\H59xAbi ]&N  0hW 㒔L*  $@U1d-^)18Fs&L ف2p7|Exa 4>Xi찂T,PFXp?֧N2T{R-EX*f@A, p':t Q8H19;`!A&@ ruhM,  @Wc+[*hP1X >$I6-[S N*4`zh^&D ]5@ Zl I90_$mN2!!4+ ^& Mh+lj:qR&sMB;PsG.U͆<6q`9* sb E0b7H ` `1ck  P_5shfER9$xbXҦTĩ}g@\Zʁ]؄j& /wvt<0>~dS\45hYY*h0̧͒z p$,@  =/hΆhGցIn(8o`MOJ'/I!b@B  B#N (, !0c_ J `a=`OmK0Z}e-/ΡP,F>4(nVҍ>.S@;{. ^R'˧< 1!s2 y?$xrX׊3 *00oZ@RIKU>.M98*_Zd ^͈ʁ-kAs+1-hA 467!O%.Qh{+ p3,i>sز5 *gmLtۇu=N-4ѕ;a&捦)Y;V7>WސaUʞ{..0[iJP `@;"Fk=$m܍|t@QۚF` A+)B!2t8EN$|D#P! Bd0 h^ԇ*&oL-|1՞W-K<3 @eEɌ"I]L<@yU_jC)2]V؏9,iE,@<[ʚ(؂jUM@Dj͔Xh l)BMi).Iae:XB)9@A8%~AP@!xIH becPXd+D$`mLqp\lkVjJQȀ Ђ+\58=ƒ|Ac9 Cz4QEMɮ HtA!0(\t@(@l2&ZzkA"a84@xU; 쁷ɟ<|Ѝ( Z*hiMkIh wLByPBA)/D' B'ԥ0Gך=@;h8̾* 7&ClT2 8?)V4)dMϭDP|`y<4FihƙMA LI![Dų%0i$|ƺ.:l)Lm&ݶ QQ `/b`A) j'B4£jw+L l&A[2@<`X7FhtxT@Phtվ͉8ȇ-qVC$A0J`G Á)a̯ɞEkp$|ށy*uޟ *@gr VorJLCӴ/2fjk֘+LB!P |GTAHD0| B3B#.,,8Q|W f˾`HAlkI @/@0TY*PZꗤwC&Vμ@$OtY,Ӱ~I`+н@AF /ͭ[q :,(gO@'QA0ͻ 7S.x>dA1B-PA#ȀA05)1 %8B3pCri+6L@0 D q98kd-_\"d2[28"icLAwA =ILHf8DDjJlШ|Ad˳l'Z4A\0 IMf.˜ZB˼ )^Xphf. HA,CA^.$%V0rDt]5ؔh%Eެ@U3tI^fɒt!@ Z6`DxAc$\,ϺF.$+v%CM˜F t 8dZ3E@pmʟh\P֬ЀMOtT4¨2D*`B%*d 3^",dB;plBFɊ|D01dI*v:0&@ .x1M@Poo? ؓA8hԄT(@f`T7ߋzݝT` D6`,.P3D=pkԝ(UdR ` ȸ  % @0,ɚhO: BЂ/T7TCUK2/‘X&4%7r^ ΅4Ͼ2E7DEy-C#(F<|E\:JF0|L@ A\6 pA!DAOY@ L@m7@ XA ;zkA5$ ^mҀ?@\)pT(.Ѩ/A9S*9?RS^ {o8>TCd߄!PE\%|T15#·$(Qi] 'uA;AAAL0  G\P@SA \noA 44A;K h>py5]n1E{;p[RA݈T*L0 @ggh \XfdX=lBŗ8M_ͽhOA~%%8x%d-AX A USCV/%% )T1q~Yo4Y4\O0~_u>'kTxiܶ 8E#~1 44D<I-[БA- N@\ .8Ƞ@fLz0yeJLL"ϖ3iXM>tjXπ?T4P PIKOV[6Zd-Fl2 otp=ܸ8A#GpF d̈-HEio;3QbC X`%sgILLthS8|%&Vè6gٜY7`F2أ?<{jٯrްc#m[nܻ)}ĵw.5m3׍ΥF eÏ@XEhD#dԭxYKNp#. D؀R0A 3nQCcPX8,B9%QPE'V Ғ~E KJJIѴK *A% $5t[0MCEJz J mt(a/GJG*A\3T Yik8@$THZnIWS&(ь̘S vB a"`hYJLIa q"Y 8GV2D#} ypjEѐ3k5xZqYA)SiJu dC4}HhCylC}%| )֥ЗGMcH΃̕@7IcdدDd*Hqf ilJ~`5E8lLPDrᮂQvk.0*`tUǿ*Q*tc'1b4Jg%Rg&*Sť4:!V.Smܐ?A$ Plw5+JЉZ&(R%Gnjs_LFpFʰ 3id0H|aEEazE#HUIX*G!olV,Xf dZkCagt|xLc؀63JXdo85i^ꇱ.,YcDD;i^ h~" >8p!a['a` .R .! !Rc`C5C=O.zJLLLN , L N L PT, T,r!La 0 sp 0L! e !Nc@D!h(0f0oA arfpf&of!sra 0a A lP@rA xrp sְCN@ H:' (@`a J! \A2+x ra\>!A TAU Y 2D5Xb$^!@Qq1x9saaVaj> j""  A#7R# H=pf`*5`;zz&_;'%T3T%9 am a!!#r""!#9#3@ 9*ac:f&@ W@*Jb EjB.g.WBAa.S0sa 7,c01kb3 2.2R2 .3&Q @m&,v`pA,A G6$Xhb bޠ `` `@ޠ @4sB3@2S89Ggf&s5Q 9c X`PKM5S}b&nM3 jBY`O`V' FQ uޠPK& '3"=u:W3LTMS5T=UJT Ur sG>^@x&2, ̀2&AJS !(`2X` ZZZ:`8-t0̠"dA<ߠhޠ.K OQM6, Z n!ZWb&r!$fb `WMe`b8(  @h@urfZ@r*X r@- $8}`wVWU (`t@ @ >&b8 Pxz=8x(@D :\EقB" @  tv4!` tAĀP8| xYxٙ`'F $N !` P  D! eF A4H8aLs&V!O.a@ "@bڅ5W}`*3O n`laXr j \zW*ة-X#`O$Xz[|).8Q8a`ZZ3GY~-2 .Zf@Ba( 0aRTAS lV(#@ @[ۺ۫9io^3 ◙3|z R`CBx&U'`[S99}ؾ-l8e3#<` {ٛ xɗ 4hB3 `Y &`| |3[} [~zX1es f RWa6!a\aft! ŀH |BA{}9 62 S Z+Xae3`#x| 曁5; u X }ڠPXG=љ|ra @W~w 8ҙ}OR@Rǘ4X AʁT`pZ"10+ %`m!+ EDx@@{~'6jc8|]<L{!U y3Xx @ X8ۂթ9%c &WujG! @ :>|yV/[3Μn *wWƽx @?~ ܅.lN ~HEX\&pH}V@ 7'ʇ9z@]]`@ >7ۖ{ ji zS/`Q Xc8ܡ|5`gՋJ O_ ?A}o?|~WZ  R : r0@p@A ́@Cp ƍ2fH/cX1snu"Ʈ1qJ: гq#  NL:}T Z4V, `-0H08l1| Ex(<@j'[20@14uF 䑫,O$rP۠B4p`E9S oh&_((fnU1 o_>@r|pn)p\6} EiQkǏqQȑl)W0uM)BPC ,]@B$bV03+xARPmT>YtUt[b-4.=F 8VM'r4@Y А 8 aV]fWnf)y:p  ʰWh @ZklrQe\L@ (̕c)LF ='b5 2_08y9~Gd!`а>"@ a|Y͗D:-m+bJ)dÌ$`. Z@TV01GeaMyXU"f+F*p AzBRg6~-  -)" @#]] }W@ cV;r%тoL0)xj@ |Ț (`mKx4@F@b,8k$o<_Pj9loȷ1OZPCMѪ%A X$s;UR#DLEXBJlP0@sx1F4[K(Μ][kʀP0y002Z.ZjZ``k@c8BB* &du!AKo qt/ i@I,'z R eǡoĕ @@rϿ\`Txs&٢ A|Qq@%AE ajAZ|J5 I9jZPd H,)O `=x2j1Ahyg-: e/MzYb8`::P?=5ˎ<3yN!–?(h$1.-,QjȖB?1j.ҵeX.ЌP#mUA0 jdc^(V1 W4m(aVA! .Tnk9+6R:S#ĹARjIdڢ)!eT뎄 b -p%wG=__)PP; X@EErxZ0?S y |MKtP/~9Bh4M< P`Ғ@B' PL&#DtqBC)Ր T@B WA2ap|q3Pb"l(:e/tCuiWyąQ4k@ģ [[&3VpFY@ p \0BNCS-y٘`gd(1SR P?J>T~M7DEeAݵv{&p 7po=.gQe굏Y!~AՍ <;4(E3hEΚxYUɕK0_]"%P#N{X\ndeEyW"eI`8`AL)[6a_B3شd ±˕ԄH-X g, (VnԂNH&F ISi๪dP,! g@6je80jRBPD(]HoS =/WBJ9^P y Z(k&/dch,d! o u h!H4u\@|GSͤTn&0R<2Kn D4nZ>Ȅ%p|bX`IhZm7@N$! `8HX9EtP<4`@joU,Cz̉"9`0͔Eo) PԤYUeȐY'x0At0'`6v2 EpDpyPp} p*B?EjV), @E4Id\#{q% Fa-o<;)W;`L[`%HID41aLp-S@Q,b'%3{-@`zXYPkq(VX _K{]LWz5FsbEsCz4SI` P PuP 60 R j '@`x >@ ˸ ([C H7 )qlI[@49F[bEky@ג͂cE|)Pks);`3U v3Zp:0hߑzTh\9F֖ F[O(" uGL0x)7Yt`9ia_ u@0  ~`m +QP? E D@ ^̣ 0 $$pkpr- zmAz)z4))#e!$7 MXJȥNDe[3 ?@\ ] q A X͐S[@U j} #L`hԥ MZW y|a:|2rtsy`aUp0 oPiB pR rrSy@*` a *yP0@ mP2 A* v @ D (X0@[BE;0G 0Q  `p @ Z 0p fk ` 0wH[AE| S {L0] TkXZ۵ac[ n &&{ v۷z+˺SP ;wPP X~RT ^ @ 2@ . }Ӵ뼸*ӻ kr rgK K0`˰ +拿u m ;F R"p4k2 XVD>0qB +o` 5@w9;=,"YP16Qp AP 0 Zp 7[]_up @h q m 5@ 0 ` ȁuP3P: Bk+1ۀ P  Z,ȟ ʡ,ʱ ? D _[qp`Ywż˿ [Z$@ А`Pm -, _@4P lA8P pXkjRP wQ` X  *gɿP 5 /D0@ p  EɁ  QQ 'ǯ@+ Λ9;*0YpgP|q7` ' ' ` "=! ֨` , ֱ0 n閳on kn mu k= S pG ' ` qq $`  jզ U1 ` -oۼ 0 Zpn= )pnw)0 p0 })ܷ} ny ȝ]@ PDQI P' Յ k` Jkp`ֳ (0 0ӐĐ`lny 0 -" di@Jd Kh@U d qGJa  %Up%NkP pdhpLGiͻ Z ƝJ@pW%JpJ ' a~_ a. $@=`#14C+Ay2Ix'ZoSl9$\ps|PaFXB xlFT'(RFjյR"L1G@E޶@gŎOfyg$``igg:+Q.{X`a,3``+Q"oB'q5kz0@\=.%\jL:A E ݵkYddY ]txAD%F%tQ=Yv R,]p6EeR Rc`a2@` ^!WB0.`E%Ȅex-`3 `! gT0*:w1`8C!`'z "D7\n G&Pɡ*R/Er, ":`'x 5pc@:.q2 @ F jd!ـG2Ffb`T6`A Q V`_ 8 ." B0)M hQ 08" -LP 0LbӘ,f) rdVӚ$.* 8l69a 4N5TAMr`:_S4f7!F|Y_+r``@` ($p\U@ bCyp,C9lI Vxf+uЙa9a= s.h 6b9K9}CT|CMFԥRSɕ}iV)O. ZxTYj;԰5R f^e=(~ R(4x: g`(5Q5 $4]e'@-PhBJG*QAy>0QY]Ma]}lx >mAdEB2f``b}xԿr`. 67[`qUl-k0aoHjJ]!eiyϛ=xdWoRÜ7!{Mޔ vp.,!E`(31;Ah+! 2_P/la >8p=d` }x{ {n:P&"82@ h dЉ־}ǰ r@  Pndހ=u^0& X D\=A׿ Ѕrx.1횗xƐ`]i@;zg@ 51~u#Add#ʂ)ڠD~ (C(j R- \pP]B :c  pN7h둧xE_83 (;hPhPc3aka3xQ,s &. HG{`<##4B6k(Ss#&z H3]hx(( :$AST&@Ђ:.sFM #BhA#<8:l() >X]8-6P3)AlT W@]*(( $pC; :HE-8}+UK-ЂG, PHD;nm<< :HC;`x1@$,;z,)LACE3GB҅X@kF Gp| xqk\DBݣj@X$XP'%4>3BF3L: H3>-< u &Ȁ` &:MS#A7Հ&Ğ%O(]Ђ(((x!oД&DzĀ:hktM܃< @%,!4b ](nS#<;]X=HQЁ;;ń(R7pa̶`ح:BsS+BכA3;k;%]X/(C]5®V{M[;6nm<1X1<<;c>H|:1BHP;#'́$(׫S+Q PsżΓ0ģڮ&J<8e>`p Rq H:= ؑJ;8Ax/XWEM=mAX@pE[ȂK[ gj@E"]Ob8=/huhh]Z/0XBm]M"=MS@߶+8)D<~$BcIp ܪ7h/(ͺ_9A#\-X(X$ ȵC,- HNKAۂ$XMsL`S%SR(`=M`f0X P@, jRieXRX.7p݃QP]Ƚ`Zf4=lZ%#;,8i߃B]-(׺L;eUG.(AD<*<6ױb,veW{YF{E8K;ЄbG뻶;fM O#("(A PT4 kH7H; c=*I/H= ޼/9Re]1(&M @(0gdR46;&b7^Mh ң2 B΀]/ހb,8Pʃ8H(CJ.<cOO=(>(1ЄQ @E 怌*hsՉVd3U>Ükxh\ݩe{Vj_䎂1Fp6kh `BLAR0VɚDmtu9--5OXS{Rh<9zl:ri`6m,T;('k`b<8_9́qgS:\~;H8<_H0E~lg}jgu7o-ͮ6e:ARDo\ȃg^D<\MR+Qdh@n]1X^vVN!f쒄-3~C:_ЂKԌ¿]'G"QV 8S̆PH?Gh>lN/f8ЁD h MK̃> :)Mtv]xPJB^o?HМᕿ>sMk\?h΀=-acHn̓VkT[,/pzjv[NIJk :RdpL%@[ІlxTE`dpтJ2_^`DG@A. , wɃR|Cb-h3X2@[90D:"#5/H)ꀌnЮEvA. @1HB5Z(|JXA#Wzcg @HB1@7T0 %N  ؂$ 8SCKjLv x@  c0oi 3i8^` ɡ\DipC(Gi7M` P@P,c(,i{J(:d \a X 1 R1 -AϨ]*@ `,Y*ٟ D|錚A@Hk٨͢ܠ{x`70ir<>qܥ@ O m* g4A0UxPZiR0A | 7Q 5 P`1fZN 0P\ P2WU R :(TX,:ȤZ9B B|y6@+ #D);@ᠨA % dXAW>k)T3D@ &p`q@ H.r Mz2A` l < `S@rp&a z Z0\9r0 @f|$ >lX /~q5V`~(X5.=Uo6O0l9 lƗ78rR0i B2qR8D"bp3< rc?Ѝ<9Y8kn>7ӓɕ7hĉE7߂ѾV5`P>E Fqc7P @*z͉?䡗$QߠS ~VpoòAB&d@JpOd‹'F92?@U `x\ ZnCz@jBh0o/q V)K7~':1U*..0, ?| `)" 2 0Bm_u `+ t_'_X_ a 1A_ +_ JD%^D N,,B"5@@܁=$@ lA*PZ}|A'<0$A2v0"*bc6j6rc7%8B0 ԁ. '8h@9A9.P  9'\BK tW`B"t0db7:CBdDJdJB1,.(H) CB-^$@hA9C7TA 1D2*HBWA$Ẃ&pCW`D"eR*RJ!A9D.-8B/l\C *ÛuA( ^$tBt&C$t3Q.dC2_f`N*A2DAh11:A%@H=)@ @= <0$PC'CW|W$K,C mfn+A=:B9Ȁ!@rA7(` \C)/`$!.dA '=*yQ)|B/A,,$9P @ B (] epDYtA犲(}>CD@T-/D t9l1C0BB3 C0Ѐ耊'tPzAHBB5h_64"- C$ 4A<\C C,A09Ԁ4$B0TA@T pel rj#@$D+ x$BkC*A 892-`AL8<=jIxB8<B@f8AC)$#)@ @DCa2`+ê1lCā'D/A ,(<px(`98ԁ+p :OC l:l @C4 @ @CC:Z. N. <42.4Jn F dZ Cl6:x.:L/<05ЃZ ̃J.9A U@T@ p}/Om4pN}o lo 4L @CյZU /{)'4AI6.̃Bp/41xpJȃ$8B$ 8*$@((=6\:,l)@hm=`'tA1=.=,0BCC/C>Ѓ<14:C/d; 0C/:0t:BC1=U)/rz .'/p2#{̃k .0))3:dUnnTn.3 P @C <4o/>O/G/28B.@AM2) d@Ζ5P|-*Ъ63:@" Q3@ 4 8f913@8r/@557u;:$2//Wq/1/ @>䄋(Bi# 5QH;hR8!F͖SJ9 P!p  ,B#"~NS4Tb(Cp"EhGp"NxN dP"P'ﲄs[ iØSNl&nYT_"WȎga % gr df]d~zyFeXxɡOf@"Xe%&l:hŵ`iW`+W(*9a% Y.(FN)t%*ʬ S 1J ܲ(%B _k]z/4% *V"\Y(~qИ;S̙hX]RhGhP,ByKENC BEQ/IA E< en*E (SwOZJE{2 de,lq] Mʓ:TcI#~UHZԘh9\tXB8EQЮ"qgF} cQr:x:4wb :QwB< Y!D"RЅ1P O^-` .:І88*GI컅v2ƻը:CN#p [PH@3K ^A  -<`p {PaQ(' M Ơ"J X#rN*P!]0 c("T'h-셃R\X+1L[3am EaUZ'QwRbS2c P,ā\ATAVF%Y%6,)fQA&lI?"H] @DAڳa `>A37347?\`?+=@0]ʡ>!7+ 7ò;`>iG FafT!JԁD aA@ 861z6N"(@A(ata bfJ@aAO)A@L!H؁d@!xfz F`BA 22@xA `&ʡ!D"&RSR!SS=4! 2a&U5?ʁ*a SR ޡA Al` nsab?z+ @Fxfي5KWpU XX ?FvOK@at 2štO{A6{!nya^2hUNf!jyaԡJЁxaAZ{zjaABV&@`AeYve٠Ah5aС(T6A5aR N!*afa`S!؏R5 \ 2 ! SENkVTA Wr)W,s5s=a&K`75`A`6z? X!h RaRA fh N`8Ō;~ 9ɔyH̜m&i6Gg iǙzFV v"0&\pכK>n; ,;qg$Wq,Wı[,&qh L;׋,5)?#]S'}/%StP!#\[A5bwwm{,* 4}]?~8]֘S}5ߋ5ߓ=Nt l]чl읛؈\;r7N.lp'r+N,k<3} K-Y' gQ8q6)xv_8+g{jJpԢ̈́-Lo86ntNyKK.[džǭ N$.0gG>D€Ǥ$A!Y6:0\4&dDG1H7"̉WTgӟvRf9O҆:5^NoSq5P:UMiF4`0n3껭R5k+t6R^+?z2ͬSYS5x`%(Vv6>zr%bZ4nbUw ɎֳlfٮopkLϽ(mh-XVg[QY&ZdG\᪶dic[Vָ\ʄV^7Bnȇڹu}+_ vJUSs%}Ob:"\ڔujWbu+ [Vn- ; - bvmog^IsC.қW~ͣw,df6ͮ|ҏ[*MY~]~irje9iE 8ytsc`Y&ApD+h6X\;Q(8*γ= >1䩠[q0Ps"W>gyinsdiglj^MkQ#is3س;WHJ{ӟi٧k8Qr؄v&-)cN%W ڿ%HR⊖yqsV5==,+ΝeR=]Q{FӼގ"[>'noi;.w;.p+;"v)~ZS(W.O lg*?5Ow\W?^ 7bݲylݡt"׿+3À^^H;ҧ ^vkȦ=~W1+GV}$~__s8[{N}2-|fX%jweh{fǎ<'ЋC=ڥ y/{\oӓixYu|}o ?ſqjm/fbD^xu$S#I|YKkj&͡NwTyV?(3D8K}]XbaZgVFy\RSWWn| fYW;R'eޗzv}+fggb!H}'D}x*d#vw0(x|w|5$zze|,XDv}z7 xGI[5K8~f9Hv>؅fQ1}S egikvuhwy{pZgT2oB`pgxpԁc&dt~nU7"tqwn8QuustukMjopKr;srƉHs1m!]wzr6mdJXYPrǸqΦrxO0.GHs$ x2hֈRWHcjrبʪ:AQZʫZjS " wJLsIْ$BڐW:=jA5\̆|fXSY:ꬆ$ީeگ! :I렎)9m:ʯys Y9v ˞ٚKH" $+]*1+3@}+.<{9CQ.j>ٜ8'?; 3I:kYyakD泱 RuG۵;XS J -ٕq{b{4Ykl+5ɟu;%˗A+-B[Y8ZH +gQ[~[wz4۞nKy˖˱z ;w[k{ KkNjhO0X,JC7 QhdUihLH|*GnʑGrDl>e`ۧUM hKVIs)[?nCT(#\ ,n<hɎ$oZxϪh_qg2O6̼CY: 8˿&ےM<0٫S sJL,Lu6ջk;!<3buf۽_רKȅ9˲뻑<.b~kkJrɛ̹Z[M th% ɅʬL)ʇKʶܷ=[ʢ["Dɉ 9ʸf0|ɧl|Lzܲ܌m͂;Lɞ9˭i\=}ܗD T]Lsp܍F[go H0lapNI֩ 0TlHq7 z:p3sFZ$]בbnnm[=_g½xDr}9G{bݤK N+.?~_vųd+w㞮ɠZ?.^\;`.<ظ_.\(N̐x /n4x(#$-'-"~.NȌ#N.g=瑙V>=/t.ߟD[XK /SIpJ*^N}TԗNB9{f~hː4nz,5lHt- OZ*L$s}:o Ov YLymD4UOpW/Pw:܈(R?T@ِA&8o$opmLpENrI% Y^W(xۉ}yfO%h=!?h/kH/\\"kGIfmZVïLO-ֿEIo>(폜e޶#oMF#5m]ۃE9+ut4ɲSCY $T?V,Z!CК#6"̭x>'w Uk<-\ afQe͖Rĭd˓l˙rǟ(e;N Sl1rA{$荤#,idU.EZ!W=xq뺶ƾ*-"v,́Y O,X ˾}2Nuۗ2ON/_/gmJ :鱛MSMxEHr5ԶM&hȲ1?+<1qޞsx9i.t׬ƞuܟs^]g>}(_ێ˯v^ՙ7ڂɋeS@<? ?t?P ,qsT c1 OT\LGsL+Qѥ< cRF!_1)$P+QaIO0MhtÙЈE.gr|B<%l5PM=NIJGHDBAr>0"DP8DUFc? 1S;,UPYͳR2sQ]q4MN5\{UM_Ja,oO/F5U]ou=BImtW\qv[Jͨqe NVRQen_vDI!T* e ڄuSW鳷A\\d(].fgY/gkf &Zf\CZ5hܞ gY:̮,޶Fn=Ly2Bj4>mb Y"+B͹+[P3UMzn7bq) ' '"? ]\9/3\Vѽ"9 v۩*]4ԱR]׫v3# Yָ;s^J/7%U[c<ɉ^׾tscgpoxTG) cKJg@ni .3֡ots]p7o4_]Hf+  *BU'< Ѓ{G&vl3bŶqXG7A;6.Ƌ[Ȉ8&ctM9|Ěf;r8z  8H,ktdfH=9%1<"򐙔d33QlC[JvJkL{*T%]\ JX-6a^1R$Ki]BY>~6ld3=ACӍfk׆dճ#S{-x޳t-^d5nmmousnqo\n(t܂u7{x n,=W@mgՍVEJZS*,Mפs VZ|܊)M':ܖAʦ5`&9eI \μi4]<}(Lgƾ /+*գMcU}-S*نEUV յEmWqw4F:O]rt:j<_ξ'dꛩA-d ﻛݴ:Ď3d`>ku aԻ.|-W+ܯ[#=?9n~ N]|Ϳ'rdٱ0G>N;:=pSOݠ򳓒ލb]>gjnr/ wrl ­M iԴ m,P8p3'tl֮ kR1mF-^QG.0STVPP[ /-ւjpDif Ok ,mXʐ̎ e p '$ A ѐ yp KВ Um  0 0 1 ;Q  I KQk40L+17 mQQ?yqsq+eQgq }o |d//K1/+H8͉lN*騬H1\ko*cjϰQ.L/nJ>&$$)%#G䚎Joj}e!J%Y' k\NȄJ%{r.4KR(S")"&,r*皏+}R*r%Og&s/msS t)?Ai7Q&AaM ̓Nɔ?ŰB+b,?BtBAKTOT\4"V41iA;t>znrteH7TDoFE/CtLGqH1s X^QaItDgK Ô6bFM4K(JӇGk @IN APB4T>14ITP)Tt1R4=BǓ=UK3SR 4=2Q!WVJ9yK9Ɉtɴ*:+S"e#צ6?X$2Z2Y0]_/ Y)u/3wlS$S/M2% a"/Jb{Ώ*Vb`%aOu%YwW )Ive3:eUvx6_S;4^)嚒6rXhfKV3^.A ;j_%#e\6EіX+ho`eWogV6qYv[ IZ-]duqkRWw ;bOmq+rr'=T5U9KCt5ig 4Q[tNXJtQaPIvt?SM7EsuauoMAIOvwSMw=Gy?ՎzWwLǗ{IxBHQ gI-}}}} 54~ T`HwsWG7{7B|QFMN8"8J&؂u7| 7zw/WIWEAWxwuwCPgxzEOERxQS|+%tK5U<8u'uSb9Ws-QV|ui;svx+8vmKUXSpZu+6/[Ysg߸*2ŲoOngsS,o'12&)9"\vj59h*"\k5Or_)Ch]H.+بs_Se1\8WU1719l1ˢV]%yh s̚f;]Ӛ_9kE2ɰ_-}h%7eғ-61 s[B)w 厙.3:i2-9f]v3vV/ EҐr_\- mml?L C@I;mj;*B,'xx[/wά:q py8 ;:xG,mtqᑠm?;EZI5ۻ{:FB~s8{KW#\Tu؇7\u:+MQ Macro: 38 28 16 1 You can also define PC keyboard mappings in the *.layout file. The syntax is identical to that of the keymap file. If a layout file defines a mapping for a key that is also mapped in the keymap file, the skin-specific mapping takes precedence. Note that, while Macro definitions may only contain codes 1..37, a keyboard mapping may contain codes 38..255 as well, so you could theoretically map a PC keyboard key to a sequence of macros. This is not recommended, however; for clarity, it is probably better for key mappings to consist only of one key or macro number, preceded by Shift (28) if necessary. This will also allow Free42 to match the PC keyboard key to a skin-defined key, which will be highlighted for visual feedback when the mapping is activated. free42-nologo-1.4.77/skins/SemiAuto42.gif000644 000765 000024 00000022520 12110237246 020343 0ustar00thomasstaff000000 000000 GIF87a19J?KrBJZ^)1J)9kss!!1, dihlp,tmߦ|pH,Ȥrl:tJZl3zX贺X^8-x:~~L;{k_SG=|ung`Ys+oID?: l AdKÆB%q"aaj#GT? )RɒNm?./Y%My3o:ʩ3͞~ѣH*]ʴNS@zb*VȪU׫_M:6jYgeVڶnq(0\ -:W/)l틼 B_U0wDd|cE,"1ʏW{aҖ5C,C0άg~{jظiK}ԯKW\Eetۺc2g0ϘZ8 έEd޽} n^wۖn6{4_ >v1v Gb a `ᗘ졨!yI( quuԅ긣@iH&yL6P`CTF9eX2yeUe5| RbbICcƀ&g en: gZ*dx{6)Ýz. &&0(|F¤iiQdj򹀠g(jiRLF`뭸u^ *lP+&Ų F*Tk؆۪jZ.:;6+,kҺ ,kkkn/S:`/p7|0)p O0(Hl1_h;#7 ;{,)%,r#24<& s\?l˗L3@ MW̰;=1+}V_J`@b v`cd6mr}nM@kq7w8MxkN7ېmlx^}ق䟏9F͍9Z޹uWκzب?w݋9G.<+~;/}?/wS/=\?;>g/;_Ї?*k.߽ܻ>_Ӌ@o|L$p} ?O=i}`( #TMH? Єҳ8j18dFPn> (D DLCe0YMD%D'. W\">Q8a @2h<҈эi\#F9e#=Q6ح p@"XHrq#!FIR2%hr5%CI@*UB@bfIZҖr,o[20ilr,fĎL_vґl&/&:Qsքe6ilΉNߌIrL:v~ @JЂMBІ:D'JъZ4ygFrt'b8QgH)я/}hKgҖ.iHk:ѝ´ KyS&(MgVӡ:2u*I}ӛ@jDJթԨ\5QR2^(RjR*VֆiKײUZַժ`-`л.rŨXWNV95N{М:,XGYUzMUZY=,bOU4-eػ)g*[mW[P~idTjVY2?F;Pz.Fm|Kͯ~l.Կ pBP&M#x v5%L'bTM:0da^S.3bR%_bvJV:0c/J6.mֶd yȹb;da)Ŀj2,)CʺzCQ+`ڗE/yK&&W1lWsҢ g9yZ+YԎV,kZD)kX6EziFcZ P zҟtVL#[l^ܼ=Kyï?'{t{Hн;|??|<.??p=<}ڣ]zGS|>~b,Z}ג'|mp7aUx ؀30PSP؁_ 8sD(81*_3x /Z5^^984:hF0HXG;(P5ӄN CE4ȅBXJeYu]A2C؅TK7qb8PU8B]5^8kHeYlUODx(}]XRlxn^L臒m@9~ 6b`HakbqbQDen"K9Ia{/c\an+*~[)_nai7cgc]cl)0}\iB*}ȇe~Nt\f . rٖ;7ovfIƗ;ggReK-/gf" .vgi.xv- 2CifkKw06a6ks9#C;3 lIցY+C3ƹ0ʲ&kЉiljk8SmqcCo#oqfppFop#f Uqqm|; CrIlɠ7FӞ oy` *3r 93K'o%o)ʞ'*nqqʡmAA::sDtT@;j=Pgu sTs,DuN?4t8סOA0trQWSu[wuSt: u=ǣJQ*[bBԦ~DggyenyGDwanzwtj|~gǧE$vtZEVxerzy{ꨂVĨGzGDm`g}sHu$gd{tDdǘ ${Ī$ܧ$F:GW}&Ff}$׬:Lc ~ъ*Mow:JP9TBhbQiGW:ZJ85ɓv,;-;[ʯHS?*ɑUqXSH,+ذ"{DQXQ] hh;+!K {؎i1XXLˍ ر "xN:+8uRP ٌ؆Y+R˵4 V]+09?K;kZI%d*+{1K{˶< Ȇɱ5q{%V.P۷Y+SX:ˏ;[kxېk7۱Qm{8[8RB !#Oۇ wص[ iF[ [7ɷ;+{;E8LHIYd;:a˚^} ܕ_|zb8Xwt>`|˻.;JI<=7,zڦ`䨣t zڦ뼩Q*zj_^zܧȉLϞz{:HJF;G-|={Kz(I1jҮG}6=9}+-؊CM'H-LGMM e+,L\^`b=d]f]Q ;6ּk]Y@h׉)k˅#jO1Ɋ6;W(,Wf׷h~mؗ$[\zkK۾rxۙKsRt=mڧmW]&K knk؊<ّ=ۻ0K٣Hb"iM7(XXmK){ۻ+]|ohћ;ߟ{ݰ  Q-Kn{+?Kn2יf ߦKبːm8ٖrwn4ilݰ=;Ny}H~S]WMjO K.[ͮUON]B]xbN(lg”,b>'p^.R<.\>Pܚ:|e.BӃ MgF|.)O<Õn.i0`,"dvlAቝtoS 3uS뺾2NƮťl1jɈ ɱL7 ȩܟ ɠ3bҾnhzȇ ; Jnʳ %8z\CtЎɹ?֜; <,C\TԜ\\ ͝A:aU\ЗnH]fw3 }M<[E{y- d"jBG c%*-kwNө8y<OrKL/?M/ՉLF _OP~oϗI~b>B.k+ ûW[ չ>{&>מ ( UT]g{}]ކ?]]V= _[Zܳ 84'h9"1ήm J#2\2O:ѧ9mZ1\+ s\3(č~{*[{\~VUՖ`bY#[^Y'R`aViޤd몥&+efoʨա0V*b,2#YsMYsr,/7xd88v9zy:^;{{4={<?w(h „>)l!Ç%Fh1aŋЏǐ CnIɓL%̘1 i)&Μ4N.=YfJa倨RRХZk^e"vҲ FP۶Ghk.޶ {wٺ~&k8`M|^"i,k,.#1Wiȥ1&Z5_֨MV @ wݻEkA|t{[,gw\ErtLGPnVV&}|z& h 8j't:ʀZz{a饕rꩨR*(AF:jZ*ʪvzB@BJ^Z,FKfۭ;-p.>ne;.o7 #oM ڦKPAX|1gT`h1!?1c|r$kh2-c<gtRJ*=J?@E}4I+4M;4QK=5N7OYqu;Z/>^=c  Hw}Gӌ U cw*𤭶3)2@!TC421 ㍸)_s7 P>}.M!O^9FpCz^~펁Ӌ'dG9yGÛ쁿Umsc]1B)z+t爷nlH(l5C:!9|5 CF<" fkAE)6@m,"Y1hQ]gA N3Q&jȔ2"')rvG'|N,叀ģGSEQiG.R2%/\$''@& *K)h6d%(eĨ.LbH\)6X@Y` :=O{q thf&a2=O1dӘԔ )evӚqfYpRӛ|5Lsn@ = Ga %I?tFS~R@CP@Ћ4IB55TL2m칤Z/Hю, }DZS;]  {ҟd)QrS %uKJj'US*XU41PeªRzUB_jX4V2r R ȔPXKJTjUayWSuc r*5Vu+d;YJQulh}uZԖi' bj]Yz2b ,r]=Ez+}nA2WZ=s܂ѫͬnyY bV΋^wm4͎e4{_屎2 c N(Jx @g(' 1S.~1c,Ӹ6 >1,!F>2%3N~rc@y2{W6A\f>3,fyNv3,ov򑷬g0Ov޳-A[Ym}-dGC{43i5̊N|IWujKӛ~5QС4I|LCԉnt+}@_ 28OͭN=:ו~{=Iן^g?:urG9r3k]Wnvvߋ#tn_Mc䉏< /Ol_}6_~֧p'x7=ٓ{W0o_U_Iᵝ]损Ҟ^!^n q~  -^: >^׍Y] ^ۉ] ` 2习1Zݡʝ^j B>`ޑvna!aNaV1b^5ba=1!) r!v" rb'Z$'> &(b"%~a&:]% 2^b, R!"+ ^&aVRŢ-`z"&jb)z"5$#8_R"!"F#>v#>;f)#8=n"!b+b9/^Fd6!9j"1"-0#0B6_ `u ":J# $NN$OO$P%ONPQb %dd5Z7R`1aSNeidKdd"V%1de=e2^ #=`!$֡G&FB"G]e]8֢"%^u\=#'NRF"^)1J)9kss!!1, dihlp,tmx|pH,Ȥrl:ШtJZlzxL.zn2+N~l~gvn]ui_Ѿյ٬ݥ{+(A!Bhp"E/˨Qǎ>'r&աLin%Kq._z)S͚n┦s> l(QbFKt B%u굧V)V k+mXJ +hFYI"{n&;AC08P Pa\aϕelFڲ*F3<u'\vYլMYvͽ[ǀ[̋m;KwZ'wWo|\9rSW?{wֵ8?Cp'|w_g᠟ !zxb h]{4W]>7 i8Hbp u"fcw+8݀Bw$5 E5=oee\jeM`)&6ieiy nYCr@4Yg zgb (ʦ_&袌62$V|zi馋b Z .j*ɧ P 륑޺*j le&&{h4@Fl J[6{- V+߮㖫¹Vr.R;mK/;AL.*k, ;\gs[ oq ({rʟ~.s\*23z2̤j|42;lt%lBB/%@5XF,5$Xm1#$=4Y;v- Mr-x]7m| wM݉#,wW>W .ݷ-☗_NjzީM㳓{C-C~ckܳϭ{y[:_~o@>?/R_XG~C?}jC`?JP|@RAp? *P} ,TBLa SAЄ6!(?z\ HZ-K$*RQStE,*[IOQ Xbh/a$.x̣H=qwBLD2#ICkeL !I<^Rg JQ򑔦JJ;~rlcYR|'#EbDfEd:3&IMcFs]ͬ39lj4LnP&:pbr2T<ťz̧>~ @sЀMBІ:D'JъZͨF7юz HGJ҃BƠ'mPҔQ)K1әT/iJ[ S; jNwQ/jHӕ=UO}zQRU2e*S:U:uժXTUMխV+MSrfe^7Vկ<_JגUy]:UuF ٖ֤p,cAZIlEYZnijW"e]h1Fm[]KϢ=e ҾM+pMXJVkuDVvUS:╱hi_]6(nD^&%}LNAיQcE%\Q p;5lbD[}š"։)ZbXx-^Ջ%cS)֍9*b>1) RF&r|%{]! (/K0~eGkR213jUa008^o,:9bW5g6K3m4fcڔ lgq˖iiEYCakCPOfN1U7duK j<Eʯ1]g}= ҔhE|J/_![OշgqKoߑ?Nr*o n{s1LK_' a(aǀb 6Xx؁h/"1P#x3Q(*#_.S %!؂6X28$_%AX=[_Х_eGBxHHT2V8UIQCȅ\#ShBP8XEZU^Ox@QbxYE؆pQcPkChly(sZz5ȥPRh؇k舟8`zX|_ZȈ؊{JHY5XPxoXDdxVh_AEZ e_uȊȋȆf5HHh(ҨYxPhڈب18~x蘋]8FX X荣y(V^hS(X/]؆wX(y_<Ȃ4P53<ٓ-y(?5ILٔNtr#`(b}BBRRٕwYWb h|=vdjibgo9+hi mooI dudkٗ&dz >f~jIb+~gf٘\ujg 0/ SI}VgWƘMgh2cfY7.0΂hg)/vhw/.D#jFlY1`al(SK%DM4il)Ķ9=c41ܒ8lijƜil:3nrC#p1p&rGqqTpӟqF5ryrn#<ڠ꠫f#s*m8&o Z"` s*:Yp3p75 o rr)+nOBHtRwu$tAI>^Gvttbs-$v\@FTF]wOjBTs_7azviWvatHvKX_ icTEuGzsǧ|jzY$xa|ZxJڨGWwzFhxswZzʩʨh'{Y${waתG~IGD|$d %gꇬTZH7~4&Fͪ~!%ZMq~jJNpW*ڀ^f"zB_IUeگ;+FyuC {ذ:y;I; (TM8c8(3(:$& 0[R،_8R]HH,9؊/+[渏$28YȋZ.k -0ɴ Y\˰H $USQg `B(dk >M+IK[W erk8 2Y[,Jͨ+ [W+ڱ(p{0I-E6˸f8{+7[ =ݶފˑ}H{[޾yQ;+ͻѭ-|[ M+N@ا}t{ߴ+ٶmM޾ڤm؅m|BIzKI.]V^Z\ `a~b^^NfYjngNpf=\^vxLՃf9- <ÊB`mz~)}+¦-bd>Ö}9ΧK.;SIŻeƚLōFe>`aPA뽞<-r3gƀL3,LjL>5)lzB39^69<3^o\hzʨ,;>Cd,,符׬|yvsp~_ȟG Fϓ}ӟ5[uQ˴5Kͺ]Hkk%VU %c{{- 8#96'+ۺ/7I]ϩ.I%$RCGը*II/5is|d~t^~#֘V\I"cNO\_"&$WaVcl[-m\k* o`ӫ]134޲q!/s*r:Ld{%y(h=7 o+M$u .|Cr'F$HC761ŏ"%$ʔ*W|%̘2gXD&N6s乳'Й?M4H*]ZӠP\3֬Zni*װbz-v,ZeM˯/պvoVl0bM"&t7z!̙/] 2Т//X0tΐ?4Eayvhإq6kW8_о][xju?Esw[;m$/oyY[/&Qb|)wb~)WF.6z [_"V`.@zd dx yH 0")X-X.-Xc,8;c1H@Ey$H֏#$LBI#=6[R; ÐI)撋AIMZ#Yyi%\Y㗿}0:(V( h1袏&zFZ)x)XVRZjfz*.VꩬJzin맹2 +li+,[&۬2 -U-ʞzfKmBe..^.Ⱥ|j/[׻rY0#\/u 竰|H,0q`Z|1¸qg!;<2s%0q/p3 לsA =4DߵE+4mMKBF=5CWqY{tCE5U9]vRg]kq=7u}7y7}7IV8[nR/x/NI@r|%SHdR2j\"ɰl-%)AIQ.tI+cʴrd9 bPZڒИ#xNJMG7Ղ djLǔf$7|fLMcr;1),f6 6;K .O%{}Bl>O~s GOxڳ2= (!gD3 %EOL@(ZPR49EtJS/N?ZSJ[liO]rR9Ŵ.}OT<Ԧ4EjS 9Rm$T@錊UթLԱ)jP24t*LꮘU2عɯx5Ԭ +%},^NJb*JQy=gC2j-CiNZ5-\jl{.ri]ǂm Xm65ku3[w:o.f׺KzwXnrQ6D<[.,A`7ae/3jE`,`؂)w]n/ kA/-3k*Z)=7-H1YXi6A{bXҎ,2%d@y?F/iB#.Z2,1f>3Ӭ50,9ӹv3=~3-Awc`h=ӱЊ I97uL|EsӞ4LԒciMZ}4-hY:֠5shTzՀg[Ϻս5mlEZٺ~6A-jKϼ!bٌ.6=kgG]_ږ{nzܙt}};vwm'Sz.Mx~8mizxk{#.WєnKNC?𖏼Hzf9QmZ˺ٕ9ғ3鹎2ԣ.SVzs^:.f?;Ӯn; (dn]#w ~x詽sp#u;_[>Ͻ;Sx;;OovkiybnRA5ֻ~COz/>o}/;ǯ{_o~翾 ӿg_u^^q= _: `^ٕ_a !^e`u U ZJ ~K`) ^mž^_ N! R^!fn!v~!! ^_m ɟ:^_i^!aj #:#>$J"$&raq%VNb(!b)ڡ,f%.b)(6-+B)z_ cnb  v" .R",V#.Rc5:25!4j6b#3^b4>n3_B: 2"-#9^+(b 62;aZ#4Jc7Z#C2j$,ʠF`$2#&" *#&$LV#&f$+IF 7#FKN#/c9bGn=>'~+&eD!GJ#?-#;j>~$>P`!!aU!0X$%\ƥ\%]֥]%^%].^_B!3dyeC:E2a ba".fIeY!d#e#%6&?eEf@>$a/v%uդ,UT"#Ukfb^F0&lVjK$5.`&#JQ۩QAfi j2'O^:#vApDRe>^pdtBF%rS"cN7jeuR(J%B.Z_6^ vN)V^)f%d`xEZ֟bed>&b[>!z&$"鈦x&(wh8F*%߈Jh&Uydm0r$ $y_K',hOާr62m6㉂>O#~#uV*>SFjz#&"+vJNfgKz'b*)a&BRV':qfb*)FnFw2Ta'r(|"j;:&Dh)➞"hv(uf'$jjzgjೂbka% lΧblǂ)<=n)ʦʮ,˶˾,Ƭ^cjOvƦ%- m«.9a{c,N***fը~-mV‚ٞ-ڦl;free42-nologo-1.4.77/skins/SemiAuto42b.layout000644 000765 000024 00000010216 12110237246 021254 0ustar00thomasstaff000000 000000 # Color skin for Free42 (Windows and Unix) # With single-click activation of shifted functions # By Jeff O. # Top border added by Gordon D. Skin: 0,0,276,325 Display: 7,18 2 3 788A80 0b2327 Key: 1 14,81,28,20 14,81,28,21 14,80 Key: 2 58,81,28,20 58,81,28,21 58,80 Key: 3 102,81,28,20 102,81,28,21 102,80 Key: 4 146,81,28,20 146,81,28,21 146,80 Key: 5 190,81,28,20 190,81,28,21 190,80 Key: 6 234,81,28,20 234,81,28,21 234,80 Key: 7 14,118,28,20 14,118,28,21 14,117 Key: 8 58,118,28,20 58,118,28,21 58,117 Key: 9 102,118,28,20 102,118,28,21 102,117 Key: 10 146,118,28,20 146,118,28,21 146,117 Key: 11 190,118,28,20 190,118,28,21 190,117 Key: 12 234,118,28,20 234,118,28,21 234,117 Key: 13 14,154,72,20 14,154,72,21 14,153 Key: 14 102,154,28,20 102,154,28,21 102,153 Key: 15 146,154,28,20 146,154,28,21 146,153 Key: 16 190,154,28,20 190,154,28,21 190,153 Key: 17 234,154,28,20 234,154,28,21 234,153 Key: 18 14,190,28,20 14,190,28,21 14,189 Key: 19 58,190,39,20 58,190,39,21 58,189 Key: 20 113,190,39,20 113,190,39,21 113,189 Key: 21 168,190,39,20 168,190,39,21 168,189 Key: 22 223,190,39,20 223,190,39,21 223,189 Key: 23 14,227,28,20 14,227,28,21 14,226 Key: 24 58,227,39,20 58,227,39,21 58,226 Key: 25 113,227,39,20 113,227,39,21 113,226 Key: 26 168,227,39,20 168,227,39,21 168,226 Key: 27 223,227,39,20 223,227,39,21 223,226 Key: 28 14,263,28,20 14,263,28,21 14,262 Key: 29 58,263,39,20 58,263,39,21 58,262 Key: 30 113,263,39,20 113,263,39,21 113,262 Key: 31 168,263,39,20 168,263,39,21 168,262 Key: 32 223,263,39,20 223,263,39,21 223,262 Key: 33 14,299,28,20 14,299,28,21 14,298 Key: 34 58,299,39,20 58,299,39,21 58,298 Key: 35 113,299,39,20 113,299,39,21 113,298 Key: 36 168,299,39,20 168,299,39,21 168,298 Key: 37 223,299,39,20 223,299,39,21 223,298 Key: 38 7,68,43,12 7,68,43,12 0,337 Macro: 38 28 1 Key: 39 51,68,43,12 51,68,43,12 27,337 Macro: 39 28 2 Key: 40 95,68,43,12 95,68,43,12 54,337 Macro: 40 28 3 Key: 41 139,68,43,12 139,68,43,12 81,337 Macro: 41 28 4 Key: 42 183,68,43,12 183,68,43,12 109,337 Macro: 42 28 5 Key: 43 227,68,42,12 227,68,42,12 139,337 Macro: 43 28 6 Key: 44 11,105,36,12 11,105,36,12 180,337 Macro: 44 28 7 Key: 45 58,105,28,12 58,105,28,12 215,337 Macro: 45 28 8 Key: 46 103,105,28,12 103,105,28,12 234,337 Macro: 46 28 9 Key: 47 146,105,28,12 146,105,28,12 0,349 Macro: 47 28 10 Key: 48 190,105,28,12 190,105,28,12 23,349 Macro: 48 28 11 Key: 49 234,105,28,12 234,105,28,12 48,349 Macro: 49 28 12 Key: 50 31,141,35,12 31,141,35,12 72,349 Macro: 50 28 13 Key: 51 101,141,31,12 101,141,31,12 106,349 Macro: 51 28 14 Key: 52 146,141,28,12 146,141,28,12 136,349 Macro: 52 28 15 Key: 53 190,141,28,12 190,141,28,12 162,349 Macro: 53 28 16 Key: 54 234,141,28,12 234,141,28,12 190,349 Macro: 54 28 17 Key: 55 14,177,28,12 14,177,28,12 215,349 Macro: 55 28 18 Key: 56 59,177,37,12 59,177,37,12 239,349 Macro: 56 28 19 Key: 57 114,177,37,12 114,177,37,12 0,361 Macro: 57 28 20 Key: 58 169,177,37,12 169,177,37,12 33,361 Macro: 58 28 21 Key: 59 224,177,37,12 224,177,37,12 66,361 Macro: 59 28 22 Key: 60 14,214,28,12 14,214,28,12 96,361 Macro: 60 28 23 Key: 61 59,214,37,12 59,214,37,12 118,361 Macro: 61 28 24 Key: 62 113,214,39,12 113,214,39,12 153,361 Macro: 62 28 25 Key: 63 169,214,37,12 169,214,37,12 190,361 Macro: 63 28 26 Key: 64 224,214,37,12 224,214,37,12 219,361 Macro: 64 28 27 Key: 65 59,250,37,12 59,250,37,12 0,373 Macro: 65 28 29 Key: 66 114,250,37,12 114,250,37,12 34,373 Macro: 66 28 30 Key: 67 169,250,37,12 169,250,37,12 70,373 Macro: 67 28 31 Key: 68 224,250,37,12 224,250,37,12 105,373 Macro: 68 28 32 Key: 69 14,286,28,12 14,286,28,12 136,373 Macro: 69 28 33 Key: 70 59,286,37,12 59,286,37,12 161,373 Macro: 70 28 34 Key: 71 114,286,37,12 114,286,37,12 196,373 Macro: 71 28 35 Key: 72 169,286,37,12 169,286,37,12 224,373 Macro: 72 28 36 Key: 73 224,286,37,12 224,286,37,12 0,385 Macro: 73 28 37 Annunciator: 1 8,7,19,11 8,325 Annunciator: 2 30,7,22,11 30,325 Annunciator: 3 54,7,25,11 54,325 Annunciator: 4 81,7,17,11 81,325 Annunciator: 5 100,7,22,11 100,325 Annunciator: 6 123,7,6,11 123,325 Annunciator: 7 129,7,19,11 129,325 free42-nologo-1.4.77/skins/SemiReal42.gif000644 000765 000024 00000017562 12110237246 020330 0ustar00thomasstaff000000 000000 GIF87aK19J?KrBJZ^)1J)9kss!!1,K dihlp,tmߦ|pH,Ȥrl:tJZl3zX贺X^8-x:~~L;{k_SG=|ung`Ys+oID?: l AdKÆB%q"aaj#GT? )RɒNm?./Y%My3o:ʩ3͞~ѣH*]ʴNS@zb*VȪU׫_M:6jYgeVڶnq(0\ -:W/)l틼 B_U0wDd|cE,"1ʏW{aҖ5C,C0άg~{jظiK}ԯKW\Eetۺc2g0ϘZ8 έEd޽} n^wۖn6{4_ >v1v Gb a `ᗘ졨!yI( quuԅ긣@iH&yL6P`CTF9eX2yeUe5| RbbICcƀ&g en: gZ*dx{6)Ýz. &&0(|F¤iiQdj򹀠g(jiRLF`뭸u^ *lP+&Ų F*Tk؆۪jZ.:;6+,kҺ ,kkkn/S:`/p7|0)p O0(Hl1_h;#7 ;{,)%,r#24<& s\?l˗L3@ MW̰;=1+}V_J`@b v`cd6mr}nM@kq7w8MxkN7ېmlx^}ق䟏9F͍9Z޹uWκzب?w݋9G.<+~;/}?/wS/=\?;>g/;_Ї?*k.߽ܻ>_Ӌ@o|L$p} ?O=i}`( #TMH? Єҳ8j18dFPn> (D DLCe0YMD%D'. W\">Q8a @2h<҈эi\#F9e#=Q6ح p@"XHrq#!FIR2%hr5%CI@*UB@bfIZҖr,o[20ilr,fĎL_vґl&/&:Qsքe6ilΉNߌIrL:v~ @JЂMBІ:D'JъZ4ygFrt'b8QgH)я/}hKgҖ.iHk:ѝ´ KyS&(MgVӡ:2u*I}ӛ@jDJթԨ\5QR2^(RjR*VֆiKײUZַժ`-`л.rŨXWNV95N{М:,XGYUzMUZY=,bOU4-eػ)g*[mW[P~idTjVY2?F;Pz.Fm|Kͯ~l.Կ pBP&M#x v5%L'bTM:0da^S.3bR%_bvJV:0c/J6.mֶd yȹb;da)Ŀj2,)CʺzCQ+`ڗE/yK&&W1lWsҢ g9yZ+YԎV,kZD)kX6EziFcZ P zҟtVL#[l^ܼ=Kyï?'{t{Hн;|??|<.??p=<}ڣ]zGS|>~b,Z}ג'|mp7aUx ؀30PSP؁_ 8sD(81*_3x /Z5^^984:hF0HXG;(P5ӄN CE4ȅBXJeYu]A2C؅TK7qb8PU8B]5^8kHeYlUODx(}]XRlxn^L臒m@9~ 6b`HakbqbQDen"K9Ia{/c\an+*~[)_nai7cgc]cl)0}\iB*}ȇe~Nt\f . rٖ;7ovfIƗ;ggReK-/gf" .vgi.xv- 2CifkKw06a6ks9#C;3 lIցY+C3ƹ0ʲ&kЉiljk8SmqcCo#oqfppFop#f Uqqm|; CrIlɠ7FӞ oy` *3r 93K'o%o)ʞ'*nqqʡmAA::sDtT@;j=Pgu sTs,DuN?4t8סOA0trQWSu[wuSt: u=ǣJQ*[bBԦ~DggyenyGDwanzwtj|~gǧE$vtZEVxerzy{ꨂVĨGzGDm`g}sHu$gd{tDdǘ ${Ī$ܧ$F:GW}&Ff}$׬:Lc ~ъ*Mow:JP9TBhbQiGW:ZJ85ɓv,;-;[ʯHS?*ɑUqXSH,+ذ"{DQXQ] hh;+!K {؎i1XXLˍ ر "xN:+8uRP ٌ؆Y+R˵4 V]+09?K;kZI%d*+{1K{˶< Ȇɱ5q{%V.P۷Y+SX:ˏ;[kxېk7۱Qm{8[8RB !#Oۇ wص[ iF[ [7ɷ;+{;E8LHIYd;:a˚^} ܕ_|zb8Xwt>`|˻.;JI<=7,zڦ`䨣t zڦ뼩Q*zj_^zܧȉLϞz{:HJF;G-|={Kz(I1jҮG}6=9}+-؊CM'H-LGMM e+,L\^`b=d]f]Q {;o sm냐sϫk\8ˑ8ۯh ؑ,Wf׷h~[wmj0KVX\zkK۾rXۙKMڧ٤]ڬ%[mShK Kn[( }K݈V%-ܽO(1[;&{GZ u)]̍+-M-[m\ ;w]޵« ؓmW W".kސ۰v]8l;4{F~JLPQR^NNVIZnWn`X=L^fh>uXY \xBRmjv^(m>”,}c^†m+=--E; ŭW">=fd>R aB3ꭾ., 3Y r,2xLzlǽ4qIǿ>^l<03Y>5+*3^a\Z\6zȐtʀy0ɚ :>.N6|ȰL<~_??6NA_6B:{UZP'8+~Oh@{~-\ϵ]g{xU9ݑE Ј#Y),*wኯ0)"4*R ѵŎ[-[%鐻#pFEuڍ{:_?`W X[L]L%LL]^JЧܓ`i!aWaYc_d,-^Z)qqq""Y,$\-[/p1`b`.hwh{vw?@2,Ȇ )lH* ĈRðbËJ#Ȑ"!(i$ʔl%˗2eƜi3e͛:WϠ@Jѣ3L)ԨQMi**֬TX1]Zjɲj-逸rҕصzVo~"x¦JFPghl2 d|yʞ1'l:h̠0N}fןCi-Ջ-C-2"qYqq1\̑s77Q 7`} ;> cwU}i7~XЂ"4AFjX@!(f#('Hi+'#0xJ"#A١7Έb!7I#lH>btQƨc< $\$M: b8e8ib-^qv9qyf{'iZ}"j硋:*(IZ:b=jީ飝zi`v**i1ꥱV*z+n+k;*,[X:l*+ Z>U>l`zmߚ+v A ;{+oʫ*|0ю% K OõY|1 1zcYrrU/﫰s%=p39s[3A @Ϭ<4BI;4DQI)[K6#}6i6m6q=7uGD!Ev}-E=x y42'xҸ:;@)+83 #F:SN|"zGdn6D2h+: OG4z7s11naɬ.ּ DTޅ[@ ob1~zyP~~jNJrG>ǽqX="${=اdN^u)𕐅0_)FytDķ~\1׿0 3sP!J;g>@ W,M"E0M!#ӨFl kdx7ʑo Hغ&ǫil(YG6e0paC, #G"-J _'ɽ,`- UhX8KlIjy0當*X29f,%C]FGtdZ͇Z)g 87Qpr9DsH:)@h@"!|s|;N~@C@y'EYP}h#EYԡ~)08:T%ɣT(JSꢑRcrKEљL1KWjSi$RiNsj"ƴMgNEԚJ3H}ӒfEVPPz!UuSNEVGjUj+Xֲ)Uh5\ 'UtkPwMkaZ*A֯|[ FYj:V@{+n k\"mrʳrk_+^6Qf=lB+Ummpu\qeYբ+\.,ַ2Xv+ /_.|K2\e|;/ڗ^_n/\&)xꊙ˕?nܴYd"ʩi"χK '*} Ӳcoxǭ1I`k<29N~2,)SV2-sl2,1f>3Ӭ5n~3,9.3f9e3}f>$x.ɗ E3ю>J9O:)M̀tMѦ>5|iIk͊L\jQsխ>kgP:վ#]WyՐ uMkMzϴ~Ek`SrFɱjc(c49lWKъu&vA+[.6G nR?+-lLk'vs d+7#oBy;3nZۻӲηq\ݟn uLŏ=P%9s!;free42-nologo-1.4.77/skins/SemiReal42.layout000644 000765 000024 00000003632 12110237246 021071 0ustar00thomasstaff000000 000000 # Color skin for Free42 (Windows and Unix) # By Jeff O. Skin: 0,0,276,320 Display: 7,13 2 3 788A80 0b2327 Key: 1 6,68,44,36 14,76,28,21 14,75 Key: 2 50,68,44,36 58,76,28,21 58,75 Key: 3 94,68,44,36 102,76,28,21 102,75 Key: 4 138,68,44,36 146,76,28,21 146,75 Key: 5 182,68,44,36 190,76,28,21 190,75 Key: 6 226,68,44,36 234,76,28,21 234,75 Key: 7 6,104,44,37 14,113,28,21 14,112 Key: 8 50,104,44,37 58,113,28,21 58,112 Key: 9 94,104,44,37 102,113,28,21 102,112 Key: 10 138,104,44,37 146,113,28,21 146,112 Key: 11 182,104,44,37 190,113,28,21 190,112 Key: 12 226,104,44,37 234,113,28,21 234,112 Key: 13 6,141,88,36 14,149,72,21 14,148 Key: 14 94,141,44,36 102,149,28,21 102,148 Key: 15 138,141,44,36 146,149,28,21 146,148 Key: 16 182,141,44,36 190,149,28,21 190,148 Key: 17 226,141,44,36 234,149,28,21 234,148 Key: 18 6,177,44,36 14,185,28,21 14,184 Key: 19 50,177,55,36 58,185,39,21 58,184 Key: 20 105,177,55,36 113,185,39,21 113,184 Key: 21 160,177,55,36 168,185,39,21 168,184 Key: 22 215,177,55,36 223,185,39,21 223,184 Key: 23 6,213,44,37 14,222,28,21 14,221 Key: 24 50,213,55,37 58,222,39,21 58,221 Key: 25 105,213,55,37 113,222,39,21 113,221 Key: 26 160,213,55,37 168,222,39,21 168,221 Key: 27 215,213,55,37 223,222,39,21 223,221 Key: 28 6,250,44,36 14,258,28,21 14,257 Key: 29 50,250,55,36 58,258,39,21 58,257 Key: 30 105,250,55,36 113,258,39,21 113,257 Key: 31 160,250,55,36 168,258,39,21 168,257 Key: 32 215,250,55,36 223,258,39,21 223,257 Key: 33 6,286,44,36 14,294,28,21 14,293 Key: 34 50,286,55,36 58,294,39,21 58,293 Key: 35 105,286,55,36 113,294,39,21 113,293 Key: 36 160,286,55,36 168,294,39,21 168,293 Key: 37 215,286,55,36 223,294,39,21 223,293 Annunciator: 1 8,2,19,11 8,320 Annunciator: 2 30,2,22,11 30,320 Annunciator: 3 54,2,25,11 54,320 Annunciator: 4 81,2,17,11 81,320 Annunciator: 5 100,2,22,11 100,320 Annunciator: 6 123,2,6,11 123,320 Annunciator: 7 129,2,19,11 129,320 free42-nologo-1.4.77/skins/Standard.gif000644 000765 000024 00000025020 12110237246 020205 0ustar00thomasstaff000000 000000 GIF87a.@,.0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.n|N~{(ȯϓոۣO "\N!ÇBM"ŋ,bL#Ǐ< L$ɓL\IK%˗\œiJ&͛lI'ϟ|:H] Ms)ӝNތ*u&ժ/b]uɮ^G q,ٍf^Lv"۶](wvo/}~ ,8^(^̸ǐ#KLVhqz4ҝScu a4汙 ӵA~[umٹg[A/(br˙|ݰX}w˟wtq38os vxϽ;O" ' v߀교EX1WCbxz"9p"%bk--X@X;#-: ANP Bd,IEJ%夑FTRfY–8Z%^ &fI=Yn 'bι&v>t (xީ'P(pgy)~Nꀜg`.Pi2`꫰n颊vhjꯢ~> +*[l"jp.Km*cFK k-PТ'鎛.Z򾋯B-[/ {Njz {Dzjq- @ O|oll-2ר;\-%=#s7ܮ^tA;l\Ѝ>[t^а +k7{jD]0ySݜN@J;]5d\[KM=rY9E v'4ۜfx+t}浻.m][75Mz3^2ƻ/.ީ;c^^L9;_|ԅcg/?\?>Jw& [U&i?Otok<0A /TS: V›`n"|5. ?KV?LahH>+#D5GOQ5-QhWWmE+VQJ]H2afLcŨ6nh&:±n>IxppCЇDYgȀD #EFg$86Q$>!yH;t(? urDnVNV:<#9#i5l|ZKH.)JfB4OXSDς NK2$ рR BMxRCڬ&uPNLgz HIzpʤXԧjO4TWM9mQBp*=xDItjTUjX+nUjT8^jDшmXTծieWWU}嗯엹d p c>hӝig=e?ݎ>8Y_lM tGdCXgvxen_[aη-۔\R/|*w;ދ-tݮ6m`\&;F׵lu9G^ʞ-j{³WwK^m+:%[VE[` rq|Gݻ8v{ 7|6t9lbW-8$1j9:Vy9^|7cBV }|\SO|[S^qm|*y㘈c!0kqlΡ_+]{Q|\EO#]Jw*f4\.@EV:Xb6lj.&lk,{kSŖu}ͽ/|m,joڟ Fl]7+<^cҝu[~-2w~NE8 >;\ ]|xe{?iQߚٸQr'g#<1jrü(9+rAyҗ^[iXrghp'Juw=Z'z/b_])qnAǸޙqdGˮ\W:ڽY?;=$JfD3]~Wȿwz-~2Wr'^}9tDRrǀלߥ;9 3/{Rho.;7>w>r4+v^`T}?{w~z@WyhDgy5F|i&[u%52/C톉%]>heK$3ZθjzhFXaYg_ul6YVhSƘ4#ͨAFՋx0:;։~AD^i}FrHHF:9B]fِ(h(;M&l'Ovli֊8kfɆ)I8vxfA)ՍfwH8I5lU WHlx6V#kM)-TiZYm ki8RtExyN{}YZm CnنꢘXoHYn9X釗I(qht0}PwɚR%WNЀiz_spзفx7zٷuҧ{̗JEK"pv'~gלةy){i'YϧptPGwoޙɞyspǟIy:ǀwg J+y霺Wښy9 y x{wǠJ*Re!0pu3&zqZu4*7~٣ *9ǡ%Q%~C }˩Ƨ@:|m p]|I'z#*ڙܩǥĩFzV{ppZZ|<:Z6Ő,S`L 30`D]  ,*lR*Cz?e/JŚ1 ʫ:*@j̺lJHo)gɆ,uI𨬏IG(E֮Y讚 9U s(zШڅE) yګ[Yʕs *h ^(jג;˔Jʈ 9:"\QI6o{k &+F?{#6 uIF>۲.+/䈬')ںkʱ [grH9{m˲kU+صY++HgT˷ok{돏{GC+؏;eVk ٹk[[ֱhIvɺkN˷뻩m|JToțwm0n۪kkk:н;0{kܚ۾߻ [ڠڿS<4駬y=z{2rrJ pBW9r: \z}L(W >'˙JiPLL2j(|"xLPEGx,}g8U#t4&NϜ*⢉̡J. H${zȧi̛@Nk1ӭqFTm(M@P*W__&[_]_dJf/YϯŽnp> :vk/bqج(oWq_sߙN=3 C܍ aMل/N;oo3?To&r,_??BOJПlyc_Oz ߿~OIت;Y(Dd悮0olcsks p"9T>̘Zجv3m'.ZM^ݸ|N~~&| w ät˩ϯ˳{ ҬЭvȺ꾁׾rzʷo8}dtA<yG|޷~ ,ة{u4U/G|7u0C'3X:eZU.ү`kQZm؂!BcuӨm(Wʍ[9gCa{Zׯo-]:ɩiS[:ͺװc˞(۸s=!qݽ_~< Ё;?8&k}u|?} "'%[OC~%'=z;7!! 1!>qa6砄!!R`y'QGʵ( haō#ָw^\ Xcs81:P$X=Gti# ZzрU, ]ڡL>bɀi2/V٤,zI{A:>&n:s&؟9^)"YF'a)@*FY)v&iqz:dpJl:yR*do:k e`k_*v(*,zzplpPkJTZmjب]|[g)rz(J.u^-n(HVV *[(>,"ܫT,,2,3`f\/(LÓ)BByf&̣m>M߉ a3 ""Y.SzGk"}%r5v-uT6-⎫iy m.枋kAYꪯ~z뮿An/o'7G/Wogw{A?꧞~>ӡ㏹ d@`@ޜ4ς # >`TA BP*aOh C*! WBfDb؇~|a0:p%6 XOE,4ÿ3" ,aMd,:4ڙ`Eʎj<I,Mx<#xhE:bl<FHR2AۊHQJO1e OZr#JILc(JV2DexYBrd@.MQM/GfCID5iGsj\>L̙H$$͈!9qr4]517)d4g=wF :I,tr$k߉3{^*a7gѕܴ% r2mKlYɌvY<&YU.E11R s5mfjPh"44ͥbRvE}i7oӑ?ɦ2%8I: dEgzkQ5)3Ԑs~.]OcԤUXhZs:Щnʏ .&J1"lDTWTj`ͿjFZ=4)]j%fD@}"(eXnvm-_ͷo.lsk[vٍ27}m+]Vw0.v&vߥ_x8^:o5׽5o|ǘwb~_4 "C\ Zӊq%nUYv co[a ånǘa'&!o 1? 87vqYL 5UȄ:La},;:YwLdY9,Gz,}jA+tXf]ci(ΪE |?n(`Nw}0(ugYyHPewnuG38EQlD0"KTOcTxkĦM'1FiWi.ve~tHrjut7pVKnvĈ93fDa2X{x&7yZ_x4HkhxE~Ņx.E }V/TW8/w-~qJJ0(8xc#)ԗ،ՈM9ģH̘wLux㡏y8!^G^] 9y >ESve^y(nU{G _" Tbf:(9[r%-I2ْw\w`C:Iq)De5)A>x% Y|rg@yv)_9Zi阇ߙ\)PIyٞrIOi'⤝:/CjiʁG%ZLIy)}#*)ΩĹ(:)8z'ʣ JAZ :,b`FE 4IеATZPzeZjPg]*ac*c 5yOYn3[0Y!$ɧ)X(YI1MiwI 3z>:ڡS)>K٣J5 ʜOcX-ڍzNi'^H:8Jʫxši9*;jJꫦiʊ~Um ɠ:Gseʤij2ʫn*+ڙ ZꚰlZ޹DNʟ+Ize*in ذ*[HZ}zδz;4WC[LZ#+AF 5mIMٮ<?{'I`JNdPɒʪ딚mRvJpK곕g۬5KCڨ k;kڷIa@gj]*Œeː[y{zc:p>I-iIgWڧ"9˷y;[{+jpг̻x[Wٸa۽\kv+:oޛ ƋBjZ$; +Up`KZ '̲+w;{j l۱++Ok"*:N;YW"Ê +, 9FCûX)T7-"4(;ɴ\Л~Jl&7;wQDU|Wܵ^ŶzV&K l3@6.L {ȋ[\܋ɠ|@ <.[۾ ʀKɛǝEj$Qw{i<}сӷ$[[#ԭ\yԻlʖs L ӌdoB:7c'yի)muo|b}Ј\c4_h-*@+@*D]ҹY&׼Ԍ--hm+kiE+.bu.jӖM3V3;hc"JOH 0ӷ `sM'k&/]mҭe[ݰ ˶ם(ݽмLץ\xRKPļm=ɁIW>~QYO"W}$Ņr2&^!\:fn4>7^㴣XދNp.XA.sR8NPFH*,.:>BAHI9EK&29.36DKN(02&.3:BG247EBKM"(.'-/?LQBLPgnr6CI[SQ!(+R^c`bd6AGDOSoz~JRU,6<,:@ERV)28?HL28;&*$/6$'?;<).1)-16?EL:7FMM>FNSLJ[>9>CG EMQ.3;3;@#+-!*/%*+29*.0"+4-2:@HO;DK$-5,.1,39$.5#,0#&>IP$*-CGJ!$"), $",0$) .5:$+&)#(. ")0 $' *0%&!$ !&$&#',.04 %(,!"   "$,4 "#$)/-4!'),'06+0!!)!)*05!(&*/7  +1(-")&-%/18//$*3*0*09CIK%,!-3$,2%,1  &*-16)07$, $-2 ).*2; &- *3'-1@FJ#,6+39  (0@FF, x+ Aoޒ(\`+' %wЛD~0SI F'cqD#Gd ;,,ؑB=N(RƎyq O>I\ԩAQF+¨ jJ[G4OxxrƬI0pƂc ,xc$Onn`F0r-%ٙ!^6{o%EU gy<;辥Z УKNسkν^ӫ_ϾW^(ە& g  F(Bf (xq4((], H4`بG<)$|8iGL6Y A:)DS \2ya`ed8fhxflfprftR8gx2yy|~*|~%&! 8#$vnQz**wwj*usjeZ@|랺as[zlƞǬ&>;J)֊inmV-&1.5t.K>ڟ.:oF/z˯,l' 7G,ďU d=,!D@f2 y`aER\/l=)r0L->цJ|RW`R$!2E|ya]tZ8XhHXцm$V0}5%OD 5-""p$+Hm켈~tyOfL҅Pδ.{~\n7хgGha^4q:a"7M쿣x7Q ~ DDdD="=s\lKEm\!EX컟^߅7fZ"40 y$4,,omBa-dZE5@q#~ oDڰӱPeW[RHJ!H D86oX"!q2>У fP\0EE.up94htfrG=h#8dу$1 `Ƞ3i07hЄ"nh7@ErZЂtP0xC y"v Ds#vE2 2 ᯗ3(SA`[f5X{aiqI((,bp,* r /tMӠ-z _ a wB39\̂'> NX9|71.F5QhDN@ :$8F$^s0yx] QFB 1a@ plC ßּZXP4!mPh /ءRF RHXh *z!#C' >FLhn^ B64`=]ƻ:kȣ*^;" @܁`E#kibH el,6#آ.B;yN օ-b=E!9 r@`"in"0NsWԂ7<0 4Ch:-0-p<@&@h #L/}iEW?Bd hP _C7 T@F6SV}P"7t!+2+ׅ1@ {31$0 bVVTKX aEt`\WBhngS3 }@+A'@P #G`jKh`+Cam%5Y9xm-6~PasBR&ȀeXDPSB\- ]S&r+1pJ- r]~"\Dч`4A6T.hЀ@>x`.bP00c{y}aaW,RutNIM9RW?1C\o`^ll19 o&%['N3XAB^I, 9z~ipv@9&5> ^0e {GB>*t#aİE0 UxmvJb2(F~:[Ά7 tEg4 ^#@vԗ^M` !rx@ppGGE 0;J; N' `b ` y#7sAnm.M 5}6]dbUpa987ld`kw8KC@K :{d?zpCڔ6Xv;5~#cfYc:XL ' & bF@k-  Zy` E:  +>xSh P7M3 # l!ť`h N}j=X 2 y> fCV+0 NMt` pAp  ƶ N=&#!vb3yc2X3|m6+w z`cPB:cBkxKMIO@t:d6UCYYw|]c AmБ~`а (:@ bBn gEp9: [9'y svpt7 8@]a +#2p}abEk} h4z4V\xt5 `J vw`@y:p Bik xxrp:7 A8A7 qE6#XEM4y@C:)T7i6nwS㐫t 9 @WpjcE4 EGqx~zhL8#+ W_4Gk̖ ߘ zj[!P]ab-kVskN]Q^a Atna 3F u F: BprnNمryiwߖ{?l2@ N(2,v+pOA%Æ:+阠YMX2IXgfDd"Fy pD2:gDZ6Dm B5nSqC fJ9y; y40{-w0zkzJiР { ^ ِ  {p ץ #F0vӵ40 @0Y 4hwp ^w w`rw q F w Q@0;c`w@aЫGWN x8qn|pt@)T @4T2N2R`6^Z?^@8=jy G8tRЪktF>ޔJDȗ{laYX3X0y^BD3!0 pJ Ӏ ã 0 P3 Pʠ0 qʠ K lkkAP@F]@Ff O N0ʐ ` e ΀N@=^ ?@ :+PE Lk tCS5{B%z11JAm|K ^JK$Sf0A3p 46^@X8Vc7AjC}ajh1| c y0;ȇV׹+C}z?YCPDb$9tXC9b9C9YӴ<wEcF]IF^<ˣ;ӋzԳs:k92g% ,  `AVUDGi@ T``:2!<%|ž`@4e3LVjjFL!F+Vk1bIpE(3 ~p0L>1Jp14cJ04ƙ ]㽓@} D8Jc F;bDlf:־Sv3;Zmn 뺮0Ph9)d:`뼾 @z9` ꧞,N;YpNڎ⧾0p {5&p[ ` 0ܞg0F8C~5쿮 kpǟ_9֬N@Dp +~d`X-~`d`FcFc@f~〠lPjcЯ*M[_c?28m2]q~@ 9 p q k@K:pD0Nj0Z;F _Pjp *_ LJ] 5`d96p i`c`YeY_0!qY} J`fjdKg ;Ft"s6\J?_">0'U&pld YKNPR}0L-`nM,X49p',WF$I(UtI,JTemګB[`B˖3ht1SJBcq L7O4BAL.^h2\5ARqBV (TQŌMLQO8˄,-_T &MseZ U8gh݊ ٥\|: &tFɚcheN&NhUi֥TPF|"BTI1*E(P@E< @]j)Bj٤j,g> U`9DS@ *+c )S6DZn̬j'%it1ej6.) /0 ,fZLōZ 0! N$Yd <‚JKIKR.آ1iQWip@xYƕMNP`̸bN2P%h@PeUTo酚TLWT8‰ 4*PG`>#G:d\`<}9!(C%X >7v@0?e )a_ g6 `0AELB x5  D[X؇x`L !0p2&06 :ЇD Hȁ8PGD%f`0E! `R(C' `@ʗ#No!^6dDӀEt$ImGI b&PS8,i88}8 @p# L9 |c<v@b>;Ŕ' `1 0,e@`Da\ ` ")H@ ( .P@ H5 Bi`MaMj? oF tbģ8Ky (SB0A}X:^_A͐:,1hj򐇕50h&pH9hDf8@ B_P zA$STe !  %yK/A(H1_ *x3 yye< Ħ4/d^A (BP9_ ;719 (+e! F_,\ lP` 8]% Pk@`p*B@xq!Pc`Щ76B fY pi--Jh#f0 [`2!&`bg X !He\%9>Tt$BA,  h ;|n(^)EP$,_ @˷EA&8P r& bx)ѓq^t9"AvFŪ3}6 pP_pP@Jp`}uB?7>d@ Vl ؗ(PH,AB@OܖJB1> Np FK`PF-YH&M m`A-@dkQ 2!2a8g 67`0jd9ܜ Bp@ )SpHA-P$t\` Qp^e%` `p/D@H PAWp@"$Wwnz , }58xp/ @GLP>|(qR  po3s@,8m=PL= ΰ!q)qiHc2.2)0 @ M&g3 mg枘9|Tɀ ؇Op p.j4+( (K钄3$'2 Ȅ(X@;8r 4 -Hs9K[,)0&d$}Ȁ>o`80$`PG(P,{zwp K6]z  Xxہ(Ciø2N;,FK#0.2o{X6k3;8?s#FCKtxn&;.2. l# ( L, 2 ;18K@`6e;sPÀdл{XSnr^,H9b_@ *AP9E({j8Γp?R {E`@A] 4xHD"sw[E H9L7. YV5p Gps~p;(=(8X5PF"PAF+519|p 9(bE(,ĀL>HxA`(kLoX"$p%Gr (e& 0{0 n2( &H(I(K[A,%fu2H'J(Di{xS@pJɂ)اJc(p ͲFȲGƚ(HHuoQ؀ aMӂ. 2ǂp[s|dP'4,K{(/NC+}ۺ~ÁھA-1pA6CHƗ|8x%X7 87 72%0ʀ159w!33p 2L 3:;`Y8`@?xx%m7@҃0`m2+X;ò8a(mT;@MG)13@(o$MH/0W 3d+5-inG~P*XּKHHX2(C=̂/YK0AA%% ҲL,8<i  $@I k"ʀ,Q&7k,KR jع.R5,l ŇaH00EYS% F<ʸl|O+M^HH$SX"^8J8 עTRXㇼTi`[g# k hI崥Z0O|ML}; ;X;PZO`P(ą,uP{߼ޅ۾-t[s2T  ^!WXo^(hPC=TCxXD=eaY(c7(2L`2p`:J+hMn@޽Hb%. (MdSͩ,Sf^;6E(}8[%X&ȃԛMk6PR`( 3([X()[^."PN[v5pBY^]ZH+EeLX"(-KXwt੥ucˀW)&ْTlaw趆΂+8B7Sx`λK1(d8ܥ0[Rg ug"peH+(veiPb,X[N0 yԜaP+0z.'P8`MU.u- 8!/(`kIMâ,2pa5xb-5Mp[/2Mт*xchjN@a./CM4E(7uheȃ6,P2iлfJ#ixI[BM, +=;.- o D]$5͂|^,)AX-`+PxNŀgnрaДIP0P`8Q;t@&pHE.)aP] a^ )4@f5H6x^c X%ۂB5R1 6.h2x-E0XU1`$0&Hb\pyIPOk>߫P"6#[ր,@*9,ekR788P5hn+,0p88cj@pP51+.@ 0aSu8j|XXO>IXOXee_bHaI(- $j;` `a3) -,r Y1,0΂ n^85vU.pwlY91HpAG$#aZK> /6po>3? zR$>y mw& ,I"EK+(!( D"X dK$`#HË^̘bE"D2$R$+YdLIe̒ EW&UEI(@ӧԨa*52Y$ EPQ ;JpBar$@(Jع2,҂eayARX q R1 bQb LbDTm˂70Ç-ma/ߵ|"A$>@Xq浊I Ct7z… )N"4M@D@:#/h1W +6 Wb{,6!Aw(J!Hg8؄)`1 Z/` X(#`?8 8*>QT,58p5~AT#Gt"H-_H֐!@q I\b!ӬQnX`+.y|b;`!@(0F5"Q"@|:-Ŋm|( rAnO a>~0n 1l>2 F{ h(Vo;lBXELx^!TD*@~MĠ)@`aB ОT>@R;dw*d#8/q:G<6 dcn=dc+&M[;-!wVH=pFw np$[, OHtV`2 Z@o\@)| K18%37(d A 0!⑍\9(iZg"}G^PU Ј=b@B؃+F0B(:Q N+fC&VP!Hį]AF@t#@H8,#P3i@C,i{B Bx8vуCQ tP;H !hHu+͊)G4 EO!G&@UЗyxĐm mEku`a0F-5O9F0N9!6y0:bvX)A,&V V]AaA6|̑#0r`%`1d 10~\sD 0 A1\9gCw60ndePC hl2 2Lm!tکj ] B N#+D _P}[v_nR T(FAm9 eZwN8g r+F@L#8a`Brt 5QyPj ⁼D +F BY?(}XP2 oݯ=nAP[P<O!Z`@i lT0sq8 C@Ԃ{iЂ*BPDB 耏uZ; @ ;]@8 T"D;ͧBp X\vY` }J 8<^I}^D13p݄ǡ(l!@VMi,) 2 40+,` l*}QÐ/h]4h8F6͗VADEKvedӖpPG? {ٓq]XH u@v@1܂j|DK=TU+ PN<XBI++d"T^ nVT/Ā3Tí$@"&EõG+0C;ZPB6B1B 䃣f0U6&BT0;@$ ^]!^10Ca8<1)PZJœ8tAqV@A`@+Ё0|@>h0h;XJ89p)\C x@= 0 ,C;'',|O'5RD8L2d%d5X(8/pB*XC*\3%1.쾮+rPh< >.<.@/C5 TCڼ8-(@(>@ (:X$׫G =\Z//$ @ <x,X^@-X\R/ 9|0@L,Cdj $OHĀ#88ޚC8B {{Hh{ 0}к𑴇'Py Hɿ(=9A A*L!8:o@԰l0ȃ-.>8v>çD"%1qqZ/x1>XZo/l ? -@1<SB+lb*&C@'2>(29,@/-w-4L-B'j'\6 4031%C5//$@<@@ %|Y358C5!` 9<@L+@ī$O7Ǐ,$A# D*%sg|8DSlBò;ԃ=(<0!N4BN18 3,PMȁ0&΂G}\4ЩPX̓TU[5epW;n>r180u*/@2N=b=@])h”\]B_I8I"7H@_C!ACij|0tLW5 n׀o6Adh B8;%‚{K||@}@ TA*@',1A-Ȃ1ÄB'54@!g2G2\Z%>H3j-؃<<$\W;\%,*LN}%H5API Ըl0ԂB-0%;8ҁ<7 9sTtSt>(9,@L6c.H4|4&: 8.@lp @H`KvժEBRP.Z]s PS1 DDч>8A 5S`@t  Ox&AL @VADHPvg5ɰL;z@ ( 8@<֗1FH!K[}A `@` ˰!@aeH & *@Ngp`ѡ̃`bH"5fS@H,[|gD>P Lf@@舂Hy4. Z0 o@қ 'na` رj81V3QK"! JdXpa  -EG/X1 IL]adyD&8 Tp"Bz\pɢXQnj8Mܹ \cxB44Jyn,A Yr(@na v @.ykn.HaGϢ@2!i8 롪D`<`2@,l,Nƀ{!񸁲J@N!,xZ-d MANvٶ,aza 0Ata$ J~h,t =8tn` `Na1A la \@*`$`NAP\ (` n@:J2za m`B ~l-/2@ @rhK*!O@@!,Q2a A"aNdA aP=vJ0!N6ހLgƪ~W'v cAϬh\\ !:A``n f`` (o .!ԃn`A{f@# `l!2L ` .܁3= /, ` ^ J2@a 2@p @,) zAN Js-: N=Ra-%)Jv%߰>%2I[hf@\`,0`)d'1*\ܱfb!Ɗ!YvE`.e)*@ ؁ N=,@$M3G}T=HY$@ªl nnvָ!za  !0@J3$In.a8d*8mS`Z/!>%Nر€ % %Gq!^,ifNp!AAj !`B`,8!.^p@  J` fA ,jJ j $:!>'kL#& ` `I d,@s@!2 i\ > :rhFNM*` @pP&N;tpa.k`M:2$`#`\6h  @M=f@,UJ Jqk)k'$. k1`⢧pO8%q>%P q 7lAaXgϷ``JnpjiL#1:)xr־WjcX7|A ""p!=8f-L4!؋r^קʰkka  724r6@82m!zRr82q kL0( x᪒gAV8"3@4R&tT w lS*<HW%`vǦ>(!tp%`3*O"u8A$t@IƤ@^ؒh\Fan M`@ Y b,`@Vwj?M%MFy%x,sv#SA1k LԀ5W0] kԣh^`e&Տxzا R bi1j 9#9pM6ش^ k$" >#);WAA"a 7`$A-Ѹ2aJ!>r"GHZ ց4zO)' PX] K#kWV-: D!5yca^`kg+1W?@ƴh!@⠱7R0A8>Cwx1aa&p!9l%x|{B`fHAx[=\!P!DCTa^Y9Ā^) .A 2bEhi8 ~qP x `a^ek Āy| F߀T̽V`8"@k= d k LӀ P(қY0]ma`KK`:^(A!T Á{M X BxA]LUYa!<`l}aڝA?a@MNPw<@T o c V}!VC(!6{Bҁ<,T "36?>/aKݴa`aNA B FJ> A @ԀAGKԯx o^a^@@wPaB4aAaGA <@@ `U!⓲9uP-2FS %jXKڲe+SLe ',voİt XHa.Z5PlcˋqC*( 4.'l0I6 RK<ႍ:NL$gƎSb&2̌AX¶xjܺ͡G>zuz*R@T%\|Zvԧ৆ի7$8p2DmN޶a!z@zѐ0+E 8lZ#8U8>Ѱ73KGDilUT16D =@B؄N<PDRL9TTYV\yXdZl\tم^|`tf)ƘcIe(;KM 8 <.⠐bi&j*#l /<G8 G|OA95 `/TAG ĝN{"19ӎ=18jMq=lPF >M!:M lDB3ư*1tF <6 $"2J5T5`B+A(°2((U([ e(;| &ncL0*39\< $WFAG+F8 S1e 0\8Hp3S89lp+ECIhsmOh3N=C}tK7K@H=BtCq,G]w *8G=i@("NID{`@;6u֎c@78aj ƦLH @9A~[IOȅy8adtA=D6 ZF1*` l, ~aF#"1-p 0 `bC\g;yg>):AuH3:hsƆ !Bp)tNHI* d\"$%a S ' k|q!kl:&رj( VPF5c !cCװEACPAV@B !0@0>A7htx1@l7t s9@ V31Ca J. A@YeP !ܠ941 q@[8@83^@F3esH B!!w@l+0A9 @>*;@q!F&\Q7AFqD CAV=h@er=Da \@M/Xr(`rH D,B.Jq ؀ pȁ,f@9Ȇ `V>[`8h:L$=0G= BժN] kY8yZgBX t  *(cFd!dc]Ah`b;x'̡1.B` 9Sw43 s,+@v1nE<00hbM|@Qf < 0a#Ȑ/YzXAy(AYA2CloЊ?o 9\luB6 oh +X*6`HlLCm P[ڭC{Bqq={.]N9A}# {AЎ\3MbAb|9+N9&p LY,Y O{2 *$ р7@o iKbhAĂA Wx|\C ڈu>@Q:й\fcX(V@YA+Pv9_>pP wpu`k ptu vP EgVA@ 0 =R)P5.sdԃLS6apM7JwpJ>0rt?lC ! ޅmBApz{ t+c8"P{pJv~2`  4 `:1 {pxX4pEЄ Ӡ 0r`tdVYn  =P[ ed7pfސl!g+LB\ &f!'rKW=Pld(u`F y ۨ @-0Xl 0 0/ SH EE 2$]HlEmAiB6 ^{` B`h8 kt2}`Mg $ E| L@/p9Ѡ hnt}Р gnP}72@J“ =s';~a MVA@Zpp \Y r0JHBhpϰF\@M5~{dٰp>@7C&` @p iR 7UxeLXl&{"*lpp'p{7@ouv7`&hrvl @irp wXym6i  , g $^E` ?  _PEnY40f4j$ @W0 xh`^@dM da(  Pn &#QpNPNp}=>ft1 `g`M6 f wP P 0` Fpafr` ϑ |BrB X8S5C bN l0X "Ðp@  %XG%P WQG`^rw@$M @V)L<#WO<@@V%7$C SGS,n>\PLT1tpt`4\O` `  @ ` Ӑ /?tp F@5.-.0`G `l E`UbQ)Pfπѱ 0 @@ @r #  ð wP b@ a 1 p `ِ ŀ W b  "" t`"/PIo =O F4s0 PJ'UBI G@r@RLeغ*LT*L0vv?B`g`gc0d`__pg65 1LU(Cʑyl )S$`c+Q$0 q $ p < 1 죞P !]נS k$ğ p ʰ ` ʐ@R@ =C'@ @hlƾ P6/P p@pY \ @0`Z ,P ɬfF`k)0ɓL @ ɳ N2IOpp P P -ijPhp CU Qp 2T8JX*O5Pi +1НTdjT  i`"0πiTπ`HLG IyL81 O==p'HкH *P # Ӓ -np/X  p29J0 A !# r@ @`  P y  0 n!)  r w]יp"q Sڌ_c @ ̀ج p " [ YH"p  `-H0@ vPI >\E'C Pl P `  1P \p@M h p5-0ZQ頩&`4` i2uZ u &O@ t %s2NW=u mj mz b7 K= pp P N P = Z)< 0MfR @]8P  0=p4jPi `>' 0 @U=P1[;pn_| 96 j `9`6Po>n^6k2`nnpr~G``` Ұp N ~NĠ @ mpp%0 e`" %`e y72Wcl`  {l } `O%@j"P Н@8H@0 *0 %/}p0cpck`k]1_ b@saZc.: y+or I`ļhp`Rh ;!T5_i R@P Rga0@ Nn P Y @ /  ) q C@ GP / MDP uZuJ pu1pl $ N t@ b0=`р|]P [ ` \ jV6 հ\e 0 @qp*,Y#ƼyEEK"**TT qt{ቤ*idk8ހ)'AQBz0H4j$X:p&$[.䲜[;~I7@^ 8R qw &\cۀP*lh/@bgoL\h$8%l@رa~{ _U*kpIP!ޡC*a)d,ڱ7pN}BF%P! 'ͻTnβl8@y#?PP. J%@lŔC6 I8d+E遉>  *p=*C $` <b^8yy!X45a1YfzBD 6a`uyBQlya0Ƞ 6+3P# B;7!7 NC4QJAÆ5d<0c SPB,1e7F pl 1E_oqeL| vGmX* EYO|P@!OV"xh0:`  PJPФpH &[PYn&|!sjT FC#ǭ&Z|PiXF! qW`X5 fD!AD`hLaqS.e\p[~.łpyŋLlE\lNXƗKtiTIBPR!N2N$YT|_h9v؈#Sl{X ma7YaJpE?bneEmƉ @%BF$p' w'8*t4af D)%hj Ztug@Dax-d"ebU@~[jG b^0pNl8460ey!"b!@ 4 -pa HgҘ041Pc4dtBt NT lD_ D*2 _q 5$Z< f#p \p[HC/2 1HR&@. 3 r"@ у p-pE,R48ı7%xa/a |A_X.nq^\Xp` )b rK| R6sQlAJ~`c: ^bfD= meH/ ģcY @i@±q$b)ҀUs HfmNr܀/(!"-L"DMC 9g8s( 6t j LAh:/(tl 8*0o^ PK\؄ P p`B%0icv b0@h!C%s#\Vkb :-d,s l<0]B/؆2 }DCx<(x& xGԡjja(QCƃy"҅ *3P#-b\6cF BN99*Wx3"p K8\qp-Hlʨ5APHv! \PN 0 j $1 ZNtc0_!X.(уHb99v# -u[dbЄP?`GC+g=3a /U(2P  ^`t+ P@q!P9pY  6/4Ї  #9@ \o:TF/sW< *``t)EpA[-z @ %"YNbh8Dty4bHC|a סЇ@x4T`} 1^PH@K#K`W-D! rfPG 8GH${)@B#(FAΦH0A{khP ' !k]i H( /^jxHpNx8>7DimL8ip aMmBHN%`p KYЅ > p@phA 5sP7`av@!P=;B@q&lS4x(GGbG8$8skiJN;;gXH #PC=h7iBAPS|`hȇQЀrϓ' 000=b{!K8xxQnȀ€CPD =< 'X@ML+c>dSWPiTAeNte2n@ ث3`sC1@(1`m8hhпPLS3+$H BP:xj` j JHL4R8'K@|0qJjkOLd}}i.^(S>Peȇwx 8@P>^ ROWa8H!w$8wpt OQ mPH)m0/ɑ ͐GAz@PpCHrfhLZN^=bMȅexLPir/&Y| †0G32lÓ>KP,P9Klxj8qwЅf@`p`|Э9< @S  㪀5XkSh8\;D}˅BBnN_؆#\A}O(}"BQxhvx.AеArSpc`=MeHP p *(Z= @Ayh/8    8jp7(.lsAM Ѕw LͩRxAKxYw%$8NK P/x>XN^H5Ux+pTtDsRQdA؂hyx T$P3CCW@Sc{U 8  t(W5Nh[txW8xa0 $@UZZ> DkHDkUQxJwB еDZP(Tk(W^`$wxPa$y`@LHs;` ^أ=.h;u/TxU>RJÇAH46Ѕr H/MXU (8`jx:萉5 P{`@ꃈ̇I=Y Y H-dhxv@Y=^50@pG 1H@"A(Aa ,[ V )ikKR>AI B}PZru`}M`~'AX 0HY3^MpqDrC^Kp`}HpXKV4}G:ݘA@TNZ41[;~AQq=BKdee ,|M8M]60g: 8(m`s0I ȀMa$(iІvXT*l_(7PDhl#D;dkH@;cD v7v9c6]|3D3\ddk7h4hD;0#JEe$h4b^Uȇ|Xw4Ll(8w[Tjpͳ`ApDXcל*۴hxѾ ixkpDNbTҥm+7gg f x:3mK4\=vidMr4`VOzR^k(Dbdl˾|Xtht` Sx]K㨝@lQ5ZRFH H`L4^qqO`#pxkx` hi@xPli :p "[[A 1A   xNk@PhHWXjmZu($iX.M>dоx<+: 4p?@@( q0`X%B(z7.suM(ƛ'0F6VQ;dc6LYZʴ/~F~*hb4jJH bh7.pqEIE&T{A8+6att3_lB^PsN"P'0!˰-N˄k! h!,QݰGJ1=Tα'q Dma=0W`0 \!(L?vz`v`9ZkN8G%lA5 l1;1#DCMCAtp*@A1\6.$;P0<A  A <ׅ1@8T8@ A8pA,|Aui dޱ-!@ Ё@6a@34+5DH e @&=C54@'[6 P dA"@8U̒DD܁݁ <^(NPA!#<^'b;vAYmJpֵ03(+b܀u9[-᐀L!`+ʀ 9$ 0`r 34B$(TH`3ɀ8@+6  4 pB99D$*d: 8;BU&û#@XBỲcXV_!/@4/A3l Z \ ɨړA DA=Z 9BPAќCAD_ ^@q$AytC#<^7^\ |,98C0YȠdACJ(c$f:T۰HDo9< V|=0X^6 X;&e^+\#Pr*g'&Bt~d@@<%HHD%PB)89T*`@ T\i70<K8pTBnnn#t9@959+eb^ j2B"D$30@7ta=Cpn 4ÊCBlJ@vCĈhA͆H8*Jq $T#0@9/+/D@ 8-%H+B108Ӣ\C> "$$E VA). 8KB<-4HC< /+).|6@@"hD 1<h a5Aq @`qTjE618&--\"8dCB8$8BXtB-0B-LC-C'sFŦSQTsU9)؃!C p?q1A=| !"%@BdO'P'@P+ v=(ލ#@7;V9Vs/RA7A"$B( Vogb*ፐ.V:d͎:jEc hZCEh8ePJ LD/L@EBKA311T)E,ŀbPP}=AJ ,@_T 0UYeR ;!,;C./@>O(OOB?.}9bA"1C%U l=9AoA  =C"$6$^֔4lވC:DPPpA@58‰M@@B4"(B||d%C @D1ȂTJg,\3[A_\LA$xXה½ILA =W DzXDsmN %=Xg6oSD'LijGD> 686TAS ^0؃D584CHBd I036=@PXED/\/DB"B 8 쀰@ &%DP@@A#\蹀G@ &50?1/8K*tV3B+@/Hq-4|4焉|I,$ @11hYaHT tw$G#$"yg+>.`2;ù/,Lheqhr 51i40` ^qEagz04(K("J-4lʹzz`"`clK2F]heK$9fN@Q h"0 uBKB&JYI.D@. $9Vu ,!&9)([K%z 0a/0rӮ+ #kgm@ (Pb3иb3ؘ3IA4!#x5:k!Rȅj ',SL(q4ķNgB F>` @ |EA"QZlҶ-{)蠄"% |^ ]l7nq 'ps^@7 pz뒥Fd&x€: Vjh yw1Ac,D |)H Pe>FNqUd%ejH؂ @lX2EZŗ}y՚..vP!7. C>ɏ~Y-a7J5OXC4!F: 8,`;a4x1la[` P^ԂRnk[( ^xJyl`@QCB!.P آ-!@8[N zL8P[c-a G[ ,1Nw0^GȄ8H|00xbE&N0@QTcbȇ60'آP^'iLHPL`lx$g8R .K_Sļ9@ @./ @pB`6Q7#F& _F5nn,A5m@*  }F8-C08>#x:YC_ *QԠ-Ё6"wD BHo@,1Y884,Qix Tb4x a`“`0u8殡$ /C @. 6!Lm"W 1!4p@B i`@7$:Љ!4XB6e1YiLcH8a4r:k ;2j7X0- P\!%Gj1 w#R ǰ(!0O"hG1b :L</0(Lp( t0@摏m=w@  . {17 d]C0AQ <p `=0 dH!@#x8*BT}QB @@Z'T!@p)SX>e08 .&a \BR̯ 5.ۉ@2, v|A6ah, o6ZS!}(Ġ> | zuE4KP((:D½jzV( (J1h2ԢLֆ4 H0HF`ySb:(!#hG!k$(Aч>#=;qA'(T k8xm 8y0_$6Dh F;U)65ZD8fC15kp]pS>At B,kK@׮ ;0 7?fo\"  5vA4!KCM/ȆzAR4dCN07: Hǎ{$Fd4` ȑ C"=`;8ޗKw8v8JzwXPq _ $La4!^͐&h2a*(bRf.!OxAtaA>a $" 6#& ` ,8@nNA!Fj@ @,j j. 櫾> gOf`ada",$FOx @:0@ J`ΎzR 61>L" @aFA.<#(fU&ill!4`+ +.'R  3\~ap \ j\PAJxr!APaL pN  $z Z B $@avQ4a #$@x^@r!@z!.bxߒa2`2 a ,p@D!ƇhRAqp`Faz !@Ѐ V ,@!a !la 4@ha Ǡ P&@!E@$6x`PQ@dA, pMi("aPa[ PVAx2"YnPAz `^ v6~Өp! $A@T!~z8N!xa!XatmԳ!/@JJH^!o| B)Γ` R>' kdAOzg2a^ > !aHS2gD ơ6`6Le@&!@@z AZ=` q$0A ! Am@NAj\`; 1`=O!$Ā0 B5uxA?n B!A AH6QS+3f B!aFA2|u|5r$C`*J@a B@B@Az4@:`H|a[pQaQ`J) Ġ"H (L+b-Lށn0,rp,@=@A``t]0zAAp;Mdj j`R +׍2udQP.b"'3 (ErV' t\!r.BWQS3Ġ=L2+ٯHډx/&QPa\`|nH^> -*BpnD p:@0!f\`IIԤԦ@4aYb7mb/wT. nf,x(@@!"!u,> d!`[` *7u7o6i pV=wJ ADA+y!!Z3@ Ưxa<& P-[iYn!!l Hv.& v`8):`ORa^.tA*aBe-?pHH(`zb3ʁ"!0P eqQ| 2A ^ - (vlN4MÁz@@w}%9-* ak7L@uA.! ʵ\26kVa/J`raN Dg/@j@]ࡂ @F!AK"P ND5@:N8aN7\.6@\p kՓ8 .`HrW ڠ@L !~!,6B!&@#z`H@:) 8c?:I.AAYA"A3@ J3 | 5rl|Z Aw!7!2bAt,3!AFlp`Dp`!K@>յ ./o?v !d@ :zsaP`X-ߒP :Eݷwx?rW:pUWS(lZOy}2D?~cYBimA϶p{@@AH=bJN njp`YС؜!Du ԓǹK`,;'‰wa^`g&i8(^XAt-ios}M{*r[@,l~y`[yRSҁ-#61&  ^ f55pʫ1@z` <" fLPgw R媡{D "\A|}@&}}`fo!!Ġq!la( V%9ia{`E=ik4W{RAo:0 <@` X\oAM q)8N7."-q Vo^7](]oPml! l!a!l&mDŠP!>2A^ A?ӞZY= 8BaZ9 az>`)yY,{LA FܦX3@?Zq 3b7 NAL! !iM^ *^ BA"5 ދ)Bok7!B6O듺[=aa!AuB?BǒW}i`[rV,g!@N]auLY@a(` >N%` X{ F!A9a> Xxr;q(\Ȱ)_4Vjȱ#CgFUÇNS6T]O؄#C=׃PTv$Զc=X|-P<66Pa/!3HPv]+-EB\e8 ʘ(ժIgmo|z&[m8FXek ^f e#6I9ypD"IHɗ/=U7-`D't` /ZYG9.N^Uk/,TRp- D 7,pRc,r /lE3 3^.A=H==N)/DbU)lMBU,@ ;(u'YH(h<22l3B 2ʌlajBz0|r 8e% L0A*Fp8 J9eENҎOPp 31p9F@0*'=iѥ!BLO <4bBW)D\<$IFc*PA0Wy"lap<6,"BQ4,@6[=H"#C@FTc !ю4lG 8x0@(s|0SHD ab \ A5U$!( 0NSLFڣhҠE:BJժZXͪVծz` XJֲaM U֫.lj\Z YBz׶ WՊ׽հtHZjոΕZ%عղͬf7֎kaUUuy,h1r-V=;ZzUl!KY^o+Zmgm[Zφkt% RwUiUenWmܼNѭx[׻/XrnqݛZ붖|LJWum]=Bve[Xea{T ˁ%Z^(NWb;free42-nologo-1.4.77/skins/Voyager42.layout000644 000765 000024 00000003767 12110237246 021015 0ustar00thomasstaff000000 000000 # Voyager-style HP-42S skin for Free42 (PocketPC) # Converted from Christoph's Real HP42S for 240x320 (PC vertical) # By Erik Ehrling (Sweden) Skin: 0,0,268,240 Display: 3,13 2 3 758a80 000000 Annunciator: 1 4,3,18,9 0,241 Annunciator: 2 23,3,20,8 19,242 Annunciator: 3 44,2,23,9 40,241 Annunciator: 4 68,2,15,9 64,241 Annunciator: 5 84,2,20,9 80,241 Annunciator: 6 105,2,5,9 101,241 Annunciator: 7 111,2,18,9 106,241 Key: 1 210,209,25,29 210,209,25,29 209,208 Key: 2 102,77,17,27 102,77,17,27 101,76 Key: 3 27,77,17,27 27,77,17,27 26,76 Key: 4 77,77,17,27 77,77,17,27 76,76 Key: 5 52,77,17,27 52,77,17,27 51,76 Key: 6 32,164,21,29 32,164,21,29 31,163 Key: 7 61,208,21,29 61,208,21,29 60,207 Key: 8 90,208,21,29 90,208,21,29 89,207 Key: 9 27,121,17,27 27,121,17,27 26,120 Key: 10 52,121,17,27 52,121,17,27 51,120 Key: 11 77,121,17,27 77,121,17,27 76,120 Key: 12 102,121,17,27 102,121,17,27 101,120 Key: 13 118,159,25,76 118,159,25,76 117,158 Key: 14 61,164,21,29 61,164,21,29 60,163 Key: 15 126,77,17,27 126,77,17,27 125,76 Key: 16 126,121,17,27 126,121,17,27 125,120 Key: 17 90,164,21,29 90,164,21,29 89,163 Key: 18 2,77,17,27 2,77,17,27 1,76 Key: 19 148,77,25,29 148,77,25,29 147,76 Key: 20 179,77,25,29 179,77,25,29 178,76 Key: 21 210,77,25,29 210,77,25,29 209,76 Key: 22 241,77,25,29 241,77,25,29 240,76 Key: 23 2,121,17,27 2,121,17,27 1,120 Key: 24 148,121,25,29 148,121,25,29 147,120 Key: 25 179,121,25,29 179,121,25,29 178,120 Key: 26 210,121,25,29 210,121,25,29 209,120 Key: 27 241,121,25,29 241,121,25,29 240,120 Key: 28 27,208,21,29 27,208,21,29 26,207 Key: 29 148,165,25,29 148,165,25,29 147,164 Key: 30 179,165,25,29 179,165,25,29 178,164 Key: 31 210,165,25,29 210,165,25,29 209,164 Key: 32 241,165,25,29 241,165,25,29 240,164 Key: 33 3,208,21,29 3,208,21,29 2,207 Key: 34 148,209,25,29 148,209,25,29 147,208 Key: 35 179,209,25,29 179,209,25,29 178,208 Key: 36 3,164,21,29 3,164,21,29 2,163 Key: 37 241,209,25,29 241,209,25,29 240,208 free42-nologo-1.4.77/skins/Widgi42.gif000644 000765 000024 00000023531 12110237246 017663 0ustar00thomasstaff000000 000000 GIF87a@ pppPPP0(P,@I8ͻ`(dihlp,tmx|N",ȤrlΨtJZ2(zxL.zn ~u,w)+vΰѤ+[Ԫa\_Ĭ]P j1/"H-ӊOǀ!#dž'-\2Fp6 oI%Clɳė4L3绣`p\ʔP-ͦXn{SiU5SJwUٳȸ -9Hnٚ\jݻ2/߿ L+N(J̵#KU˘3k̹ς{ӨSNJؾɰcB,|eͻ7%NOе>N}سkνīËOeW૫/}׭$_}®߀kA6X@ c!*v(b/Zcl8⊸"0A`8W{fߍ9 +*BL6PF)TViXf\vWBCd=̡lp3Y㙮Yޞ|tycw'~&袙 *I'fiv nj꩚v#J)*)YhfJ&lisN Vkm^#4/*Y*覛.+;䰺d-Ǟ\gҢ+G3mg+·/|*ZL0+$i2,3-Jl<}0l0Dw3/+tLw4@+JMW&VwZ:doAO=vl[rv[>q'M( -~괁8ٺlxá6ns83_P8;,y\(Nx ղ+Nq./ć:ҏ_|f]ry )σ0g+W̽k~ѷc/?o>QLDM `W?$IP{$`/8 fU S+0tk^x= 0?4 P?=qHAp 8%M,(YX̢b+{;2%3c'‰?NCittGG䑋b a$1dܨHDFdyّFr\ԡT&0Nz (E$DrrL%))Y*Uҕ^IQr.=yVrd2C1IKeH&+LXNl5Mt#JM[ns@:iJl2d,xҙL=yr2o OhD+Ot*!uZTd(:1~:94 zΊVSgHk Z5 H9Ӆz4OA)Suq#vKJVuT ,N)WeaXӛ .D l'G [5m71ѝ"e 7mu^JMÂ֟n9MfӺ,t;ZLvZ+UⒷ.vE^uw֎.~kN׿u1m)~o &{Gr8!Owk^ _8MnE&Xd`JSTCy j!`ls h=i3 V*gߧ:!^\bT82 3xsRMÚ X) ńpUb^f39=tfDWv0YJ?z](= . *dVͫK^+OOZ ԅXOدFvj.:ط5kMNi ljC~#jh+ێnEcڟ>5;nөl{ϳNo=My˺Жm?upnzec h>8m;\W^pm7;r:6xK.hRǛ=q -̉r&yjy^Sz :*/8֝>"KEO8=sO}k4cy߼^Ӄ/M-ԃjܣ~Eѕ,Cע6~>f^~s|8/+`C[g)yo]@__Y(M (T J\${>eL&WbJUh%T"(%$XPWyZ&0%u4)(6$5M<K}pFbFhTFGxF2KFaT`RCPAȂW(YzbochMhOȄiHEFVq`hy.hGGGwh̢{Sx`.HH$IJ TIx-s6 t|S6{V ΄פĊ䊸ĊVaiq9ؕi9f6YTNIgy i c8{y)w(R} `C) |)(}隧 Ix P9P:~yH`霻)Zo i9֙ 92)(yP9y+Hڙ`kXGcpbF_نJEIRѓ Jڠd;ةǟex"X)@^a٠*)ʠZ.ڟ:Z:({y\$zJT:ILz0V:EHڤDZX:Jo9Rh9aGzN:ԥTurZxy wjP: BE*'ejj!_ʡUȟzڊj_ztX JyjU飀z"oZrڤ*zI*iz Ȧ.0Z~I3Jgj3´Oڢʭ՚:ʫE;z ʬگZ)Zuͺm ˭aIɜⅯݺZPj*Dӊ8!lmaڣ7j2۪zj*2ۯ,ۨj>}ڲ! :n 9S={@۱A;Z Y#יj )Y[zEʸsDX+aÛvw{К`ˢqgK6J۵^c[yi@ɹIY:kK; Hi`ک;G'ȃk;HKIѼ˼냾kk}K“nml"kBjBR(]vڋ6Z[;[{;b/| ~؇ o~.-Hh pH 7\*v!yMiz;{靴k0L˩uQõ Ĭi*j T9.-x(ɘP\и3W̓` I\,0Jv ]ysYq)+9oL9y w"{Lm|kǼ0XR|s,a^ HagIPY}LOŘ kƅ)ʍ:̱9(luL`C7yɬܓm_˵,Ǡn)dL˅l*@B{̷|̄v9\̿iˏs]l(`ʛ썍W{ˍI}awAXS|FyU,aбЈ˞IН hѳ̝˩,=ؼ',?ϱ;ܞ\/}+]f[P {; $M{*$}7-^=]==r΃E]@ z)= ήZ ۴zjcfֱl rM+g Un]^=:Y; ؓK|Mhۮ$Q8Z *oawy|Zݦʱz!?%mP-ȫ-glE˶K "A8kmٲ~[չ-_k_C==ŭ=؀ ׾-s ]mԡׄ-4;e=؏ Ʃ؟ i(ںЦ 󍵴6o K̽ ڲ#ީM+]- Њ}"lO .~ mE >= >ƽq}MN=FK-cn_~涹!n5-wyN +޻GN dF ޼ ؋br+,K &n>\8,볞e.<нt>帴GͭqG{VAЈq훠nIúΚrn._Kn>n>>n4厚6kNXK|TލV</Lȡ\P o y\ی7YV+ll\L-my#6ڼlRBqIiF9KO~>2/Yd ΌEZ$P*?0_|fdZ?{2aohOL=ΌhBO_z̢~s/FȌ/ ~clΏ/xjN}?__//( o?ZC o0G s_4-OYX ks'W?C  1L(lȢ ҹx4[ꑰ~ge 5H+.;1&BĐGE:CȐŸ8ϑ<>)AQ RP.X2H#JZ [I^ 7LC*/|&B[tE-tikoQwRƩ ' aH&ˡ!J!G+"+B0 &2$/3TM5۔-,7㬬=9A./!oBO@tP~.\SBuQH# ЪEƬSN67uSOׄ3OG%U.20\!XcuVZk$[sյV)w^W\56V.)]R01UXhKIhvjV jvWiSt=/ V@cOw#.) E'2r$%Iox _|7Qw<`).8)fP Bٺ/0y eKdM'.yfy.Vg;sEWQuaKgm:@Bz뛱NIι둭n竭6V=n{n5em-b:e.kޚ絥^zl /ncBm/;#/3',ptŮYfg'6ME#AnWr]G3Svu?سG9fAn9 w!򬓖^N6^z޽5@篇+b(1m(cXӅl;AyAy:1 rOPUzq!sC2˄Qv><"\& XWܤ3t8rK/c bx'Z%XF xDD&joTy~ -q8|^0(J.>,&N"ő4ޠ奤L"12HUf1 %+E=ц}^ڀt%*hG\gFY)v~%-VE>ٌ1[HWjU$k͢𚰜;K$neIT`0IH0N2MLpRrߨ8VNS@ PsF69Ȇs,e6sI1bTW3=zd~g1CG5fթFUP]i:gJFTx$՛INN5ŁF6.sjDbUUBS8Z5NexȦv"SCpz2.9;~"2ګh{ny+)Z hiKFdc$^f9"˱> $P틲ZEk4S T}9y[+nkOiIRens;㖴ϥnu{;$$4U\x[URg[Fj\ |[,ꪾ~KdiwDNY+S`Z9XIN0zLȆi)w#QH|V,5\wUߕklb=b(* ;QޑH\K`Bv %&RU.zfq*Ez7k f-n^TE{Sk3pM{;6зä Lͱ\0tVM8ΌEg݁yWfڦ/9=\4:ssTc{Yw3,`"xjPb^HϜL?r k}9|q]}LcCSkj{޵[ 竿 3a/8MVxG!cOc֓8x 3yB8u}љ8}m&'s[l^b^- bk /s \_yrGNS8Mm\Ak;: ΰigܯ]bD3=={!y6$*VYs-[Q/9ovh՟M\4wKDެZzϻW[l["ox_@m|ɾ~J~WxďXW/jHngŗe r?)>J>A#:C<J=D@ܽ+3y^?+k:T;[tMNľE49\|bWLӫa8ܸ2:ԿPkA9nӞ:*vFu4AǺ6oiǻkÀ;4{Ts5i3G2KC<'r܃ůc AAwt 5bşH[HYd6skk G"IB H95ZI3\,IsǟџYC4\i4~ItW+ȁ66[dKHTtHƚHfQɚ<}dGtâr,Ǯ=fB6lpK##˥TAKƹ@dFL{ɷƃ+Ĝj@\L8ܿt??OL t͜δL\* ![Tg=Ɯ<" K(<P@f(NLdnl>„J@N8 l|/T0;$ct#3<LN&0OBOIL LN=A$M`|;OaO{LDĻJqN|N %nP ]Mb ?xOPMcPU'FìOZ?4 Lф Q-cQjT$NnjZQN{N!-uP#PcLKxl~W` V`~\ V>0 `=aX `,a ~ጵ #`k`> `vaa"Cքqia~^a'b(; 1-NFH0_+b1Fc,v$Vb5r>vcc1c2>c= cc -7d6c FdO;free42-nologo-1.4.77/skins/Widgi42.layout000644 000765 000024 00000003672 12110237246 020437 0ustar00thomasstaff000000 000000 # Color skin for Free42 (Windows and Unix) # By KR Skin: 0,0,320,406 Display: 29,54 2 2 ffffff 000000 Key: 1 23,114,49,38 30,116,40,35 30,115 Key: 2 72,114,44,38 74,116,40,35 74,115 Key: 3 116,114,44,38 118,116,40,35 118,115 Key: 4 160,114,44,38 162,116,40,35 162,115 Key: 5 204,114,44,38 206,116,40,35 206,115 Key: 6 248,114,44,38 250,116,40,35 250,115 Key: 7 23,152,49,38 30,154,40,35 30,153 Key: 8 72,152,44,38 74,154,40,35 74,153 Key: 9 116,152,44,38 118,154,40,35 118,153 Key: 10 160,152,44,38 162,154,40,35 162,153 Key: 11 204,152,44,38 206,154,40,35 206,153 Key: 12 248,152,44,38 250,154,40,35 250,153 Key: 13 23,190,93,38 30,192,84,35 30,191 Key: 14 116,190,44,38 118,192,40,35 118,191 Key: 15 160,190,44,38 162,192,40,35 162,191 Key: 16 204,190,44,38 206,192,40,35 206,191 Key: 17 248,190,44,38 250,192,40,35 250,191 Key: 18 23,228,54,38 30,230,40,35 30,229 Key: 19 77,228,55,38 85,230,40,35 85,229 Key: 20 132,228,55,38 140,230,40,35 140,229 Key: 21 187,228,55,38 195,230,40,35 195,229 Key: 22 242,228,50,38 250,230,40,35 250,229 Key: 23 23,266,54,38 30,268,40,35 30,267 Key: 24 77,266,55,38 85,268,40,35 85,267 Key: 25 132,266,55,38 140,268,40,35 140,267 Key: 26 187,266,55,38 195,268,40,35 195,267 Key: 27 242,266,50,38 250,268,40,35 250,267 Key: 28 23,304,54,38 30,306,40,35 30,305 Key: 29 77,304,55,38 85,306,40,35 85,305 Key: 30 132,304,55,38 140,306,40,35 140,305 Key: 31 187,304,55,38 195,306,40,35 195,305 Key: 32 242,304,50,38 250,306,40,35 250,305 Key: 33 23,342,54,38 30,344,40,35 30,343 Key: 34 77,342,55,38 85,344,40,35 85,343 Key: 35 132,342,55,38 140,344,40,35 140,343 Key: 36 187,342,55,38 195,344,40,35 195,343 Key: 37 242,342,50,38 250,344,40,35 250,343 Annunciator: 1 28,39,19,11 28,407 Annunciator: 2 50,39,22,11 50,407 Annunciator: 3 74,39,25,11 74,407 Annunciator: 4 101,39,17,11 101,407 Annunciator: 5 120,2,22,11 120,407 Annunciator: 6 143,2,6,11 143,407 Annunciator: 7 149,2,19,11 149,407 free42-nologo-1.4.77/gtk/audio_alsa.cc000644 000765 000024 00000040440 12110237246 020027 0ustar00thomasstaff000000 000000 /////////////////////////////////////////////////////////////////////////////// // Free42 -- an HP-42S calculator simulator // Copyright (C) 2004-2013 Thomas Okken // // 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, see http://www.gnu.org/licenses/. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include // We want to be able to run even if libasound is not present, so we have to // link it manually using dlopen() and dlsym(). These are the functions we are // going to need; the pointer names are the same as the actual function names // in libasound, with _dl_ prefixed. static int libasound_state = 0; // 0=closed, 1=open, 2=missing typedef int (*_ptr_snd_pcm_close)(snd_pcm_t *pcm); typedef int (*_ptr_snd_pcm_format_big_endian)(snd_pcm_format_t format); typedef int (*_ptr_snd_pcm_format_physical_width)(snd_pcm_format_t format); typedef int (*_ptr_snd_pcm_format_unsigned)(snd_pcm_format_t format); typedef int (*_ptr_snd_pcm_format_width)(snd_pcm_format_t format); typedef int (*_ptr_snd_pcm_hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); typedef int (*_ptr_snd_pcm_hw_params_malloc)(snd_pcm_hw_params_t **ptr); typedef void (*_ptr_snd_pcm_hw_params_free)(snd_pcm_hw_params_t *obj); typedef int (*_ptr_snd_pcm_hw_params_any)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); typedef int (*_ptr_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); typedef int (*_ptr_snd_pcm_hw_params_set_access)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); typedef int (*_ptr_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); typedef int (*_ptr_snd_pcm_hw_params_set_channels)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); typedef int (*_ptr_snd_pcm_hw_params_set_format)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); typedef int (*_ptr_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (*_ptr_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); typedef int (*_ptr_snd_pcm_prepare)(snd_pcm_t *pcm); typedef int (*_ptr_snd_pcm_resume)(snd_pcm_t *pcm); typedef int (*_ptr_snd_pcm_sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); typedef int (*_ptr_snd_pcm_sw_params_malloc)(snd_pcm_sw_params_t **ptr); typedef void (*_ptr_snd_pcm_sw_params_free)(snd_pcm_sw_params_t *obj); typedef int (*_ptr_snd_pcm_sw_params_current)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); typedef int (*_ptr_snd_pcm_sw_params_get_boundary)(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); typedef int (*_ptr_snd_pcm_sw_params_set_silence_size)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); typedef int (*_ptr_snd_pcm_sw_params_set_silence_threshold)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); typedef snd_pcm_sframes_t (*_ptr_snd_pcm_writei)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); typedef const char *(*_ptr_snd_strerror)(int errnum); _ptr_snd_pcm_close _dl_snd_pcm_close; _ptr_snd_pcm_format_big_endian _dl_snd_pcm_format_big_endian; _ptr_snd_pcm_format_physical_width _dl_snd_pcm_format_physical_width; _ptr_snd_pcm_format_unsigned _dl_snd_pcm_format_unsigned; _ptr_snd_pcm_format_width _dl_snd_pcm_format_width; _ptr_snd_pcm_hw_params _dl_snd_pcm_hw_params; _ptr_snd_pcm_hw_params_malloc _dl_snd_pcm_hw_params_malloc; _ptr_snd_pcm_hw_params_free _dl_snd_pcm_hw_params_free; _ptr_snd_pcm_hw_params_any _dl_snd_pcm_hw_params_any; _ptr_snd_pcm_hw_params_get_buffer_size _dl_snd_pcm_hw_params_get_buffer_size; _ptr_snd_pcm_hw_params_set_access _dl_snd_pcm_hw_params_set_access; _ptr_snd_pcm_hw_params_set_buffer_size_near _dl_snd_pcm_hw_params_set_buffer_size_near; _ptr_snd_pcm_hw_params_set_channels _dl_snd_pcm_hw_params_set_channels; _ptr_snd_pcm_hw_params_set_format _dl_snd_pcm_hw_params_set_format; _ptr_snd_pcm_hw_params_set_rate_near _dl_snd_pcm_hw_params_set_rate_near; _ptr_snd_pcm_open _dl_snd_pcm_open; _ptr_snd_pcm_prepare _dl_snd_pcm_prepare; _ptr_snd_pcm_resume _dl_snd_pcm_resume; _ptr_snd_pcm_sw_params _dl_snd_pcm_sw_params; _ptr_snd_pcm_sw_params_malloc _dl_snd_pcm_sw_params_malloc; _ptr_snd_pcm_sw_params_free _dl_snd_pcm_sw_params_free; _ptr_snd_pcm_sw_params_current _dl_snd_pcm_sw_params_current; _ptr_snd_pcm_sw_params_get_boundary _dl_snd_pcm_sw_params_get_boundary; _ptr_snd_pcm_sw_params_set_silence_size _dl_snd_pcm_sw_params_set_silence_size; _ptr_snd_pcm_sw_params_set_silence_threshold _dl_snd_pcm_sw_params_set_silence_threshold; _ptr_snd_pcm_writei _dl_snd_pcm_writei; _ptr_snd_strerror _dl_snd_strerror; #define TDIFF(begin,end) (((double)(end.tv_sec - begin.tv_sec)*1000.0) + ((end.tv_usec - begin.tv_usec)/1000.0)) static unsigned int audio_sample_rate = 22050; static snd_pcm_format_t audio_format = SND_PCM_FORMAT_S16; static const char *audio_device = "plughw:0,0"; static int audio_channels = 1; static snd_pcm_t *playback_handle = NULL; static bool audio_initialized = false; static snd_pcm_uframes_t buffer_size; static struct timeval last_use; static pthread_mutex_t closer_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t closer_cond = PTHREAD_COND_INITIALIZER; static pthread_t closer_thread; static int audio_set_hw_params(snd_pcm_hw_params_t *hw_params) { int err; snd_pcm_uframes_t size; if ((err = _dl_snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", _dl_snd_strerror (err)); return err; } if ((err = _dl_snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf (stderr, "cannot set access type (%s)\n", _dl_snd_strerror (err)); return err; } if ((err = _dl_snd_pcm_hw_params_set_format (playback_handle, hw_params, audio_format)) < 0) { fprintf (stderr, "cannot set sample format (%s)\n", _dl_snd_strerror (err)); return err; } if ((err = _dl_snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &audio_sample_rate, 0)) < 0) { fprintf (stderr, "cannot set sample rate (%s)\n", _dl_snd_strerror (err)); return err; } if ((err = _dl_snd_pcm_hw_params_set_channels (playback_handle, hw_params, audio_channels)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", _dl_snd_strerror (err)); return err; } // set audio buffer size to about 125ms buffer_size = (audio_sample_rate + 7) / 8; if((err = _dl_snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &buffer_size)) < 0) { printf("Unable to set buffer size %li for playback: %s\n", buffer_size, _dl_snd_strerror(err)); return err; } if ((err = _dl_snd_pcm_hw_params (playback_handle, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)\n", _dl_snd_strerror (err)); return err; } if((err = _dl_snd_pcm_hw_params_get_buffer_size(hw_params, &size)) < 0) { printf("Unable to get buffer size for playback: %s\n", _dl_snd_strerror(err)); return err; } buffer_size = size; return 0; } static int audio_set_sw_params(snd_pcm_sw_params_t *sw_params) { int err; snd_pcm_uframes_t boundary; /* get the current swparams */ if((err = _dl_snd_pcm_sw_params_current(playback_handle, sw_params)) < 0) { fprintf(stderr, "Unable to determine current swparams for playback: %s\n", _dl_snd_strerror(err)); return err; } if((err = _dl_snd_pcm_sw_params_set_silence_threshold(playback_handle, sw_params, 0)) < 0) { fprintf(stderr, "Unable to set silence threshold for playback: %s\n", _dl_snd_strerror(err)); return err; } if((err = _dl_snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0) { fprintf(stderr, "unable to get ring pointer boundary for playback: %s\n", _dl_snd_strerror(err)); return err; } if((err = _dl_snd_pcm_sw_params_set_silence_size(playback_handle, sw_params, boundary)) < 0) { fprintf(stderr, "Unable to set silence size playback: %s\n", _dl_snd_strerror(err)); return err; } if ((err = _dl_snd_pcm_sw_params (playback_handle, sw_params)) < 0) { fprintf (stderr, "cannot set sw parameters (%s)\n", _dl_snd_strerror (err)); return err; } return 0; } static void *closer(void *) { pthread_mutex_lock(&closer_mutex); while (true) { struct timespec when; when.tv_sec = last_use.tv_sec + 5; when.tv_nsec = last_use.tv_usec * 1000L; struct timeval now; gettimeofday(&now, NULL); if (when.tv_sec < now.tv_sec || when.tv_sec == now.tv_sec && when.tv_nsec < now.tv_usec * 1000L) break; pthread_cond_timedwait(&closer_cond, &closer_mutex, &when); } _dl_snd_pcm_close(playback_handle); audio_initialized = false; pthread_mutex_unlock(&closer_mutex); return NULL; } static bool open_libasound() { void *lib = dlopen(ALSALIB, RTLD_NOW); if (lib == NULL) { fprintf(stderr, "Could not open " ALSALIB "\nusing gdk_beep() for BEEP and TONE.\n"); return false; } _dl_snd_pcm_close = (_ptr_snd_pcm_close) dlsym(lib, "snd_pcm_close"); _dl_snd_pcm_format_big_endian = (_ptr_snd_pcm_format_big_endian) dlsym(lib, "snd_pcm_format_big_endian"); _dl_snd_pcm_format_physical_width = (_ptr_snd_pcm_format_physical_width) dlsym(lib, "snd_pcm_format_physical_width"); _dl_snd_pcm_format_unsigned = (_ptr_snd_pcm_format_unsigned) dlsym(lib, "snd_pcm_format_unsigned"); _dl_snd_pcm_format_width = (_ptr_snd_pcm_format_width) dlsym(lib, "snd_pcm_format_width"); _dl_snd_pcm_hw_params = (_ptr_snd_pcm_hw_params) dlsym(lib, "snd_pcm_hw_params"); _dl_snd_pcm_hw_params_malloc = (_ptr_snd_pcm_hw_params_malloc) dlsym(lib, "snd_pcm_hw_params_malloc"); _dl_snd_pcm_hw_params_free = (_ptr_snd_pcm_hw_params_free) dlsym(lib, "snd_pcm_hw_params_free"); _dl_snd_pcm_hw_params_any = (_ptr_snd_pcm_hw_params_any) dlsym(lib, "snd_pcm_hw_params_any"); _dl_snd_pcm_hw_params_get_buffer_size = (_ptr_snd_pcm_hw_params_get_buffer_size) dlsym(lib, "snd_pcm_hw_params_get_buffer_size"); _dl_snd_pcm_hw_params_set_access = (_ptr_snd_pcm_hw_params_set_access) dlsym(lib, "snd_pcm_hw_params_set_access"); _dl_snd_pcm_hw_params_set_buffer_size_near = (_ptr_snd_pcm_hw_params_set_buffer_size_near) dlsym(lib, "snd_pcm_hw_params_set_buffer_size_near"); _dl_snd_pcm_hw_params_set_channels = (_ptr_snd_pcm_hw_params_set_channels) dlsym(lib, "snd_pcm_hw_params_set_channels"); _dl_snd_pcm_hw_params_set_format = (_ptr_snd_pcm_hw_params_set_format) dlsym(lib, "snd_pcm_hw_params_set_format"); _dl_snd_pcm_hw_params_set_rate_near = (_ptr_snd_pcm_hw_params_set_rate_near) dlsym(lib, "snd_pcm_hw_params_set_rate_near"); _dl_snd_pcm_open = (_ptr_snd_pcm_open) dlsym(lib, "snd_pcm_open"); _dl_snd_pcm_prepare = (_ptr_snd_pcm_prepare) dlsym(lib, "snd_pcm_prepare"); _dl_snd_pcm_resume = (_ptr_snd_pcm_resume) dlsym(lib, "snd_pcm_resume"); _dl_snd_pcm_sw_params = (_ptr_snd_pcm_sw_params) dlsym(lib, "snd_pcm_sw_params"); _dl_snd_pcm_sw_params_malloc = (_ptr_snd_pcm_sw_params_malloc) dlsym(lib, "snd_pcm_sw_params_malloc"); _dl_snd_pcm_sw_params_free = (_ptr_snd_pcm_sw_params_free) dlsym(lib, "snd_pcm_sw_params_free"); _dl_snd_pcm_sw_params_current = (_ptr_snd_pcm_sw_params_current) dlsym(lib, "snd_pcm_sw_params_current"); _dl_snd_pcm_sw_params_get_boundary = (_ptr_snd_pcm_sw_params_get_boundary) dlsym(lib, "snd_pcm_sw_params_get_boundary"); _dl_snd_pcm_sw_params_set_silence_size = (_ptr_snd_pcm_sw_params_set_silence_size) dlsym(lib, "snd_pcm_sw_params_set_silence_size"); _dl_snd_pcm_sw_params_set_silence_threshold = (_ptr_snd_pcm_sw_params_set_silence_threshold) dlsym(lib, "snd_pcm_sw_params_set_silence_threshold"); _dl_snd_pcm_writei = (_ptr_snd_pcm_writei) dlsym(lib, "snd_pcm_writei"); _dl_snd_strerror = (_ptr_snd_strerror) dlsym(lib, "snd_strerror"); if (dlerror() == NULL) return true; fprintf(stderr, "Could not load all required symbols from " ALSALIB "\nusing gdk_beep() for BEEP and TONE.\n"); dlclose(lib); return false; } static bool audio_init() { if (libasound_state == 0) libasound_state = open_libasound() ? 1 : 2; if (libasound_state == 2) return false; if (audio_initialized) return true; int err; if ((err = _dl_snd_pcm_open(&playback_handle, audio_device, SND_PCM_STREAM_PLAYBACK, 0)) == 0) { snd_pcm_hw_params_t *hw_params; _dl_snd_pcm_hw_params_malloc(&hw_params); err = audio_set_hw_params(hw_params); _dl_snd_pcm_hw_params_free(hw_params); if (err < 0) { _dl_snd_pcm_close(playback_handle); goto fail; } snd_pcm_sw_params_t *sw_params; _dl_snd_pcm_sw_params_malloc(&sw_params); err = audio_set_sw_params(sw_params); _dl_snd_pcm_sw_params_free(sw_params); if (err < 0) { _dl_snd_pcm_close(playback_handle); goto fail; } } else { fail: fprintf (stderr, "cannot open audio device %s (%s)\n", audio_device, _dl_snd_strerror(err)); return false; } audio_initialized = true; pthread_create(&closer_thread, NULL, closer, NULL); return true; } static int xrun_recovery(snd_pcm_t *handle, int err) { if (err == -EPIPE) { /* under-run */ err = _dl_snd_pcm_prepare(handle); if (err < 0) fprintf(stderr, "Can't recovery from underrun, prepare failed: %s\n", _dl_snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = _dl_snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { err = _dl_snd_pcm_prepare(handle); if (err < 0) fprintf(stderr, "Can't recovery from suspend, prepare failed: %s\n", _dl_snd_strerror(err)); } return 0; } return err; } bool alsa_beeper(int frequency, int duration) { pthread_mutex_lock(&closer_mutex); if(!audio_init()) { pthread_mutex_unlock(&closer_mutex); return false; } /* roughly benchmark the function so that we can return only after the * sound has been played */ struct timeval begin, end; gettimeofday(&begin, NULL); int format_bits = _dl_snd_pcm_format_width(audio_format); unsigned int maxval = (1 << (format_bits - 1)) - 1; int bps = format_bits / 8; int phys_bps = _dl_snd_pcm_format_physical_width(audio_format) / 8; int big_endian = _dl_snd_pcm_format_big_endian(audio_format) == 1; int to_unsigned = _dl_snd_pcm_format_unsigned(audio_format) == 1; int numSamples = duration * audio_sample_rate / 1000; int bufferSize, err, x; char *buffer, *p; bufferSize = numSamples * audio_channels * phys_bps; buffer = (char *)malloc(bufferSize); if(buffer != NULL) { // generate a triangle waveform p = buffer; for(x = 0; x < numSamples; x++) { int res, i, chn; double v; v = fmod(((double) x) / audio_sample_rate * frequency, 1); if (v >= 0.75) v -= 1; else if (v >= 0.25) v = 0.5 - v; res = (int) (v * maxval); if (to_unsigned) res ^= 1U << (format_bits - 1); for(chn = 0; chn < audio_channels; chn ++) { if (big_endian) { for (i = 0; i < bps; i++) *(p + phys_bps - 1 - i) = (res >> i * 8) & 0xff; } else { for (i = 0; i < bps; i++) *(p + i) = (res >> i * 8) & 0xff; } p += phys_bps; } } /* play the waveform */ p = buffer; while(numSamples > 0) { if((err = _dl_snd_pcm_writei (playback_handle, p, numSamples)) < 0) { if(err == -EAGAIN) { continue; } if((err = xrun_recovery(playback_handle, err)) < 0) { fprintf (stderr, "write to audio interface failed (%s)\n", _dl_snd_strerror (err)); break; } } else { numSamples -= err; p += err * phys_bps * audio_channels; } } free(buffer); gettimeofday(&end, NULL); duration -= (int) TDIFF(begin, end); if(duration > 0) usleep(duration * 1000); } gettimeofday(&last_use, NULL); pthread_mutex_unlock(&closer_mutex); return true; } free42-nologo-1.4.77/gtk/audio_alsa.h000644 000765 000024 00000001640 12110237246 017670 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef AUDIO_ALSA_H #define AUDIO_ALSA_H 1 bool alsa_beeper(int frequency, int duration); #endif free42-nologo-1.4.77/gtk/icon.xpm000644 000765 000024 00000047374 12110237246 017112 0ustar00thomasstaff000000 000000 /* XPM */ static char * icon_xpm[] = { "48 48 901 2", " c None", ". c #639ACE", "+ c #514B47", "@ c #484441", "# c #4F4E4D", "$ c #639ACD", "% c #629ACD", "& c #587189", "* c #454441", "= c #44413C", "- c #73635A", "; c #534642", "> c #494646", ", c #6399CD", "' c #6299CD", ") c #57585A", "! c #45423A", "~ c #4A423C", "{ c #3B3B36", "] c #5B5858", "^ c #907D75", "/ c #544433", "( c #464443", "_ c #5F81A2", ": c #504A49", "< c #564D4A", "[ c #514C4A", "} c #746360", "| c #564D48", "1 c #3A3935", "2 c #474442", "3 c #645C52", "4 c #61534C", "5 c #4D4744", "6 c #57687C", "7 c #766464", "8 c #9D8F97", "9 c #6B5556", "0 c #635654", "a c #7E6F71", "b c #554A45", "c c #3C3A39", "d c #3F3B39", "e c #5F5554", "f c #68574F", "g c #4F4746", "h c #4D453F", "i c #6394C3", "j c #524F51", "k c #4A4341", "l c #877578", "m c #887D83", "n c #5E5854", "o c #625D5F", "p c #796064", "q c #4F4A48", "r c #494544", "s c #4B4645", "t c #423D36", "u c #353534", "v c #59524D", "w c #3B3835", "x c #403E39", "y c #4C4845", "z c #669BCD", "A c #5E84A7", "B c #4B4A4A", "C c #534846", "D c #87777B", "E c #86797C", "F c #746963", "G c #837972", "H c #706554", "I c #605C5B", "J c #776362", "K c #5F4F4C", "L c #4B4544", "M c #4A4441", "N c #44403A", "O c #383635", "P c #3C3B31", "Q c #3D3B33", "R c #403B35", "S c #43433C", "T c #629ACE", "U c #5B6875", "V c #444039", "W c #47403C", "X c #56504E", "Y c #635C57", "Z c #7D7266", "` c #867767", " . c #867663", ".. c #83776A", "+. c #776B61", "@. c #63594E", "#. c #766763", "$. c #6B564F", "%. c #4B453F", "&. c #484240", "*. c #3B3B2F", "=. c #383730", "-. c #3C3931", ";. c #3E3A37", ">. c #40403B", ",. c #504B4B", "'. c #6496C7", "). c #545155", "!. c #393532", "~. c #474134", "{. c #464039", "]. c #625E56", "^. c #83746B", "/. c #857564", "(. c #867461", "_. c #827769", ":. c #81735F", "<. c #5F5549", "[. c #7C7378", "}. c #7E615D", "|. c #4C423B", "1. c #4C4337", "2. c #48433A", "3. c #3D3B37", "4. c #37372E", "5. c #3E392E", "6. c #3F3931", "7. c #413D30", "8. c #45433F", "9. c #6585A6", "0. c #43423E", "a. c #34322E", "b. c #49433A", "c. c #5F4333", "d. c #473D30", "e. c #434033", "f. c #453E38", "g. c #494039", "h. c #5D5750", "i. c #7C7265", "j. c #837668", "k. c #847565", "l. c #857663", "m. c #83796E", "n. c #726B6B", "o. c #81686A", "p. c #504340", "q. c #7D6E69", "r. c #70574D", "s. c #423B33", "t. c #3A362E", "u. c #3F3729", "v. c #3C3932", "w. c #3F3930", "x. c #444135", "y. c #576A7B", "z. c #39332C", "A. c #503B36", "B. c #37342E", "C. c #574D49", "D. c #453B35", "E. c #423B32", "F. c #403E35", "G. c #413D32", "H. c #443D36", "I. c #463F36", "J. c #564F4F", "K. c #776C5D", "L. c #85776A", "M. c #837867", "N. c #847768", "O. c #847B6B", "P. c #706555", "Q. c #67605B", "R. c #8B7674", "S. c #644E48", "T. c #766767", "U. c #6C554A", "V. c #4B3D33", "W. c #39362B", "X. c #39352F", "Y. c #3B3730", "Z. c #3F3831", "`. c #413D31", " + c #4D4940", ".+ c #6B97C4", "++ c #48525D", "@+ c #2C2D28", "#+ c #574B43", "$+ c #624035", "%+ c #633E37", "&+ c #2E2C27", "*+ c #313232", "=+ c #55433C", "-+ c #5F403A", ";+ c #433C35", ">+ c #3E3E38", ",+ c #424039", "'+ c #534A40", ")+ c #726963", "!+ c #84786D", "~+ c #857765", "{+ c #857972", "]+ c #887768", "^+ c #7C6E5E", "/+ c #564F46", "(+ c #8B7E83", "_+ c #6C564E", ":+ c #594C46", "<+ c #6C574F", "[+ c #4D443E", "}+ c #38352E", "|+ c #393534", "1+ c #3C3732", "2+ c #3E3A30", "3+ c #423E2F", "4+ c #504840", "5+ c #5D88B0", "6+ c #423931", "7+ c #2C2D27", "8+ c #2B2C2A", "9+ c #504643", "0+ c #6F5B53", "a+ c #5B3A2F", "b+ c #473831", "c+ c #363428", "d+ c #5A4D49", "e+ c #463E33", "f+ c #3C372D", "g+ c #403C32", "h+ c #413F36", "i+ c #413D3C", "j+ c #4C443A", "k+ c #6D6359", "l+ c #817869", "m+ c #877964", "n+ c #87796A", "o+ c #887A6D", "p+ c #857465", "q+ c #584D42", "r+ c #847376", "s+ c #725752", "t+ c #594E41", "u+ c #65534D", "v+ c #50453E", "w+ c #3D382E", "x+ c #38362F", "y+ c #3F3A2E", "z+ c #413835", "A+ c #4D4137", "B+ c #669ACD", "C+ c #576B81", "D+ c #34312F", "E+ c #272828", "F+ c #3B3933", "G+ c #2E2C2B", "H+ c #2E2A26", "I+ c #2B2D2B", "J+ c #3C3B39", "K+ c #433C34", "L+ c #493A36", "M+ c #312F2A", "N+ c #373632", "O+ c #32322C", "P+ c #443C33", "Q+ c #59413B", "R+ c #423E36", "S+ c #3E3D35", "T+ c #413E36", "U+ c #49423F", "V+ c #645F5A", "W+ c #82776D", "X+ c #857B70", "Y+ c #877A6D", "Z+ c #8B796B", "`+ c #655B52", " @ c #7A6D6D", ".@ c #826C69", "+@ c #584A44", "@@ c #63514B", "#@ c #514742", "$@ c #3F3D35", "%@ c #3C3930", "&@ c #4A433C", "*@ c #665C5C", "=@ c #6794C1", "-@ c #4A4D50", ";@ c #302F2D", ">@ c #2E2E2A", ",@ c #222324", "'@ c #73605F", ")@ c #5A4A44", "!@ c #663F36", "~@ c #41302F", "{@ c #363532", "]@ c #6F5D57", "^@ c #3C3532", "/@ c #35312D", "(@ c #34322F", "_@ c #31302E", ":@ c #635756", "<@ c #433A38", "[@ c #3C3B35", "}@ c #423F3A", "|@ c #3E3C38", "1@ c #403C31", "2@ c #4A423B", "3@ c #5D5650", "4@ c #82796F", "5@ c #887A6B", "6@ c #8A7868", "7@ c #8B785D", "8@ c #877664", "9@ c #716151", "0@ c #5F5951", "a@ c #89726F", "b@ c #5E4F48", "c@ c #4E413B", "d@ c #433D37", "e@ c #4A4541", "f@ c #736363", "g@ c #608CB6", "h@ c #3F3C35", "i@ c #242422", "j@ c #34322C", "k@ c #603B35", "l@ c #44322B", "m@ c #282723", "n@ c #716157", "o@ c #51423B", "p@ c #50352D", "q@ c #262824", "r@ c #262626", "s@ c #292A21", "t@ c #35342C", "u@ c #3D342C", "v@ c #372E27", "w@ c #292A27", "x@ c #2E2D2C", "y@ c #403C33", "z@ c #473A31", "A@ c #413C36", "B@ c #3E3D33", "C@ c #453F3F", "D@ c #594E42", "E@ c #81725D", "F@ c #88786D", "G@ c #89766F", "H@ c #887868", "I@ c #8A775F", "J@ c #776750", "K@ c #494139", "L@ c #49433C", "M@ c #3F3E2E", "N@ c #756763", "O@ c #6599CC", "P@ c #586F87", "Q@ c #3F382F", "R@ c #2D2C28", "S@ c #252623", "T@ c #75645C", "U@ c #3C332B", "V@ c #2D2E27", "W@ c #2E2D2B", "X@ c #635652", "Y@ c #2F2F2C", "Z@ c #292A28", "`@ c #2A2C26", " # c #524B48", ".# c #483D37", "+# c #3E322C", "@# c #32322A", "## c #5F5350", "$# c #5D4841", "%# c #4E3B33", "&# c #454037", "*# c #423F3B", "=# c #3E3C35", "-# c #454135", ";# c #564E4B", "># c #786D60", ",# c #877B6E", "'# c #887967", ")# c #887767", "!# c #736047", "~# c #494238", "{# c #3A3732", "]# c #4B423E", "^# c #6E6E77", "/# c #649ACD", "(# c #4D545C", "_# c #2A2928", ":# c #2A2A25", "<# c #503E37", "[# c #5F3A34", "}# c #2C2C26", "|# c #272823", "1# c #34302D", "2# c #49312C", "3# c #222220", "4# c #292829", "5# c #2D2E2D", "6# c #4F3938", "7# c #553632", "8# c #2A2B29", "9# c #2B2A2A", "0# c #2C2E2B", "a# c #3D342E", "b# c #3A3029", "c# c #343231", "d# c #3C3838", "e# c #363531", "f# c #3B3931", "g# c #473E39", "h# c #484036", "i# c #3D3B34", "j# c #433F37", "k# c #544844", "l# c #73675A", "m# c #887966", "n# c #67574E", "o# c #474239", "p# c #3B372C", "q# c #544C48", "r# c #6685A6", "s# c #659ACC", "t# c #668FB6", "u# c #3C4247", "v# c #2B2B29", "w# c #292A2A", "x# c #4D413B", "y# c #6B605D", "z# c #352E2A", "A# c #323028", "B# c #252522", "C# c #33302E", "D# c #614034", "E# c #51362D", "F# c #2A2C2B", "G# c #564C49", "H# c #534740", "I# c #4D3731", "J# c #48372B", "K# c #2C2D2B", "L# c #3E3A34", "M# c #63524B", "N# c #77473E", "O# c #382F2E", "P# c #2F302D", "Q# c #31322D", "R# c #4D4847", "S# c #5D3F34", "T# c #443C39", "U# c #434137", "V# c #3F3C32", "W# c #43403D", "X# c #4E4A4B", "Y# c #534B47", "Z# c #423F3D", "`# c #38392C", " $ c #554E50", ".$ c #6989AC", "+$ c #546B80", "@$ c #32302B", "#$ c #3D2D29", "$$ c #4E3731", "%$ c #2F2E2C", "&$ c #242524", "*$ c #232322", "=$ c #292B28", "-$ c #302E2B", ";$ c #332E2A", ">$ c #222221", ",$ c #504645", "'$ c #333031", ")$ c #51352F", "!$ c #2C2B26", "~$ c #282927", "{$ c #51494C", "]$ c #393839", "^$ c #3A322B", "/$ c #4E3530", "($ c #2B2B27", "_$ c #4B4547", ":$ c #2D2F2E", "<$ c #32302D", "[$ c #3E322F", "}$ c #383432", "|$ c #59514F", "1$ c #413D39", "2$ c #38362E", "3$ c #403B36", "4$ c #463F3D", "5$ c #413C38", "6$ c #403F3A", "7$ c #393736", "8$ c #5B514E", "9$ c #6D85A5", "0$ c #3A3532", "a$ c #222924", "b$ c #934639", "c$ c #CB614E", "d$ c #3D2E2B", "e$ c #2E2E29", "f$ c #2D2D28", "g$ c #272825", "h$ c #322E2E", "i$ c #4E3936", "j$ c #63372E", "k$ c #212320", "l$ c #232424", "m$ c #282A26", "n$ c #2C2A27", "o$ c #3C2E28", "p$ c #292928", "q$ c #2B2A28", "r$ c #57372E", "s$ c #633C35", "t$ c #2A2826", "u$ c #272727", "v$ c #665C58", "w$ c #5A3D33", "x$ c #5A3A33", "y$ c #2A2B25", "z$ c #30312D", "A$ c #59504E", "B$ c #4E3933", "C$ c #46413D", "D$ c #5F5659", "E$ c #6A8FB4", "F$ c #3B3632", "G$ c #282828", "H$ c #2C2B28", "I$ c #503531", "J$ c #372B23", "K$ c #683730", "L$ c #292525", "M$ c #2F2F2D", "N$ c #322F2B", "O$ c #212222", "P$ c #514442", "Q$ c #4D4242", "R$ c #302929", "S$ c #272726", "T$ c #2B2B2A", "U$ c #212121", "V$ c #5C5251", "W$ c #543F3B", "X$ c #42302E", "Y$ c #282728", "Z$ c #453F3B", "`$ c #474246", " % c #372C2A", ".% c #312F2E", "+% c #2A2B2A", "@% c #584E49", "#% c #6B5753", "$% c #413230", "%% c #35322E", "&% c #2D2C2B", "*% c #514645", "=% c #7F6665", "-% c #43372F", ";% c #37342D", ">% c #383632", ",% c #635B5E", "'% c #6494C2", ")% c #443D38", "!% c #262726", "~% c #39322D", "{% c #574E4C", "]% c #543634", "^% c #493230", "/% c #232A25", "(% c #2A2A29", "_% c #272824", ":% c #543834", "<% c #623837", "[% c #25221F", "}% c #232324", "|% c #252527", "1% c #302D2B", "2% c #45302E", "3% c #25221E", "4% c #645553", "5% c #2E2E2D", "6% c #282928", "7% c #2D2D2B", "8% c #262624", "9% c #2B2C2B", "0% c #423734", "a% c #5B372D", "b% c #2B2925", "c% c #242523", "d% c #463D36", "e% c #533731", "f% c #282523", "g% c #3B3535", "h% c #32302F", "i% c #3C393A", "j% c #6C676C", "k% c #678FB8", "l% c #4B463F", "m% c #282825", "n% c #3C352E", "o% c #5E514E", "p% c #4F4241", "q% c #2E2F2B", "r% c #2E2E2B", "s% c #252624", "t% c #302C29", "u% c #413E3B", "v% c #553331", "w% c #48332F", "x% c #252524", "y% c #38322F", "z% c #5F423F", "A% c #643933", "B% c #262421", "C% c #262625", "D% c #2A2A28", "E% c #2D2A28", "F% c #553631", "G% c #302C28", "H% c #323131", "I% c #49423E", "J% c #4A352D", "K% c #322D27", "L% c #2C2E29", "M% c #77665D", "N% c #67463B", "O% c #3F322D", "P% c #282A25", "Q% c #474341", "R% c #6B6B76", "S% c #688FB9", "T% c #292828", "U% c #2A2927", "V% c #614F43", "W% c #2B2B28", "X% c #2B2B26", "Y% c #282829", "Z% c #3B2D2C", "`% c #5A3533", " & c #232523", ".& c #242323", "+& c #313025", "@& c #332E26", "#& c #22221F", "$& c #584D45", "%& c #3E3633", "&& c #392D28", "*& c #3D302E", "=& c #2A2C27", "-& c #242525", ";& c #575050", ">& c #57423F", ",& c #673C34", "'& c #272928", ")& c #262524", "!& c #332E2D", "~& c #4C3531", "{& c #2A2923", "]& c #302D2C", "^& c #373234", "/& c #2A2A26", "(& c #4A4440", "_& c #707382", ":& c #3F3D39", "<& c #292825", "[& c #282A22", "}& c #313028", "|& c #2A2A23", "1& c #302D2D", "2& c #4F494A", "3& c #4F3231", "4& c #222421", "5& c #272724", "6& c #503632", "7& c #573527", "8& c #262525", "9& c #2D2F2B", "0& c #312B2A", "a& c #302F2B", "b& c #594E4D", "c& c #37352F", "d& c #2D2B28", "e& c #2D2D2C", "f& c #2F2F2B", "g& c #282827", "h& c #43403F", "i& c #594440", "j& c #623A35", "k& c #2A2B26", "l& c #2B2B24", "m& c #4D4848", "n& c #79717C", "o& c #6398CA", "p& c #3E3931", "q& c #272826", "r& c #2C2C27", "s& c #2E2F2A", "t& c #2F2C28", "u& c #433C3A", "v& c #252625", "w& c #3D302D", "x& c #2D2F27", "y& c #222424", "z& c #433D3B", "A& c #4C4542", "B& c #4C302B", "C& c #4A322C", "D& c #252825", "E& c #252525", "F& c #323032", "G& c #54433D", "H& c #5C3832", "I& c #2C2825", "J& c #282826", "K& c #292A26", "L& c #2A2A27", "M& c #3D2F28", "N& c #342E26", "O& c #423B3B", "P& c #47413F", "Q& c #39312B", "R& c #2D2D27", "S& c #605858", "T& c #747785", "U& c #6398CB", "V& c #72615C", "W& c #39362F", "X& c #2E2E28", "Y& c #2F2E27", "Z& c #232323", "`& c #46342D", " * c #4F362C", ".* c #292A29", "+* c #262424", "@* c #322E2B", "#* c #392F29", "$* c #413229", "%* c #292625", "&* c #2C2D29", "** c #2B2C27", "=* c #413E3D", "-* c #4C4239", ";* c #573831", ">* c #6C665F", ",* c #6E809B", "'* c #6399CB", ")* c #70625F", "!* c #36332A", "~* c #272923", "{* c #303126", "]* c #272925", "^* c #272929", "/* c #6E3D39", "(* c #46312B", "_* c #222523", ":* c #54382F", "<* c #50322E", "[* c #292A25", "}* c #2B2C2C", "|* c #2F302B", "1* c #2D2D24", "2* c #2E2D27", "3* c #3E3933", "4* c #2B2B25", "5* c #7A6C68", "6* c #6D788D", "7* c #5E514B", "8* c #33322E", "9* c #272725", "0* c #2B2D26", "a* c #2A2925", "b* c #353331", "c* c #312E29", "d* c #2D2E2B", "e* c #423C37", "f* c #50473E", "g* c #4A3330", "h* c #4B3029", "i* c #463A35", "j* c #5E3B35", "k* c #2F2B26", "l* c #262723", "m* c #3D3D38", "n* c #77696B", "o* c #707E97", "p* c #5C534F", "q* c #302F26", "r* c #2E2F29", "s* c #242423", "t* c #242424", "u* c #262725", "v* c #3B312E", "w* c #43322D", "x* c #343431", "y* c #373231", "z* c #373029", "A* c #2B2F28", "B* c #443F3C", "C* c #60554D", "D* c #41322E", "E* c #2D2924", "F* c #282A23", "G* c #3F403A", "H* c #7B6867", "I* c #6A8DB4", "J* c #2D2E2A", "K* c #433E40", "L* c #3D3632", "M* c #753F34", "N* c #41312F", "O* c #21221F", "P* c #262623", "Q* c #3B2E2B", "R* c #53352E", "S* c #2D2E2C", "T* c #434241", "U* c #776565", "V* c #648FBB", "W* c #584C49", "X* c #2C2B29", "Y* c #403A39", "Z* c #594E4C", "`* c #3F302F", " = c #2C2D2C", ".= c #292827", "+= c #3B393A", "@= c #5A3735", "#= c #402C26", "$= c #232425", "%= c #4A474A", "&= c #756A6D", "*= c #678EB9", "== c #514643", "-= c #272827", ";= c #282727", ">= c #2D2D29", ",= c #2B2929", "'= c #232422", ")= c #443231", "!= c #393432", "~= c #2A2828", "{= c #655E63", "]= c #75707C", "^= c #6593C1", "/= c #4B4540", "(= c #2C2D2A", "_= c #2A2B27", ":= c #292722", "<= c #6B3C31", "[= c #1F2020", "}= c #2B2B2B", "|= c #696367", "1= c #716A72", "2= c #836A69", "3= c #43403C", "4= c #2D2D2D", "5= c #363432", "6= c #625B5A", "7= c #2F2D2A", "8= c #5F3831", "9= c #302C27", "0= c #2E2F2F", "a= c #625B5C", "b= c #7B7883", "c= c #6299CC", "d= c #6399CE", "e= c #726059", "f= c #403A35", "g= c #292927", "h= c #2E2D2A", "i= c #312D2C", "j= c #222322", "k= c #31312F", "l= c #675A5D", "m= c #7286A4", "n= c #6495C4", "o= c #735F5F", "p= c #3B3732", "q= c #262727", "r= c #2A2827", "s= c #2F2E2A", "t= c #262622", "u= c #232423", "v= c #716464", "w= c #69809E", "x= c #6F5B52", "y= c #3C3733", "z= c #282824", "A= c #2F2E2B", "B= c #6E676B", "C= c #6D7A95", "D= c #645557", "E= c #3A3836", "F= c #5C5453", "G= c #4B494A", "H= c #545154", " ", " ", " ", " ", " . . . . . . + @ # ", " . $ . . % % % % % % . & * = - ; > ", " $ , . . . . . . . . . ' ) ! ~ { ] ^ / ( ", " . . . . . . . . . . . . _ : < [ } | 1 2 3 4 5 ", " . . . . . . . . . . . . 6 > 7 8 9 0 a b c d e f g h ", " . . . . . . . . . . . i j k l m n o p q r s t u v w x y ", " . $ . . . . . . . . z A B C D E F G H I J K L M N O P Q R S ", " . T . . . . . . . . . U V W X Y Z ` ...+.@.#.$.@ %.&.*.=.-.;.>.,. ", " . . . . . . . . . . '.).!.N ~.{.h ].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8. ", " . . . . . . . . . 9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.<.n.o.p.q.r.s.t.u.v.w.x. ", " . . . . . . . . . y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. + ", " % . . . . . . . .+++@+#+$+%+&+*+3.=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+:+<+[+}+|+1+2+3+4+ ", " . . . . . . . 5+6+7+8+9+0+a+b+c+d+e+f+g+h+g+i+j+k+l+m+n+o+p+q+r+s+t+u+v+w+x+y+z+A+ ", " . . . . . . B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+p+`+ @.@+@@@#@$@%@&@*@ ", " . $ . . . =@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@4@5@6@7@8@9@0@a@b@c@d@e@f@ ", " . . . , g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z@A+A@B@C@D@E@F@G@H@I@J@K@L@M@~ N@ ", " . . O@P@Q@R@S@T@^@U@V@W@X@Y@Z@D+`@ #.#+#@#w@##$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^# ", " . % /#(#_#:#<#[#}#|#>@1#2#3#4#5#6#7#8#9#0#a#b#c#d#e#f#g#h#i#j#k#l#m#n#o#p#q#r#s# ", " . t#u#v#w#x#y#z#A#W@B#C#D#E#F#G#H#I#J#K#L#M#N#O#P#Q#R#S#T#U#V#W#X#Y#Z#`# $.$. . ", " +$@$#$$$%$&$*$=$-$;$>$,$'$)$!$~${$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$A@7$8$9$. . . ", " 0$a.a$b$c$d$e$f$g$h$i$j$k$l$m$n$o$p$q$b r$s$t$u$v$w$x$y$z$A$B$-+C$=#w D$E$. . . . ", " F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*%=%-%;%>%,%'%. . . . . ", " )%!%~%{%]%^%/%(%_%:%<%[%}%|%1%2%3%4%5%6%7%8%*$9%0%a%b%c%Y@d%e%f%g%h%i%j%k%. . . . . . ", "l%m%n%o%p%8+q%r%s%t%u%v%w%g$x%y%z%A%B%C%D%E%F%G%H%I%J%K%L%m%M%N%O%P%Q%R%S%. . . . . . ", " T%U%V%W%X%Y%Z%`% &S$.&+&@&#&$&%&&&*&=&-&;&>&,&'&)&9#!&~&{&]&^&/&(&_&'.. . . . . . . ", " :&<&[&}&|&1&2&^%3&4&5&r@6&7&*$C%8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&. . . . . . . . ", " p&q&r&s&t&u&v&w&x&y&z&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&Q&R&S&T&U&. . . . . . . . . ", " V&W&g$X&Y&Z&i@q&`& *.*H$+*@*#*(%D@d%$*%*&***=*-*;*k&&$w@>*,*'*. . . . . . . . . ", " )*!*~*7%{*]*m%^*/*(*_*r@C%:*<*[*}*q&|*1*2*1$3*=&4*e#5*6*. . . . . . . . . . . ", " 7*8*9*s&0*a*b*C%c*d*v&e*f*g*h*D&g&]*i*j*k*s%l*m*n*o*. . . . . . . . . . , ", " p*q*G$r*7+s*t*u*v*w*x*y*S$z*=&A*B*C*D*E*F*G*H*I*. . . . . . . . . . . ", " b&W@S$J*W@.&K*L*M*N*O*P*G$Q*R*w@8%C%S*T*U*V*. . . . . . . . . . $ . ", " W*X*u$S*T%Y*Z*t%`* =6%.=+=@=#=$=r@%=&=*=. . . . . . . . . . . . ", " ==-=;=e&>=,='=k&)=w&p$!=p$~=p${=]=^=. . . . . . . . . . . . ", " /=g$u$(=_=C%*$:=<=4*[=U$}=|=1=. . . . . . . . . . . $ . ", " 2=3=G$G$4=>=5=6=7=8=9=0=a=b=c=. . . . . . . . . . . d= ", " e=f=u*g=r%h=i=j=.=k=l=m=n=. . . . . . . . . . d=. ", " o=p=q=r=s=t=u=e&v=w=. . . . . . . . . . . . ", " x=y=z=(%T$A=B=C=' . . . . . . d=. . . ", " D=E=u$v#F= . . . . . . ", " G=H= ", " ", " ", " "}; free42-nologo-1.4.77/gtk/keymap.txt000644 000765 000024 00000011277 12110237246 017454 0ustar00thomasstaff000000 000000 # Default key mapping table for Free42 (GTK version) # # Format: [Ctrl|Alt|Shift]* : # is a string consisting of the name of the symbolic constant as # defined in , with the leading "GDK_" removed; is a # sequence of zero or more HP-42S key codes. The key codes must be between 1 # and 255; codes 1..37 correspond to actual keys on the calculator's keyboard, # while codes 38..255 can be used to refer to extra keys defined by customized # skins. # # Anything from a "#" until the end of the line is a comment, and is ignored. # White space (Space, Tab) separates tokens in the key map, but is otherwise # ignored. # # NOTE: Key events that translate to a single character in the printable # ASCII range (32..126) are matched without regard of the state of the Shift # and Caps Lock keys -- so you don't have to use "Shift A" to match uppercase # "A", etc. For other key events, shift *is* relevant (e.g. "Return" is not # the same as "Shift Return"). F1 : 1 # menu key 1 Shift F1 : 28 1 # menu key 1, shifted F2 : 2 # menu key 2 Shift F2 : 28 2 # menu key 2, shifted F3 : 3 # menu key 3 Shift F3 : 28 3 # menu key 3, shifted F4 : 4 # menu key 4 Shift F4 : 28 4 # menu key 4, shifted F5 : 5 # menu key 5 Shift F5 : 28 5 # menu key 5, shifted F6 : 6 # menu key 6 Shift F6 : 28 6 # menu key 6, shifted a : 1 # Sigma+ ("Accumulate") A : 28 1 # Sigma- v : 2 # 1/X ("inVerse") V : 28 2 # Y^X q : 3 # sqrt(X) ("sQuare root") Q : 28 3 # X^2 o : 4 # LOG ("lOg, not Ln") O : 28 4 # 10^X l : 5 # LN ("Ln, not lOg") L : 28 5 # E^X x : 6 # XEQ X : 28 6 # GTO m : 7 # STO (m as in memory) M : 28 7 # COMPLEX r : 8 # RCL R : 28 8 # % d : 9 # RDN (d as in down) D : 28 9 # PI s : 10 # SIN S : 28 10 # ASIN c : 11 # COS C : 28 11 # ACOS t : 12 # TAN T : 28 12 # ATAN Return : 13 # ENTER KP_Enter : 13 # ENTER Shift Return : 28 13 # ALPHA w : 14 # X<>Y ("sWap") W : 28 14 # LASTX n : 15 # +/- ("Negative") N : 28 15 # MODES e : 16 # E E : 28 16 # DISP BackSpace : 17 # <- Shift BackSpace : 28 17 # CLEAR Up : 18 # up Shift Up : 28 18 # BST 7 : 19 # 7 KP_7 : 19 # 7 ampersand : 28 19 # SOLVER 8 : 20 # 8 KP_8 : 20 # 8 Alt 8 : 28 20 # Integ f(x) (note: can't use "Shift 8" because that's "*", which we use for "multiply") 9 : 21 # 9 KP_9 : 21 # 9 parenleft : 28 21 # MATRIX slash : 22 # divide KP_Divide : 22 # divide question : 28 22 # STAT Down : 23 # down Shift Down : 28 23 # SST 4 : 24 # 4 KP_4 : 24 # 4 dollar : 28 24 # BASE 5 : 25 # 5 KP_5 : 25 # 5 percent : 28 25 # CONVERT 6 : 26 # 6 KP_6 : 26 # 6 asciicircum : 28 26 # FLAGS asterisk : 27 # multiply KP_Multiply : 27 # multiply Ctrl 8 : 28 27 # PROB ("*" is already shifted so we have to do something odd here) 1 : 29 # 1 KP_1 : 29 # 1 exclam : 28 29 # ASSIGN 2 : 30 # 2 KP_2 : 30 # 2 at : 28 30 # CUSTOM 3 : 31 # 3 KP_3 : 31 # 3 numbersign : 28 31 # PGM.FCN minus : 32 # - KP_Subtract : 32 # - underscore : 28 32 # PRINT Escape : 33 # EXIT Shift Escape : 28 33 # OFF 0 : 34 # 0 KP_0 : 34 # 0 parenright : 28 34 # TOP.FCN period : 35 # . comma : 35 # . KP_Decimal : 35 # . KP_Separator : 35 # . greater : 28 35 # SHOW less : 28 35 # SHOW backslash : 36 # R/S bar : 28 36 # PRGM plus : 37 # + KP_Add : 37 # + equal : 28 37 # CATALOG ("+" is already shifted so we have to do something odd here) free42-nologo-1.4.77/gtk/Makefile000644 000765 000024 00000007174 12110237246 017066 0ustar00thomasstaff000000 000000 ############################################################################### # Free42 -- an HP-42S calculator simulator # Copyright (C) 2004-2013 Thomas Okken # # 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, see http://www.gnu.org/licenses/. ############################################################################### CXXFLAGS := -MMD -Wall -Wno-parentheses -Wno-write-strings -g -I/usr/X11R6/include -fno-exceptions -fno-rtti $(shell pkg-config --cflags gtk+-2.0) -DVERSION="\"$(shell cat ../VERSION)\"" LDFLAGS = -L/usr/X11R6/lib LIBS := -lXmu $(shell pkg-config --libs gtk+-2.0) ifeq "$(shell uname -s)" "Linux" LDFLAGS += -Wl,--hash-style=both endif SRCS = shell_main.cc shell_skin.cc skins.cc keymap.cc shell_loadimage.cc \ shell_spool.cc core_main.cc core_commands1.cc core_commands2.cc \ core_commands3.cc core_commands4.cc core_commands5.cc \ core_commands6.cc core_commands7.cc core_display.cc core_globals.cc \ core_helpers.cc core_keydown.cc core_linalg1.cc core_linalg2.cc \ core_math1.cc core_math2.cc core_phloat.cc core_sto_rcl.cc \ core_tables.cc core_variables.cc OBJS = shell_main.o shell_skin.o skins.o keymap.o shell_loadimage.o \ shell_spool.o core_main.o core_commands1.o core_commands2.o \ core_commands3.o core_commands4.o core_commands5.o \ core_commands6.o core_commands7.o core_display.o core_globals.o \ core_helpers.o core_keydown.o core_linalg1.o core_linalg2.o \ core_math1.o core_math2.o core_phloat.o core_sto_rcl.o \ core_tables.o core_variables.o ifdef BCD_MATH CXXFLAGS += -DBCD_MATH SRCS += bcdfloat.cc bcd.cc bcdmath.cc bcdfloat2.cc OBJS += bcdfloat.o bcd.o bcdmath.o bcdfloat2.o EXE = free42dec else EXE = free42bin endif ifdef AUDIO_ALSA # Note: the name of the libasound shared library that is usually compiled into # the executable is defined in the corresponding *.la file, in the 'dlname' # property. I suppose it would be better to grab it from there at build time, # rather than hard-coding it into the Makefile like this, but as long as the # build environment doesn't change (still Fedora 6 at the time of writing), # this is fine. CXXFLAGS += -DAUDIO_ALSA -DALSALIB="\"libasound.so.2\"" SRCS += audio_alsa.cc OBJS += audio_alsa.o endif ifdef OLPC CXXFLAGS += -DOLPC endif $(EXE): $(OBJS) $(CXX) -o $(EXE) $(LDFLAGS) $(OBJS) $(LIBS) ifdef OLPC tar xvfz Free42.activity.tgz cp $(EXE) Free42.activity/bin zip -r Free42.xo Free42.activity rm -rf Free42.activity endif $(SRCS) skin2cc.cc keymap2cc.cc skin2cc.conf: symlinks .cc.o: $(CXX) $(CXXFLAGS) -c -o $@ $< skin2cc: skin2cc.cc $(CXX) -o skin2cc skin2cc.cc skins.cc: skin2cc skin2cc.conf ./skin2cc keymap2cc: keymap2cc.cc $(CXX) -o keymap2cc keymap2cc.cc keymap.cc: keymap2cc keymap.txt ./keymap2cc symlinks: for fn in `cd ../common; /bin/ls`; do ln -s ../common/$$fn; done touch symlinks clean: FORCE rm -f `find . -type l` \ skin2cc skin2cc.exe skins.cc \ keymap2cc keymap2cc.exe keymap.cc \ *.o *.d *.i *.ii *.s symlinks core.* cleaner: FORCE rm -f `find . -type l` \ free42bin free42bin.exe free42dec free42dec.exe \ skin2cc skin2cc.exe skins.cc \ keymap2cc keymap2cc.exe keymap.cc \ *.o *.d *.i *.ii *.s symlinks core.* FORCE: -include $(SRCS:.cc=.d) free42-nologo-1.4.77/gtk/README000644 000765 000024 00000014474 12110237246 016307 0ustar00thomasstaff000000 000000 About Free42 Free42 is a complete re-implementation of the HP-42S scientific programmable RPN calculator, which was made from 1988 until 1995 by Hewlett-Packard. Free42 is a complete rewrite and contains no HP code whatsoever. At this time, the author supports versions that run on Pocket PC, Microsoft Windows, PalmOS, Unix, and Mac OS X (application and Dashboard widget). Installing Free42: Copy free42dec (or free42bin, or both) to wherever you want it, e.g. $HOME/bin or /usr/local/bin, and make sure that that directory is in your PATH environment variable. When Free42 runs, it will create a directory named .free42 in your home directory; in this directory, it will create three files, 'state', 'print', and 'keymap' (the calculator's internal state, the contents of the print-out window, and the PC keyboard map); also, if you want to use a non-standard skin with Free42, the .free42 directory is where you have to store the skin's layout and bitmap files. Uninstalling Free42: Remove free42dec, free42bin, and the $HOME/.free42 directory and its contents. NOTE: The binary in this package was built on a PC running Fedora Core release 6, and it is dynamically linked against glibc version 2.5, libstdc++ version 4.1.2, and GTK+ version 2.10.13. If your system has different versions of these libraries, the binary in this package may not work. In this case, please download the Free42 source package and compile Free42 using your installation's own compiler and libraries. Documentation The ultimate documentation for Free42 is the manual for the HP-42S. You can obtain this manual in PDF format by purchasing the CD or DVD set from The Museum of HP Calculators (http://hpmuseum.org/). Alternatively, there is an independently written HP-42S/Free42 manual, by Jose Lauro Strapasson, which you can download free at http://joselauro.com/42s.pdf. Keyboard Mapping You don't have to use the mouse to press the keys of the emulated calculator keyboard; all keys can be operated using the PC's keyboard as well. The standard keyboard mapping is as follows: Sigma+: F1, or 'a' as in "Accumulate" Sigma-: Shift F1, or 'A' (Shift a) 1/X: F2, or 'v' as in "inVerse" Y^X: Shift F2, or 'V' (Shift v) SQRT: F3, or 'q' as in "sQuare root" X^2: Shift F3, or 'Q' (Shift q) LOG: F4, or 'o' as in "lOg, not ln" 10^X: Shift F4, or 'O' (Shift o) LN: F5, or 'l' as in "Ln, not log" E^X: Shift F5, or 'L" (Shift l) XEQ: F6, or 'x' as in "Xeq" GTO: Shift F6, or 'X' (Shift x) STO: 'm' as in "Memory" COMPLEX: 'M' (Shift m) RCL: 'r' as in "Rcl" %: 'R' (Shift r) Rdown: 'd' as in "Down" pi: 'D' (Shift d) SIN: 's' as in "Sin" ASIN: 'S' (Shift s) COS: 'c' as in "Cos" ACOS: 'C' (Shift c) TAN: 't' as in "Tan" ATAN: 'T' (Shift t) ENTER: Enter or Return ALPHA: Shift Enter or Shift Return X<>Y: 'w' as in "sWap" LASTX: 'W' (Shift w) +/-: 'n' as in "Negative" MODES: 'N' (Shift n) E: 'e' as in "Exponent" (duh...) DISP: 'E' (Shift e) <-: Backspace CLEAR: Shift Backspace : CursorUp BST: Shift CursorUp 7: '7' SOLVER: '&' (Shift 7) 8: '8' Integral: Alt 8 (can't use Shift 8 because that's 'x' (multiply)) 9: '9' MATRIX: '(' (Shift 9) divide: '/' STAT: '?' (Shift /) : CursorDown SST: Shift CursorDown 4: '4' BASE: '$' (Shift 4) 5: '5' CONVERT: '%' (Shift 5) 6: '6' FLAGS: '^' (Shift 6) multiply: '*' PROB: Ctrl 8 (can't use Shift * because '*' is shifted itself (Shift 8)) Shift: Shift 1: '1' ASSIGN: '!' (Shift 1) 2: '2' CUSTOM: '@' (Shift 2) 3: '3' PGM.FCN: '#' (Shift 3) subtract: '-' PRINT: '_' (Shift -) EXIT: Escape OFF: Shift Escape 0: '0' TOP.FCN: ')' (Shift 0) .: . or , SHOW: '<' or '>' (Shift . or Shift ,) R/S: '\' (ummm... because it's close to Enter (or Return)) PRGM: '|' (Shift \) add: '+' CATALOG: '=' (Can't use Shift + because + is shifted itself (shift =)) In A..F mode (meaning the "A..F" submenu of the BASE menu), the PC keyboard keys A through F are mapped to the top row of the calculator's keyboard (Sigma+ through XEQ); these mappings override any other mappings that may be defined for A through F. In ALPHA mode, all PC keyboard keys that normally generate printable ASCII characters, enter those characters into the ALPHA register (or to the command argument, if a command with an alphanumeric argument is being entered). These mappings override any other mappings that may be defined for those keys. What's the deal with the "Decimal" and "Binary" (free42dec/free42bin)? Starting with release 1.4, Free42 comes in decimal and binary versions. The two look and behave identically; the only difference is the way they represent numbers internally. Free42 Decimal (free42dec) uses Hugh Steers' 7-digit base-10000 BCD20 library, which effectively gives 25 decimal digits of precision, with exponents ranging from -10000 to +9999. Each number consumes 16 bytes of memory. Free42 Binary (free42bin) uses the PC's FPU; it represents numbers as IEEE-754 compatible double precision binary floating point, which consumes 8 bytes per number, and gives an effective precision of nearly 16 decimal digits, with exponents ranging from -308 to +307 (actually, exponents can be less than -308; such small numbers are "denormalized" and don't have the full precision of "normalized" numbers). The binary version has the advantage of being much faster than the decimal version; also, it uses less memory. However, numbers such as 0.1 (one-tenth) cannot be represented exactly in binary, since they are repeating fractions then. This inexactness can cause some HP-42S programs to fail. If you understand the issues surrounding binary floating point, and you do not rely on legacy software that may depend on the exactness of decimal fractions, you may use Free42 Binary and enjoy its speed advantage. If, on the other hand, you need full HP-42S compatibility, you should use Free42 Decimal. If you don't fully understand the above, it is best to play safe and use Free42 Decimal (free42dec). Free42 is (C) 2004-2013, by Thomas Okken BCD support (C) 2005-2009, by Hugh Steers / voidware Contact the author at thomas_okken@yahoo.com Look for updates, and versions for other operating systems, at http://thomasokken.com/free42/ free42-nologo-1.4.77/gtk/shell_main.cc000644 000765 000024 00000214167 12110237246 020052 0ustar00thomasstaff000000 000000 /////////////////////////////////////////////////////////////////////////////// // Free42 -- an HP-42S calculator simulator // Copyright (C) 2004-2013 Thomas Okken // // 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, see http://www.gnu.org/licenses/. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "shell.h" #include "shell_main.h" #include "shell_skin.h" #include "shell_spool.h" #include "core_main.h" #include "core_display.h" #include "icon.xpm" #ifndef _POSIX_HOST_NAME_MAX #define _POSIX_HOST_NAME_MAX 255 #endif #ifndef _POSIX_LOGIN_NAME_MAX #define _POSIX_LOGIN_NAME_MAX 255 #endif #ifdef AUDIO_ALSA #include "audio_alsa.h" #endif /* These are global because the skin code uses them a lot */ GtkWidget *calc_widget; bool allow_paint = false; state_type state; char free42dirname[FILENAMELEN]; /* PRINT_LINES is limited to an even lower value than in the Motif version. * It appears that GTK does not allow the SUM of a widget's height and its * container's height to exceed 32k, which means that as the print widget's * height approaches that magical number, the top-level window is forced to * become shorter and shorter. So, I set the limit at 30000 instead of * 32768; if we're reading a print-out file from Free42/Motif which has more * than 30000 pixels, we chop off the excess from the top. */ #define PRINT_LINES 30000 #define PRINT_BYTESPERLINE 36 #define PRINT_SIZE 1080000 static unsigned char *print_bitmap; static int printout_top; static int printout_bottom; static bool quit_flag = false; static int enqueued; /* Private globals */ static FILE *print_txt = NULL; static FILE *print_gif = NULL; static char print_gif_name[FILENAMELEN]; static int gif_seq = -1; static int gif_lines; static int pype[2]; static GtkWidget *mainwindow; static GtkWidget *printwindow; static GtkWidget *print_widget; static GdkGC *print_gc = NULL; static GtkAdjustment *print_adj; static char export_file_name[FILENAMELEN]; static FILE *export_file = NULL; static FILE *import_file = NULL; static GdkPixbuf *icon; static int ckey = 0; static int skey; static unsigned char *macro; static bool mouse_key; static guint16 active_keycode = 0; static bool just_pressed_shift = false; static guint timeout_id = 0; static guint timeout3_id = 0; static int keymap_length = 0; static keymap_entry *keymap = NULL; static guint reminder_id = 0; static FILE *statefile = NULL; static char statefilename[FILENAMELEN]; static char printfilename[FILENAMELEN]; static int ann_updown = 0; static int ann_shift = 0; static int ann_print = 0; static int ann_run = 0; static int ann_battery = 0; static int ann_g = 0; static int ann_rad = 0; /* Private functions */ static void read_key_map(const char *keymapfilename); static void init_shell_state(int4 version); static int read_shell_state(int4 *version); static int write_shell_state(); static void int_term_handler(int sig); static void usr1_handler(int sig); static gboolean gt_signal_handler(GIOChannel *source, GIOCondition condition, gpointer data); static void quit(); static void set_window_property(GtkWidget *window, const char *prop_name, char *props[], int num_props); static char *strclone(const char *s); static bool is_file(const char *name); static void show_message(char *title, char *message); static void no_mwm_resize_borders(GtkWidget *window); static void scroll_printout_to_bottom(); static void quitCB(); static void showPrintOutCB(); static void exportProgramCB(); static GtkWidget *make_file_select_dialog( const char *title, const char *pattern, bool save, GtkWidget *owner); static void importProgramCB(); static void clearPrintOutCB(); static void preferencesCB(); static void appendSuffix(char *path, char *suffix); static void copyCB(); static void pasteCB(); static void aboutCB(); static void delete_cb(GtkWidget *w, gpointer cd); static void delete_print_cb(GtkWidget *w, gpointer cd); static gboolean expose_cb(GtkWidget *w, GdkEventExpose *event, gpointer cd); static gboolean print_expose_cb(GtkWidget *w, GdkEventExpose *event, gpointer cd); static gboolean button_cb(GtkWidget *w, GdkEventButton *event, gpointer cd); static gboolean key_cb(GtkWidget *w, GdkEventKey *event, gpointer cd); static void enable_reminder(); static void disable_reminder(); static gboolean repeater(gpointer cd); static gboolean timeout1(gpointer cd); static gboolean timeout2(gpointer cd); static gboolean timeout3(gpointer cd); static gboolean battery_checker(gpointer cd); static void repaint_printout(int x, int y, int width, int height); static gboolean reminder(gpointer cd); static void txt_writer(const char *text, int length); static void txt_newliner(); static void gif_seeker(int4 pos); static void gif_writer(const char *text, int length); #ifdef BCD_MATH #define TITLE "Free42 Decimal" #else #define TITLE "Free42 Binary" #endif static GtkItemFactoryEntry entries[] = { { "/File", NULL, NULL, 0, "" }, { "/File/Show Print-Out", NULL, showPrintOutCB, 0, "" }, { "/File/sep1", NULL, NULL, 0, "" }, { "/File/Import Programs...", NULL, importProgramCB, 0, "" }, { "/File/Export Programs...", NULL, exportProgramCB, 0, "" }, { "/File/sep2", NULL, NULL, 0, "" }, { "/File/Clear Print-Out", NULL, clearPrintOutCB, 0, "" }, { "/File/Preferences...", NULL, preferencesCB, 0, "" }, { "/File/sep3", NULL, NULL, 0, "" }, { "/File/Quit", "Q", quitCB, 0, "" }, { "/Edit", NULL, NULL, 0, "" }, { "/Edit/Copy", "C", copyCB, 0, "" }, { "/Edit/Paste", "V", pasteCB, 0, "" }, { "/Skin", NULL, NULL, 0, "" }, { "/Help", NULL, NULL, 0, "" }, { "/Help/About Free42...", NULL, aboutCB, 0, "" } }; static gint num_entries = sizeof(entries) / sizeof(entries[0]); int main(int argc, char *argv[]) { #ifdef OLPC const char *activityID = NULL, *bundleID = NULL; #endif gtk_init(&argc, &argv); char *skin_arg = NULL; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-skin") == 0) skin_arg = ++i < argc ? argv[i] : NULL; #ifdef OLPC else if (strcmp(argv[i], "-a") == 0) activityID = ++i < argc ? argv[i] : NULL; else if (strcmp(argv[i], "-b") == 0) bundleID = ++i < argc ? argv[i] : NULL; #endif else { fprintf(stderr, "Unrecognized option: %s\n", argv[i]); exit(1); } } if (argc >= 3 && strcmp(argv[1], "-skin") == 0) skin_arg = argv[2]; /*********************************************************/ /***** Ignore SIGUSR1 until we're ready to handle it *****/ /*********************************************************/ struct sigaction act; act.sa_handler = SIG_IGN; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGUSR1); act.sa_flags = 0; sigaction(SIGUSR1, &act, NULL); /*****************************************************/ /***** Try to create the $HOME/.free42 directory *****/ /*****************************************************/ char keymapfilename[FILENAMELEN]; #ifdef OLPC char *sar = getenv("SUGAR_ACTIVITY_ROOT"); snprintf(free42dirname, FILENAMELEN, "%s/data", sar); snprintf(statefilename, FILENAMELEN, "%s/data/state", sar); snprintf(printfilename, FILENAMELEN, "%s/data/print", sar); snprintf(keymapfilename, FILENAMELEN, "%s/data/keymap", sar); #else bool free42dir_exists = false; char *home = getenv("HOME"); snprintf(free42dirname, FILENAMELEN, "%s/.free42", home); struct stat st; if (stat(free42dirname, &st) == -1 || !S_ISDIR(st.st_mode)) { mkdir(free42dirname, 0755); if (stat(free42dirname, &st) == 0 && S_ISDIR(st.st_mode)) { char oldpath[FILENAMELEN], newpath[FILENAMELEN]; free42dir_exists = true; /* Note that we only rename the .free42rc and .free42print files * if we have just created the .free42 directory; if the user * creates the .free42 directory manually, they also have to take * responsibility for the old-style state and print files; I don't * want to do any second-guessing here. */ snprintf(oldpath, FILENAMELEN, "%s/.free42rc", home); snprintf(newpath, FILENAMELEN, "%s/.free42/state", home); rename(oldpath, newpath); snprintf(oldpath, FILENAMELEN, "%s/.free42print", home); snprintf(newpath, FILENAMELEN, "%s/.free42/print", home); rename(oldpath, newpath); snprintf(oldpath, FILENAMELEN, "%s/.free42keymap", home); snprintf(newpath, FILENAMELEN, "%s/.free42/keymap", home); rename(oldpath, newpath); } } else free42dir_exists = true; if (free42dir_exists) { snprintf(statefilename, FILENAMELEN, "%s/.free42/state", home); snprintf(printfilename, FILENAMELEN, "%s/.free42/print", home); snprintf(keymapfilename, FILENAMELEN, "%s/.free42/keymap", home); } else { snprintf(statefilename, FILENAMELEN, "%s/.free42rc", home); snprintf(printfilename, FILENAMELEN, "%s/.free42print", home); snprintf(keymapfilename, FILENAMELEN, "%s/.free42keymap", home); } #endif /****************************/ /***** Read the key map *****/ /****************************/ read_key_map(keymapfilename); /***********************************************************/ /***** Open the state file and read the shell settings *****/ /***********************************************************/ int4 version; int init_mode; statefile = fopen(statefilename, "r"); if (statefile != NULL) { if (read_shell_state(&version)) { if (skin_arg != NULL) { strncpy(state.skinName, skin_arg, FILENAMELEN - 1); state.skinName[FILENAMELEN - 1] = 0; } init_mode = 1; } else { init_shell_state(-1); init_mode = 2; } } else { init_shell_state(-1); init_mode = 0; } /*******************************************************/ /***** Enforce single-instance mode, if applicable *****/ /*******************************************************/ #if !defined(OLPC) char *appid; if (state.singleInstance) { int appid_len = _POSIX_HOST_NAME_MAX + _POSIX_LOGIN_NAME_MAX + 2; appid = (char *) malloc(appid_len); gethostname(appid, appid_len); strcat(appid, "|"); strcat(appid, getpwuid(getuid())->pw_name); Display *display = GDK_DISPLAY(); Atom FREE42_HOST_AND_USER = XInternAtom(display, "FREE42_HOST_AND_USER", False); //XGrabServer(display); Window root; Window parent; Window *children; unsigned int nchildren; if (XQueryTree(display, GDK_ROOT_WINDOW(), &root, &parent, &children, &nchildren) != 0) { for (unsigned int i = 0; i < nchildren; i++) { Window win = children[i]; Window cwin = XmuClientWindow(display, win); XTextProperty prop; XGetTextProperty(display, cwin, &prop, FREE42_HOST_AND_USER); if (prop.value != NULL) { char **list; int nitems; XTextPropertyToStringList(&prop, &list, &nitems); if (nitems > 0 && strcmp(list[0], appid) == 0) { pid_t pid; if (sscanf(list[1], "%d", &pid) == 1) { kill(pid, SIGUSR1); //XUngrabServer(display); return 0; } } if (list != NULL) XFree(list); XFree(prop.value); } } } if (children != NULL) XFree(children); } #endif /*********************************/ /***** Build the main window *****/ /*********************************/ icon = gdk_pixbuf_new_from_xpm_data((const char **) icon_xpm); mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_icon(GTK_WINDOW(mainwindow), icon); gtk_window_set_title(GTK_WINDOW(mainwindow), TITLE); gtk_window_set_role(GTK_WINDOW(mainwindow), "Free42 Calculator"); gtk_window_set_resizable(GTK_WINDOW(mainwindow), FALSE); no_mwm_resize_borders(mainwindow); g_signal_connect(G_OBJECT(mainwindow), "delete_event", G_CALLBACK(delete_cb), NULL); if (state.mainWindowKnown) gtk_window_move(GTK_WINDOW(mainwindow), state.mainWindowX, state.mainWindowY); GtkAccelGroup *acc_grp = gtk_accel_group_new(); GtkItemFactory *fac = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "

", acc_grp); gtk_item_factory_create_items(fac, num_entries, entries, NULL); gtk_window_add_accel_group(GTK_WINDOW(mainwindow), acc_grp); GtkWidget *menubar = gtk_item_factory_get_widget(fac, "
"); // The "Skin" menu is dynamic; we don't populate any items in it here. // Instead, we attach a callback which scans the .free42 directory for // available skins; this callback is invoked when the menu is about to // be mapped. GList *children = gtk_container_get_children(GTK_CONTAINER(menubar)); GtkMenuItem *skin_btn = (GtkMenuItem *) children->next->next->data; g_list_free(children); g_signal_connect(G_OBJECT(skin_btn), "activate", G_CALLBACK(skin_menu_update), NULL); GtkWidget *box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(mainwindow), box); gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0); /****************************************/ /* Drawing area for the calculator skin */ /****************************************/ int win_width, win_height; skin_load(&win_width, &win_height); GtkWidget *w = gtk_drawing_area_new(); gtk_widget_set_size_request(w, win_width, win_height); gtk_box_pack_start(GTK_BOX(box), w, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(w), "expose_event", G_CALLBACK(expose_cb), NULL); gtk_widget_add_events(w, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect(G_OBJECT(w), "button-press-event", G_CALLBACK(button_cb), NULL); g_signal_connect(G_OBJECT(w), "button-release-event", G_CALLBACK(button_cb), NULL); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS); g_signal_connect(G_OBJECT(w), "key-press-event", G_CALLBACK(key_cb), NULL); g_signal_connect(G_OBJECT(w), "key-release-event", G_CALLBACK(key_cb), NULL); calc_widget = w; #ifdef OLPC { char *list[1]; if (activityID != NULL) { list[0] = (char *) activityID; set_window_property(mainwindow, "_SUGAR_ACTIVITY_ID", list, 1); } if (bundleID != NULL) { list[0] = (char *) bundleID; set_window_property(mainwindow, "_SUGAR_BUNDLE_ID", list, 1); } } #else if (state.singleInstance) { char *list[2]; char pidstr[11]; sprintf(pidstr, "%u", getpid()); list[0] = appid; list[1] = pidstr; set_window_property(mainwindow, "FREE42_HOST_AND_USER", list, 2); free(appid); //XUngrabServer(GDK_DISPLAY()); } #endif /**************************************/ /***** Build the print-out window *****/ /**************************************/ // In the Motif version, I create an XImage and read the bitmap data into // it; in the GTK version, that approach is not practical, since pixbuf // only comes in 24-bit and 32-bit flavors -- which would mean wasting // 25 megabytes for a 286x32768 pixbuf. So, instead, I use a 1 bpp buffer, // and simply create pixbufs on the fly whenever I have to repaint. print_bitmap = (unsigned char *) malloc(PRINT_SIZE); // TODO - handle memory allocation failure FILE *printfile = fopen(printfilename, "r"); if (printfile != NULL) { int n = fread(&printout_bottom, 1, sizeof(int), printfile); if (n == sizeof(int)) { if (printout_bottom > PRINT_LINES) { int excess = (printout_bottom - PRINT_LINES) * PRINT_BYTESPERLINE; fseek(printfile, excess, SEEK_CUR); printout_bottom = PRINT_LINES; } int bytes = printout_bottom * PRINT_BYTESPERLINE; n = fread(print_bitmap, 1, bytes, printfile); if (n != bytes) printout_bottom = 0; } else printout_bottom = 0; fclose(printfile); } else printout_bottom = 0; printout_top = 0; for (int n = printout_bottom * PRINT_BYTESPERLINE; n < PRINT_SIZE; n++) print_bitmap[n] = 0; printwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_icon(GTK_WINDOW(printwindow), icon); gtk_window_set_title(GTK_WINDOW(printwindow), "Free42 Print-Out"); gtk_window_set_role(GTK_WINDOW(printwindow), "Free42 Print-Out"); g_signal_connect(G_OBJECT(printwindow), "delete_event", G_CALLBACK(delete_print_cb), NULL); GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_container_add(GTK_CONTAINER(printwindow), scroll); GtkWidget *view = gtk_viewport_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(scroll), view); print_widget = gtk_drawing_area_new(); gtk_widget_set_size_request(print_widget, 286, printout_bottom); gtk_container_add(GTK_CONTAINER(view), print_widget); print_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll)); g_signal_connect(G_OBJECT(print_widget), "expose_event", G_CALLBACK(print_expose_cb), NULL); gtk_widget_show(print_widget); gtk_widget_show(view); gtk_widget_show(scroll); GdkGeometry geom; geom.min_width = 286; geom.max_width = 286; geom.min_height = 1; geom.max_height = 32767; gtk_window_set_geometry_hints(GTK_WINDOW(printwindow), print_widget, &geom, GdkWindowHints(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); if (state.printWindowKnown) gtk_window_move(GTK_WINDOW(printwindow), state.printWindowX, state.printWindowY); gint width, height; gtk_window_get_size(GTK_WINDOW(printwindow), &width, &height); gtk_window_resize(GTK_WINDOW(printwindow), width, state.printWindowKnown ? state.printWindowHeight : 600); gtk_widget_realize(printwindow); gtk_widget_realize(print_widget); scroll_printout_to_bottom(); #ifdef OLPC { char *list[1]; if (activityID != NULL) { list[0] = (char *) activityID; set_window_property(printwindow, "_SUGAR_ACTIVITY_ID", list, 1); } if (bundleID != NULL) { list[0] = (char *) bundleID; set_window_property(printwindow, "_SUGAR_BUNDLE_ID", list, 1); } } #endif /*************************************************/ /***** Show main window & start the emulator *****/ /*************************************************/ if (state.printWindowKnown && state.printWindowMapped) gtk_widget_show(printwindow); gtk_widget_show_all(mainwindow); gtk_widget_show(mainwindow); core_init(init_mode, version); if (statefile != NULL) { fclose(statefile); statefile = NULL; } if (core_powercycle()) enable_reminder(); /* Check if /proc/apm exists and is readable, and if so, * start the battery checker "thread" that keeps the battery * annunciator on the calculator display up to date. */ FILE *apm = fopen("/proc/apm", "r"); if (apm != NULL) { fclose(apm); shell_low_battery(); g_timeout_add(60000, battery_checker, NULL); } if (pipe(pype) != 0) fprintf(stderr, "Could not create pipe for signal handler; not catching signals.\n"); else { GIOChannel *channel = g_io_channel_unix_new(pype[0]); GError *err = NULL; g_io_channel_set_encoding(channel, NULL, &err); g_io_channel_set_flags(channel, (GIOFlags) (g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK), &err); g_io_add_watch(channel, G_IO_IN, gt_signal_handler, NULL); act.sa_handler = int_term_handler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGINT); sigaddset(&act.sa_mask, SIGTERM); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); act.sa_handler = usr1_handler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGUSR1); act.sa_flags = 0; sigaction(SIGUSR1, &act, NULL); } gtk_main(); return 0; } keymap_entry *parse_keymap_entry(char *line, int lineno) { char *p; static keymap_entry entry; p = strchr(line, '#'); if (p != NULL) *p = 0; p = strchr(line, '\n'); if (p != NULL) *p = 0; p = strchr(line, '\r'); if (p != NULL) *p = 0; p = strchr(line, ':'); if (p != NULL) { char *val = p + 1; char *tok; bool ctrl = false; bool alt = false; bool shift = false; bool cshift = false; guint keyval = GDK_VoidSymbol; bool done = false; unsigned char macrobuf[KEYMAP_MAX_MACRO_LENGTH + 1]; int macrolen = 0; /* Parse keysym */ *p = 0; tok = strtok(line, " \t"); while (tok != NULL) { if (done) { fprintf(stderr, "Keymap, line %d: Excess tokens in key spec.\n", lineno); return NULL; } if (strcasecmp(tok, "ctrl") == 0) ctrl = true; else if (strcasecmp(tok, "alt") == 0) alt = true; else if (strcasecmp(tok, "shift") == 0) shift = true; else if (strcasecmp(tok, "cshift") == 0) cshift = true; else { keyval = gdk_keyval_from_name(tok); if (keyval == GDK_VoidSymbol) { fprintf(stderr, "Keymap, line %d: Unrecognized KeyName.\n", lineno); return NULL; } done = true; } tok = strtok(NULL, " \t"); } if (!done) { fprintf(stderr, "Keymap, line %d: Unrecognized KeyName.\n", lineno); return NULL; } /* Parse macro */ tok = strtok(val, " \t"); while (tok != NULL) { char *endptr; long k = strtol(tok, &endptr, 10); if (*endptr != 0 || k < 1 || k > 255) { fprintf(stderr, "Keymap, line %d: Bad value (%s) in macro.\n", lineno, tok); return NULL; } else if (macrolen == KEYMAP_MAX_MACRO_LENGTH) { fprintf(stderr, "Keymap, line %d: Macro too long (max=%d).\n", lineno, KEYMAP_MAX_MACRO_LENGTH); return NULL; } else macrobuf[macrolen++] = k; tok = strtok(NULL, " \t"); } macrobuf[macrolen] = 0; entry.ctrl = ctrl; entry.alt = alt; entry.shift = shift; entry.cshift = cshift; entry.keyval = keyval; strcpy((char *) entry.macro, (const char *) macrobuf); return &entry; } else return NULL; } static void read_key_map(const char *keymapfilename) { FILE *keymapfile = fopen(keymapfilename, "r"); int kmcap = 0; char line[1024]; int lineno = 0; if (keymapfile == NULL) { /* Try to create default keymap file */ extern long keymap_filesize; extern const char keymap_filedata[]; long n; keymapfile = fopen(keymapfilename, "wb"); if (keymapfile == NULL) return; #ifdef OLPC chmod(keymapfilename, 0664); #endif n = fwrite(keymap_filedata, 1, keymap_filesize, keymapfile); if (n != keymap_filesize) { int err = errno; fprintf(stderr, "Error writing \"%s\": %s (%d)\n", keymapfilename, strerror(err), err); } fclose(keymapfile); keymapfile = fopen(keymapfilename, "r"); if (keymapfile == NULL) return; } while (fgets(line, 1024, keymapfile) != NULL) { keymap_entry *entry = parse_keymap_entry(line, ++lineno); if (entry == NULL) continue; /* Create new keymap entry */ if (keymap_length == kmcap) { kmcap += 50; keymap = (keymap_entry *) realloc(keymap, kmcap * sizeof(keymap_entry)); // TODO - handle memory allocation failure } memcpy(keymap + (keymap_length++), entry, sizeof(keymap_entry)); } fclose(keymapfile); } static void init_shell_state(int4 version) { switch (version) { case -1: state.extras = 0; /* fall through */ case 0: state.printerToTxtFile = 0; state.printerToGifFile = 0; state.printerTxtFileName[0] = 0; state.printerGifFileName[0] = 0; state.printerGifMaxLength = 256; /* fall through */ case 1: state.mainWindowKnown = 0; state.printWindowKnown = 0; /* fall through */ case 2: state.skinName[0] = 0; /* fall through */ case 3: state.singleInstance = 0; /* fall through */ case 4: /* current version (SHELL_VERSION = 4), * so nothing to do here since everything * was initialized from the state file. */ ; } } static int read_shell_state(int4 *ver) { int4 magic; int4 version; int4 state_size; int4 state_version; if (shell_read_saved_state(&magic, sizeof(int4)) != sizeof(int4)) return 0; if (magic != FREE42_MAGIC) return 0; if (shell_read_saved_state(&version, sizeof(int4)) != sizeof(int4)) return 0; if (version == 0) { /* State file version 0 does not contain shell state, * only core state, so we just hard-init the shell. */ init_shell_state(-1); *ver = version; return 1; } else if (version > FREE42_VERSION) /* Unknown state file version */ return 0; if (shell_read_saved_state(&state_size, sizeof(int4)) != sizeof(int4)) return 0; if (shell_read_saved_state(&state_version, sizeof(int4)) != sizeof(int4)) return 0; if (state_version < 0 || state_version > SHELL_VERSION) /* Unknown shell state version */ return 0; if (shell_read_saved_state(&state, state_size) != state_size) return 0; init_shell_state(state_version); *ver = version; return 1; } static int write_shell_state() { int4 magic = FREE42_MAGIC; int4 version = FREE42_VERSION; int4 state_size = sizeof(state_type); int4 state_version = SHELL_VERSION; if (!shell_write_saved_state(&magic, sizeof(int4))) return 0; if (!shell_write_saved_state(&version, sizeof(int4))) return 0; if (!shell_write_saved_state(&state_size, sizeof(int4))) return 0; if (!shell_write_saved_state(&state_version, sizeof(int4))) return 0; if (!shell_write_saved_state(&state, sizeof(state_type))) return 0; return 1; } static void int_term_handler(int sig) { write(pype[1], "1\n", 2); } static void usr1_handler(int sig) { write(pype[1], "2\n", 2); } static gboolean gt_signal_handler(GIOChannel *source, GIOCondition condition, gpointer data) { char buf[1]; if (read(pype[0], buf, 1) == 1) if (buf[0] == '1') quit(); else { //gtk_widget_show(mainwindow); gtk_window_present(GTK_WINDOW(mainwindow)); //gdk_window_deiconify(mainwindow->window); //gdk_window_raise(mainwindow->window); gdk_window_focus(mainwindow->window, GDK_CURRENT_TIME); } return TRUE; } static void quit() { FILE *printfile; int n, length; printfile = fopen(printfilename, "w"); if (printfile != NULL) { #ifdef OLPC chmod(printfilename, 0664); #endif length = printout_bottom - printout_top; if (length < 0) length += PRINT_LINES; n = fwrite(&length, 1, sizeof(int), printfile); if (n != sizeof(int)) goto failed; if (printout_bottom >= printout_top) { n = fwrite(print_bitmap + PRINT_BYTESPERLINE * printout_top, 1, PRINT_BYTESPERLINE * length, printfile); if (n != PRINT_BYTESPERLINE * length) goto failed; } else { n = fwrite(print_bitmap + PRINT_BYTESPERLINE * printout_top, 1, PRINT_SIZE - PRINT_BYTESPERLINE * printout_top, printfile); if (n != PRINT_SIZE - PRINT_BYTESPERLINE * printout_top) goto failed; n = fwrite(print_bitmap, 1, PRINT_BYTESPERLINE * printout_bottom, printfile); if (n != PRINT_BYTESPERLINE * printout_bottom) goto failed; } fclose(printfile); goto done; failed: fclose(printfile); remove(printfilename); done: ; } if (print_txt != NULL) fclose(print_txt); if (print_gif != NULL) { shell_finish_gif(gif_seeker, gif_writer); fclose(print_gif); } gint x, y; gtk_window_get_position(GTK_WINDOW(mainwindow), &x, &y); state.mainWindowKnown = 1; state.mainWindowX = x; state.mainWindowY = y; if (state.printWindowMapped) { gtk_window_get_position(GTK_WINDOW(printwindow), &x, &y); state.printWindowX = x; state.printWindowY = y; } if (state.printWindowKnown) { gtk_window_get_size(GTK_WINDOW(printwindow), &x, &y); state.printWindowHeight = y; } statefile = fopen(statefilename, "w"); if (statefile != NULL) { #ifdef OLPC chmod(statefilename, 0664); #endif write_shell_state(); } core_quit(); if (statefile != NULL) fclose(statefile); shell_spool_exit(); exit(0); } static void set_window_property(GtkWidget *window, const char *prop_name, char *props[], int num_props) { Display *display = GDK_DISPLAY(); gtk_widget_realize(window); XTextProperty prop; XStringListToTextProperty(props, num_props, &prop); Atom PROP = XInternAtom(display, prop_name, False); XSetTextProperty(display, GDK_WINDOW_XWINDOW(window->window), &prop, PROP); XFree(prop.value); } static char *strclone(const char *s) { char *s2 = (char *) malloc(strlen(s) + 1); if (s2 != NULL) strcpy(s2, s); return s2; } static bool is_file(const char *name) { struct stat st; if (stat(name, &st) == -1) return false; return S_ISREG(st.st_mode); } static void show_message(char *title, char *message) { GtkWidget *msg = gtk_message_dialog_new(GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, message); gtk_window_set_title(GTK_WINDOW(msg), title); gtk_window_set_role(GTK_WINDOW(msg), "Free42 Dialog"); gtk_dialog_run(GTK_DIALOG(msg)); gtk_widget_destroy(msg); } static void no_mwm_resize_helper(GtkWidget *w, gpointer cd) { gdk_window_set_decorations(w->window, GdkWMDecoration(GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MAXIMIZE)); gdk_window_set_functions(w->window, GdkWMFunction(GDK_FUNC_ALL | GDK_FUNC_RESIZE | GDK_FUNC_MAXIMIZE)); } static void no_mwm_resize_borders(GtkWidget *window) { // gtk_window_set_resizable(w, FALSE) only sets the WM size hints, so that // the minimum and maximum sizes coincide with the actual size. While this // certainly has the desired effect of making the window non-resizable, it // does not deal with the way mwm visually distinguishes resizable and // non-resizable windows; the result is that, when running under mwm, you // have a window with resize borders and a maximize button, none of which // actually let you resize the window. // So, we use an additional GDK call to set the appropriate mwm properties. g_signal_connect(G_OBJECT(window), "realize", G_CALLBACK(no_mwm_resize_helper), NULL); } static void scroll_printout_to_bottom() { gtk_adjustment_set_value(print_adj, print_adj->upper - print_adj->page_size); } static void quitCB() { quit(); } static void showPrintOutCB() { //gtk_widget_show(printwindow); gtk_window_present(GTK_WINDOW(printwindow)); gdk_window_focus(printwindow->window, GDK_CURRENT_TIME); state.printWindowKnown = 1; state.printWindowMapped = 1; } static void exportProgramCB() { static GtkWidget *sel_dialog = NULL; static GtkTreeView *tree; static GtkTreeSelection *select; if (sel_dialog == NULL) { sel_dialog = gtk_dialog_new_with_buttons( "Export Programs", GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); gtk_window_set_resizable(GTK_WINDOW(sel_dialog), FALSE); no_mwm_resize_borders(sel_dialog); GtkWidget *container = gtk_bin_get_child(GTK_BIN(sel_dialog)); GtkWidget *box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(container), box); GtkWidget *label = gtk_label_new("Select Programs to Export:"); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 10); GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); tree = (GtkTreeView *) gtk_tree_view_new(); select = gtk_tree_view_get_selection(tree); gtk_tree_selection_set_mode(select, GTK_SELECTION_MULTIPLE); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); //gtk_cell_renderer_text_set_fixed_height_from_font((GtkCellRendererText *) renderer, 12); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("Foo", renderer, "text", 0, NULL); gtk_tree_view_append_column(tree, column); gtk_tree_view_set_headers_visible(tree, FALSE); gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(tree)); gtk_widget_set_size_request(scroll, -1, 200); gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(scroll), FALSE, FALSE, 10); gtk_widget_show_all(GTK_WIDGET(sel_dialog)); } char buf[10000]; int count = core_list_programs(buf, 10000); char *p = buf; GtkListStore *model = gtk_list_store_new(1, G_TYPE_STRING); GtkTreeIter iter; while (count-- > 0) { gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, 0, p, -1); p += strlen(p) + 1; } gtk_tree_view_set_model(tree, GTK_TREE_MODEL(model)); // TODO: does this leak list-stores? Or is everything taken case of by the // GObject reference-counting stuff? gtk_window_set_role(GTK_WINDOW(sel_dialog), "Free42 Dialog"); bool cancelled = gtk_dialog_run(GTK_DIALOG(sel_dialog)) != GTK_RESPONSE_ACCEPT; gtk_widget_hide(sel_dialog); if (cancelled) return; count = gtk_tree_selection_count_selected_rows(select); if (count == 0) return; static GtkWidget *save_dialog = NULL; if (save_dialog == NULL) save_dialog = make_file_select_dialog("Export Programs", "Program Files (*.raw)\0*.[Rr][Aa][Ww]\0All Files (*.*)\0*\0", true, mainwindow); char *filename = NULL; gtk_window_set_role(GTK_WINDOW(save_dialog), "Free42 Dialog"); if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_dialog)); gtk_widget_hide(GTK_WIDGET(save_dialog)); if (filename == NULL) return; strcpy(export_file_name, filename); g_free(filename); if (strncmp(gtk_file_filter_get_name( gtk_file_chooser_get_filter( GTK_FILE_CHOOSER(save_dialog))), "All", 3) != 0) appendSuffix(export_file_name, ".raw"); if (is_file(export_file_name)) { char buf[1000]; snprintf(buf, 1000, "Replace existing \"%s\"?", export_file_name); GtkWidget *msg = gtk_message_dialog_new(GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, buf); gtk_window_set_title(GTK_WINDOW(msg), "Replace?"); gtk_window_set_role(GTK_WINDOW(msg), "Free42 Dialog"); cancelled = gtk_dialog_run(GTK_DIALOG(msg)) != GTK_RESPONSE_YES; gtk_widget_destroy(msg); if (cancelled) return; } export_file = fopen(export_file_name, "w"); if (export_file == NULL) { char buf[1000]; int err = errno; snprintf(buf, 1000, "Could not open \"%s\" for writing:\n%s (%d)", export_file_name, strerror(err), err); show_message("Message", buf); } else { #ifdef OLPC chmod(export_file_name, 0664); #endif int *p2 = (int *) malloc(count * sizeof(int)); // TODO - handle memory allocation failure GList *rows = gtk_tree_selection_get_selected_rows(select, NULL); GList *item = rows; int i = 0; while (item != NULL) { GtkTreePath *path = (GtkTreePath *) item->data; char *pathstring = gtk_tree_path_to_string(path); sscanf(pathstring, "%d", p2 + i); item = item->next; i++; } g_list_free(rows); core_export_programs(count, p2, NULL); free(p2); if (export_file != NULL) { fclose(export_file); export_file = NULL; } } } static GtkWidget *make_file_select_dialog(const char *title, const char *pattern, bool save, GtkWidget *owner) { GtkWidget *dialog = gtk_file_chooser_dialog_new( title, GTK_WINDOW(owner), save ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); const char *p = pattern; while (1) { const char *descr, *ext; int n = strlen(p); if (n == 0) break; descr = p; p += n + 1; n = strlen(p); if (n == 0) break; ext = p; p += n + 1; GtkFileFilter *filter = gtk_file_filter_new(); gtk_file_filter_add_pattern(filter, ext); gtk_file_filter_set_name(filter, descr); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); } return dialog; } static void importProgramCB() { static GtkWidget *dialog = NULL; if (dialog == NULL) dialog = make_file_select_dialog("Import Programs", "Program Files (*.raw)\0*.[Rr][Aa][Ww]\0All Files (*.*)\0*\0", false, mainwindow); gtk_window_set_role(GTK_WINDOW(dialog), "Free42 Dialog"); bool cancelled = gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT; gtk_widget_hide(dialog); if (cancelled) return; char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); if (filename == NULL) { import_file = NULL; return; } char filenamebuf[FILENAMELEN]; strncpy(filenamebuf, filename, FILENAMELEN); filenamebuf[FILENAMELEN - 1] = 0; g_free(filename); if (strncmp(gtk_file_filter_get_name( gtk_file_chooser_get_filter( GTK_FILE_CHOOSER(dialog))), "All", 3) != 0) appendSuffix(filenamebuf, ".raw"); import_file = fopen(filenamebuf, "r"); if (import_file == NULL) { char buf[1000]; int err = errno; snprintf(buf, 1000, "Could not open \"%s\" for reading:\n%s (%d)", filenamebuf, strerror(err), err); show_message("Message", buf); } else { core_import_programs(NULL); redisplay(); if (import_file != NULL) { fclose(import_file); import_file = NULL; } } } static void clearPrintOutCB() { printout_top = 0; printout_bottom = 0; gtk_widget_set_size_request(print_widget, 286, 1); if (print_gif != NULL) { shell_finish_gif(gif_seeker, gif_writer); fclose(print_gif); print_gif = NULL; } } struct browse_file_info { const char *title; const char *patterns; GtkWidget *textfield; browse_file_info(const char *t, const char *p, GtkWidget *tf) : title(t), patterns(p), textfield(tf) {} }; static void browse_file(GtkButton *button, gpointer cd) { browse_file_info *info = (browse_file_info *) cd; GtkWidget *dialog = gtk_widget_get_toplevel(GTK_WIDGET(button)); GtkWidget *save_dialog = make_file_select_dialog( info->title, info->patterns, true, dialog); const char *filename = gtk_entry_get_text(GTK_ENTRY(info->textfield)); if (filename[0] == '/') gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(save_dialog), filename); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), filename); gtk_window_set_role(GTK_WINDOW(save_dialog), "Free42 Dialog"); if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_dialog)); char filenamebuf[FILENAMELEN]; strncpy(filenamebuf, filename, FILENAMELEN); filenamebuf[FILENAMELEN - 1] = 0; const gchar *filtername = gtk_file_filter_get_name( gtk_file_chooser_get_filter( GTK_FILE_CHOOSER(save_dialog))); if (strncmp(filtername, "Text", 4) == 0) appendSuffix(filenamebuf, ".txt"); else if (strncmp(filtername, "GIF", 3) == 0) appendSuffix(filenamebuf, ".gif"); gtk_entry_set_text(GTK_ENTRY(info->textfield), filenamebuf); } gtk_widget_destroy(GTK_WIDGET(save_dialog)); } static void preferencesCB() { static GtkWidget *dialog = NULL; static GtkWidget *singularmatrix; static GtkWidget *matrixoutofrange; static GtkWidget *autorepeat; static GtkWidget *singleinstance; static GtkWidget *printtotext; static GtkWidget *textpath; static GtkWidget *rawtext; static GtkWidget *printtogif; static GtkWidget *gifpath; static GtkWidget *gifheight; if (dialog == NULL) { dialog = gtk_dialog_new_with_buttons( "Preferences", GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); no_mwm_resize_borders(dialog); GtkWidget *container = gtk_bin_get_child(GTK_BIN(dialog)); GtkWidget *table = gtk_table_new(6, 4, FALSE); gtk_container_add(GTK_CONTAINER(container), table); singularmatrix = gtk_check_button_new_with_label("Inverting or solving a singular matrix yields \"Singular Matrix\" error"); gtk_table_attach(GTK_TABLE(table), singularmatrix, 0, 4, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); matrixoutofrange = gtk_check_button_new_with_label("Overflows during matrix operations yield \"Out of Range\" error"); gtk_table_attach(GTK_TABLE(table), matrixoutofrange, 0, 4, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); autorepeat = gtk_check_button_new_with_label("Auto-Repeat for number entry and ALPHA mode"); gtk_table_attach(GTK_TABLE(table), autorepeat, 0, 4, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); singleinstance = gtk_check_button_new_with_label("Single instance"); gtk_table_attach(GTK_TABLE(table), singleinstance, 0, 4, 3, 4, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); printtotext = gtk_check_button_new_with_label("Print to text file:"); gtk_table_attach(GTK_TABLE(table), printtotext, 0, 1, 4, 5, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), (GtkAttachOptions) 0, 3, 3); textpath = gtk_entry_new(); gtk_table_attach(GTK_TABLE(table), textpath, 1, 3, 4, 5, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); GtkWidget *browse1 = gtk_button_new_with_label("Browse..."); gtk_table_attach(GTK_TABLE(table), browse1, 3, 4, 4, 5, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), (GtkAttachOptions) 0, 3, 3); rawtext = gtk_check_button_new_with_label("Raw text"); gtk_table_attach(GTK_TABLE(table), rawtext, 1, 3, 5, 6, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), (GtkAttachOptions) 0, 3, 3); printtogif = gtk_check_button_new_with_label("Print to GIF file:"); gtk_table_attach(GTK_TABLE(table), printtogif, 0, 1, 6, 7, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), (GtkAttachOptions) 0, 3, 3); gifpath = gtk_entry_new(); gtk_table_attach(GTK_TABLE(table), gifpath, 1, 3, 6, 7, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); GtkWidget *browse2 = gtk_button_new_with_label("Browse..."); gtk_table_attach(GTK_TABLE(table), browse2, 3, 4, 6, 7, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), (GtkAttachOptions) 0, 3, 3); GtkWidget *label = gtk_label_new("Maximum GIF height (pixels):"); gtk_table_attach(GTK_TABLE(table), label, 1, 2, 7, 8, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) 0, 3, 3); gifheight = gtk_entry_new_with_max_length(5); gtk_table_attach(GTK_TABLE(table), gifheight, 2, 3, 7, 8, (GtkAttachOptions) (GTK_SHRINK), (GtkAttachOptions) 0, 3, 3); g_signal_connect(G_OBJECT(browse1), "clicked", G_CALLBACK(browse_file), (gpointer) new browse_file_info("Select Text File Name", "Text (*.txt)\0*.[Tt][Xx][Tt]\0All Files (*.*)\0*\0", textpath)); g_signal_connect(G_OBJECT(browse2), "clicked", G_CALLBACK(browse_file), (gpointer) new browse_file_info("Select GIF File Name", "GIF (*.gif)\0*.[Gg][Ii][Ff]\0All Files (*.*)\0*\0", gifpath)); gtk_widget_show_all(GTK_WIDGET(dialog)); } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(singularmatrix), core_settings.matrix_singularmatrix); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matrixoutofrange), core_settings.matrix_outofrange); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autorepeat), core_settings.auto_repeat); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(singleinstance), state.singleInstance); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(printtotext), state.printerToTxtFile); gtk_entry_set_text(GTK_ENTRY(textpath), state.printerTxtFileName); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rawtext), core_settings.raw_text); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(printtogif), state.printerToGifFile); gtk_entry_set_text(GTK_ENTRY(gifpath), state.printerGifFileName); char maxlen[6]; snprintf(maxlen, 6, "%d", state.printerGifMaxLength); gtk_entry_set_text(GTK_ENTRY(gifheight), maxlen); gtk_window_set_role(GTK_WINDOW(dialog), "Free42 Dialog"); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { core_settings.matrix_singularmatrix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(singularmatrix)); core_settings.matrix_outofrange = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matrixoutofrange)); core_settings.auto_repeat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autorepeat)); state.singleInstance = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(singleinstance)); core_settings.raw_text = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rawtext)); state.printerToTxtFile = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(printtotext)); char *old = strclone(state.printerTxtFileName); const char *s = gtk_entry_get_text(GTK_ENTRY(textpath)); strncpy(state.printerTxtFileName, s, FILENAMELEN); state.printerTxtFileName[FILENAMELEN - 1] = 0; appendSuffix(state.printerTxtFileName, ".txt"); if (print_txt != NULL && (!state.printerToTxtFile || strcmp(state.printerTxtFileName, old) != 0)) { fclose(print_txt); print_txt = NULL; } free(old); state.printerToGifFile = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(printtogif)); old = strclone(state.printerGifFileName); s = gtk_entry_get_text(GTK_ENTRY(gifpath)); strncpy(state.printerGifFileName, s, FILENAMELEN); state.printerGifFileName[FILENAMELEN - 1] = 0; appendSuffix(state.printerGifFileName, ".gif"); if (print_gif != NULL && (!state.printerToGifFile || strcmp(state.printerGifFileName, old) != 0)) { shell_finish_gif(gif_seeker, gif_writer); fclose(print_gif); print_gif = NULL; } free(old); s = gtk_entry_get_text(GTK_ENTRY(gifheight)); if (sscanf(s, "%d", &state.printerGifMaxLength) == 1) { if (state.printerGifMaxLength < 32) state.printerGifMaxLength = 32; else if (state.printerGifMaxLength > 32767) state.printerGifMaxLength = 32767; } else state.printerGifMaxLength = 256; } gtk_widget_hide(GTK_WIDGET(dialog)); } static void appendSuffix(char *path, char *suffix) { int len = strlen(path); int slen = strlen(suffix); if (len == 0 || len >= FILENAMELEN - slen) return; if (len >= slen && strcasecmp(path + len - slen, suffix) != 0) strcat(path, suffix); } static void copyCB() { char buf[100]; core_copy(buf, 100); GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clip, buf, -1); clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY); gtk_clipboard_set_text(clip, buf, -1); } static void paste2(GtkClipboard *clip, const gchar *text, gpointer cd) { if (text != NULL) { core_paste(text); redisplay(); } } static void pasteCB() { #ifdef GDK_WINDOWING_X11 GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY); #else GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); #endif gtk_clipboard_request_text(clip, paste2, NULL); } static bool focus_ok_button(GtkWindow *window, GtkWidget *container) { GList *children = gtk_container_get_children(GTK_CONTAINER(container)); if (children == NULL) return false; GList *child = children; while (child != NULL) { GtkWidget *w = (GtkWidget *) child->data; if (GTK_IS_BUTTON(w) && !GTK_IS_LINK_BUTTON(w)) { gtk_window_set_focus(window, w); g_list_free(children); return true; } else if (GTK_IS_CONTAINER(w)) { if (focus_ok_button(window, w)) { g_list_free(children); return true; } } child = child->next; } g_list_free(children); return false; } static void aboutCB() { static GtkWidget *about = NULL; if (about == NULL) { about = gtk_dialog_new_with_buttons( "About Free42", GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_resizable(GTK_WINDOW(about), FALSE); no_mwm_resize_borders(about); GtkWidget *container = gtk_bin_get_child(GTK_BIN(about)); GtkWidget *box = gtk_hbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(container), box); GtkWidget *image = gtk_image_new_from_pixbuf(icon); gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 10); GtkWidget *box2 = gtk_vbox_new(FALSE, 0); GtkWidget *version = gtk_label_new("Free42 " VERSION); gtk_misc_set_alignment(GTK_MISC(version), 0, 0); gtk_box_pack_start(GTK_BOX(box2), version, FALSE, FALSE, 10); GtkWidget *author = gtk_label_new("(C) 2004-2013 Thomas Okken"); gtk_misc_set_alignment(GTK_MISC(author), 0, 0); gtk_box_pack_start(GTK_BOX(box2), author, FALSE, FALSE, 0); GtkWidget *websitelink = gtk_link_button_new("http://thomasokken.com/free42/"); GtkWidget *websitebox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(websitebox), websitelink, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box2), websitebox, FALSE, FALSE, 0); GtkWidget *forumlink = gtk_link_button_new("http://groups.google.com/group/free42discuss"); GtkWidget *forumbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(forumbox), forumlink, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box2), forumbox, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), box2, FALSE, FALSE, 0); focus_ok_button(GTK_WINDOW(about), container); gtk_widget_show_all(GTK_WIDGET(about)); } else { GtkWidget *container = gtk_bin_get_child(GTK_BIN(about)); focus_ok_button(GTK_WINDOW(about), container); } gtk_window_set_role(GTK_WINDOW(about), "Free42 Dialog"); gtk_dialog_run(GTK_DIALOG(about)); gtk_widget_hide(GTK_WIDGET(about)); } static void delete_cb(GtkWidget *w, gpointer cd) { quit(); } static void delete_print_cb(GtkWidget *w, gpointer cd) { gint x, y; gtk_window_get_position(GTK_WINDOW(printwindow), &x, &y); state.printWindowX = x; state.printWindowY = y; state.printWindowMapped = 0; state.printWindowKnown = 1; gtk_widget_hide(GTK_WIDGET(printwindow)); } static gboolean expose_cb(GtkWidget *w, GdkEventExpose *event, gpointer cd) { allow_paint = true; skin_repaint(); skin_repaint_display(); skin_repaint_annunciator(1, ann_updown); skin_repaint_annunciator(2, ann_shift); skin_repaint_annunciator(3, ann_print); skin_repaint_annunciator(4, ann_run); skin_repaint_annunciator(5, ann_battery); skin_repaint_annunciator(6, ann_g); skin_repaint_annunciator(7, ann_rad); if (ckey != 0) skin_repaint_key(skey, 1); return TRUE; } static gboolean print_expose_cb(GtkWidget *w, GdkEventExpose *event, gpointer cd) { repaint_printout(event->area.x, event->area.y, event->area.width, event->area.height); return TRUE; } static void shell_keydown() { int repeat, keep_running; if (skey == -1) skey = skin_find_skey(ckey); skin_repaint_key(skey, 1); if (timeout3_id != 0 && (macro != NULL || ckey != 28 /* KEY_SHIFT */)) { g_source_remove(timeout3_id); timeout3_id = 0; core_timeout3(0); } if (macro != NULL) { if (*macro == 0) { squeak(); return; } bool one_key_macro = macro[1] == 0 || (macro[2] == 0 && macro[0] == 28); if (!one_key_macro) skin_display_set_enabled(false); while (*macro != 0) { keep_running = core_keydown(*macro++, &enqueued, &repeat); if (*macro != 0 && !enqueued) core_keyup(); } if (!one_key_macro) { skin_display_set_enabled(true); skin_repaint_display(); skin_repaint_annunciator(1, ann_updown); skin_repaint_annunciator(2, ann_shift); skin_repaint_annunciator(3, ann_print); skin_repaint_annunciator(4, ann_run); skin_repaint_annunciator(5, ann_battery); skin_repaint_annunciator(6, ann_g); skin_repaint_annunciator(7, ann_rad); repeat = 0; } } else keep_running = core_keydown(ckey, &enqueued, &repeat); if (quit_flag) quit(); if (keep_running) enable_reminder(); else { disable_reminder(); if (timeout_id != 0) g_source_remove(timeout_id); if (repeat != 0) timeout_id = g_timeout_add(repeat == 1 ? 1000 : 500, repeater, NULL); else if (!enqueued) timeout_id = g_timeout_add(250, timeout1, NULL); } } static void shell_keyup() { skin_repaint_key(skey, 0); ckey = 0; skey = -1; if (timeout_id != 0) { g_source_remove(timeout_id); timeout_id = 0; } if (!enqueued) { int keep_running = core_keyup(); if (quit_flag) quit(); if (keep_running) enable_reminder(); else disable_reminder(); } } static gboolean button_cb(GtkWidget *w, GdkEventButton *event, gpointer cd) { if (event->type == GDK_BUTTON_PRESS) { if (ckey == 0) { int x = (int) event->x; int y = (int) event->y; skin_find_key(x, y, ann_shift != 0, &skey, &ckey); if (ckey != 0) { macro = skin_find_macro(ckey); shell_keydown(); mouse_key = true; } } } else if (event->type == GDK_BUTTON_RELEASE) { if (ckey != 0 && mouse_key) shell_keyup(); } return TRUE; } static gboolean key_cb(GtkWidget *w, GdkEventKey *event, gpointer cd) { if (event->type == GDK_KEY_PRESS) { if (event->hardware_keycode == active_keycode) // Auto-repeat return TRUE; if (ckey == 0 || !mouse_key) { int i; bool printable = event->length == 1 && event->string[0] >= 32 && event->string[0] <= 126; just_pressed_shift = false; if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R) { just_pressed_shift = true; return TRUE; } bool ctrl = (event->state & GDK_CONTROL_MASK) != 0; bool alt = (event->state & GDK_MOD1_MASK) != 0; bool shift = (event->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK)) != 0; bool cshift = ann_shift != 0; if (ckey != 0) { shell_keyup(); active_keycode = 0; } bool exact; unsigned char *key_macro = skin_keymap_lookup(event->keyval, printable, ctrl, alt, shift, cshift, &exact); if (key_macro == NULL || !exact) { for (i = 0; i < keymap_length; i++) { keymap_entry *entry = keymap + i; if (ctrl == entry->ctrl && alt == entry->alt && (printable || shift == entry->shift) && event->keyval == entry->keyval) { if (cshift == entry->cshift) { key_macro = entry->macro; break; } else { if (key_macro == NULL) key_macro = entry->macro; } } } } if (key_macro == NULL || (key_macro[0] != 36 || key_macro[1] != 0) && (key_macro[0] != 28 || key_macro[1] != 36 || key_macro[2] != 0)) { // The test above is to make sure that whatever mapping is in // effect for R/S will never be overridden by the special cases // for the ALPHA and A..F menus. if (!ctrl && !alt) { char c = event->string[0]; if (printable && core_alpha_menu()) { if (c >= 'a' && c <= 'z') c = c + 'A' - 'a'; else if (c >= 'A' && c <= 'Z') c = c + 'a' - 'A'; ckey = 1024 + c; skey = -1; macro = NULL; shell_keydown(); mouse_key = false; active_keycode = event->hardware_keycode; return TRUE; } else if (core_hex_menu() && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { if (c >= 'a' && c <= 'f') ckey = c - 'a' + 1; else ckey = c - 'A' + 1; skey = -1; macro = NULL; shell_keydown(); mouse_key = false; active_keycode = event->hardware_keycode; return TRUE; } } } if (key_macro != NULL) { // A keymap entry is a sequence of zero or more calculator // keystrokes (1..37) and/or macros (38..255). We expand // macros here before invoking shell_keydown(). // If the keymap entry is one key, or two keys with the // first being 'shift', we highlight the key in question // by setting ckey; otherwise, we set ckey to -10, which // means no skin key will be highlighted. ckey = -10; skey = -1; if (key_macro[0] != 0) if (key_macro[1] == 0) ckey = key_macro[0]; else if (key_macro[2] == 0 && key_macro[0] == 28) ckey = key_macro[1]; bool needs_expansion = false; for (int j = 0; key_macro[j] != 0; j++) if (key_macro[j] > 37) { needs_expansion = true; break; } if (needs_expansion) { static unsigned char macrobuf[1024]; int p = 0; for (int j = 0; key_macro[j] != 0 && p < 1023; j++) { int c = key_macro[j]; if (c <= 37) macrobuf[p++] = c; else { unsigned char *m = skin_find_macro(c); if (m != NULL) while (*m != 0 && p < 1023) macrobuf[p++] = *m++; } } macrobuf[p] = 0; macro = macrobuf; } else macro = key_macro; shell_keydown(); mouse_key = false; active_keycode = event->hardware_keycode; } } } else if (event->type == GDK_KEY_RELEASE) { if (ckey == 0) { if (just_pressed_shift && (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)) { ckey = 28; skey = -1; macro = NULL; shell_keydown(); shell_keyup(); } } else { if (!mouse_key && event->hardware_keycode == active_keycode) { shell_keyup(); active_keycode = 0; } } } return TRUE; } static void enable_reminder() { if (reminder_id == 0) reminder_id = g_idle_add(reminder, NULL); if (timeout_id != 0) { g_source_remove(timeout_id); timeout_id = 0; } } static void disable_reminder() { if (reminder_id != 0) { g_source_remove(reminder_id); reminder_id = 0; } } static gboolean repeater(gpointer cd) { int repeat = core_repeat(); if (repeat != 0) timeout_id = g_timeout_add(repeat == 1 ? 200 : 100, repeater, NULL); else timeout_id = g_timeout_add(250, timeout1, NULL); return FALSE; } static gboolean timeout1(gpointer cd) { if (ckey != 0) { core_keytimeout1(); timeout_id = g_timeout_add(1750, timeout2, NULL); } else timeout_id = 0; return FALSE; } static gboolean timeout2(gpointer cd) { if (ckey != 0) core_keytimeout2(); timeout_id = 0; return FALSE; } static gboolean timeout3(gpointer cd) { bool keep_running = core_timeout3(1); timeout3_id = 0; if (keep_running) enable_reminder(); return FALSE; } static gboolean battery_checker(gpointer cd) { shell_low_battery(); return TRUE; } static void repaint_printout(int x, int y, int width, int height) { GdkPixbuf *buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); int d_bpl = gdk_pixbuf_get_rowstride(buf); guchar *d1 = gdk_pixbuf_get_pixels(buf); int length = printout_bottom - printout_top; if (length < 0) length += PRINT_LINES; for (int v = y; v < y + height; v++) { int v2 = printout_top + v; if (v2 >= PRINT_LINES) v2 -= PRINT_LINES; int v3 = v2 * 36; guchar *dst = d1; for (int h = x; h < x + width; h++) { unsigned char c; if (h >= 286 || v >= length) c = 127; else if ((print_bitmap[v3 + (h >> 3)] & (1 << (h & 7))) == 0) c = 255; else c = 0; *dst++ = c; *dst++ = c; *dst++ = c; } d1 += d_bpl; } gdk_draw_pixbuf(print_widget->window, NULL, buf, 0, 0, x, y, width, height, GDK_RGB_DITHER_MAX, 0, 0); g_object_unref(G_OBJECT(buf)); } static gboolean reminder(gpointer cd) { int dummy1, dummy2; int keep_running = core_keydown(0, &dummy1, &dummy2); if (quit_flag) quit(); if (keep_running) return TRUE; else { reminder_id = 0; return FALSE; } } /* Callbacks used by shell_print() and shell_spool_txt() / shell_spool_gif() */ static void txt_writer(const char *text, int length) { int n; if (print_txt == NULL) return; n = fwrite(text, 1, length, print_txt); if (n != length) { char buf[1000]; state.printerToTxtFile = 0; fclose(print_txt); print_txt = NULL; snprintf(buf, 1000, "Error while writing to \"%s\".\nPrinting to text file disabled", state.printerTxtFileName); show_message("Message", buf); } } static void txt_newliner() { if (print_txt == NULL) return; fputc('\n', print_txt); fflush(print_txt); } static void gif_seeker(int4 pos) { if (print_gif == NULL) return; if (fseek(print_gif, pos, SEEK_SET) == -1) { char buf[1000]; state.printerToGifFile = 0; fclose(print_gif); print_gif = NULL; snprintf(buf, 1000, "Error while seeking \"%s\".\nPrinting to GIF file disabled", print_gif_name); show_message("Message", buf); } } static void gif_writer(const char *text, int length) { int n; if (print_gif == NULL) return; n = fwrite(text, 1, length, print_gif); if (n != length) { char buf[1000]; state.printerToGifFile = 0; fclose(print_gif); print_gif = NULL; snprintf(buf, 1000, "Error while writing to \"%s\".\nPrinting to GIF file disabled", print_gif_name); show_message("Message", buf); } } void shell_blitter(const char *bits, int bytesperline, int x, int y, int width, int height) { skin_display_blitter(bits, bytesperline, x, y, width, height); if (skey >= -7 && skey <= -2) skin_repaint_key(skey, 1); } void shell_beeper(int frequency, int duration) { #ifdef AUDIO_ALSA const char *display_name = gdk_display_get_name(gdk_display_get_default()); if (display_name == NULL || display_name[0] == ':') { if (!alsa_beeper(frequency, duration)) gdk_beep(); } else gdk_beep(); #else gdk_beep(); #endif } void shell_annunciators(int updn, int shf, int prt, int run, int g, int rad) { if (updn != -1 && ann_updown != updn) { ann_updown = updn; skin_repaint_annunciator(1, ann_updown); } if (shf != -1 && ann_shift != shf) { ann_shift = shf; skin_repaint_annunciator(2, ann_shift); } if (prt != -1 && ann_print != prt) { ann_print = prt; skin_repaint_annunciator(3, ann_print); } if (run != -1 && ann_run != run) { ann_run = run; skin_repaint_annunciator(4, ann_run); } if (g != -1 && ann_g != g) { ann_g = g; skin_repaint_annunciator(6, ann_g); } if (rad != -1 && ann_rad != rad) { ann_rad = rad; skin_repaint_annunciator(7, ann_rad); } } int shell_wants_cpu() { return g_main_context_pending(NULL) ? 1 : 0; } void shell_delay(int duration) { gdk_display_flush(gdk_display_get_default()); g_usleep(duration * 1000); } void shell_request_timeout3(int delay) { if (timeout3_id != 0) g_source_remove(timeout3_id); timeout3_id = g_timeout_add(delay, timeout3, NULL); } int4 shell_read_saved_state(void *buf, int4 bufsize) { if (statefile == NULL) return -1; else { int4 n = fread(buf, 1, bufsize, statefile); if (n != bufsize && ferror(statefile)) { fclose(statefile); statefile = NULL; return -1; } else return n; } } bool shell_write_saved_state(const void *buf, int4 nbytes) { if (statefile == NULL) return false; else { int4 n = fwrite(buf, 1, nbytes, statefile); if (n != nbytes) { fclose(statefile); remove(statefilename); statefile = NULL; return false; } else return true; } } uint4 shell_get_mem() { FILE *meminfo = fopen("/proc/meminfo", "r"); char line[1024]; uint4 bytes = 0; if (meminfo == NULL) return 0; while (fgets(line, 1024, meminfo) != NULL) { if (strncmp(line, "MemFree:", 8) == 0) { unsigned int kbytes; if (sscanf(line + 8, "%u", &kbytes) == 1) bytes = 1024 * kbytes; break; } } fclose(meminfo); return bytes; } int shell_low_battery() { /* /proc/apm partial legend: * * 1.16 1.2 0x03 0x01 0x03 0x09 9% -1 ? * ^^^^ ^^^^ * | +-- Battery status (0 = full, 1 = low, * | 2 = critical, 3 = charging) * +------- AC status (0 = offline, 1 = online) */ FILE *apm = fopen("/proc/apm", "r"); char line[1024]; int lowbat = 0; int ac_stat, bat_stat; if (apm == NULL) goto done2; if (fgets(line, 1024, apm) == NULL) goto done1; if (sscanf(line, "%*s %*s %*s %x %x", &ac_stat, &bat_stat) == 2) lowbat = ac_stat != 1 && (bat_stat == 1 || bat_stat == 2); done1: fclose(apm); done2: if (lowbat != ann_battery) { ann_battery = lowbat; if (allow_paint) skin_repaint_annunciator(5, ann_battery); } return lowbat; } void shell_powerdown() { /* We defer the actual shutdown so the emulator core can * return from core_keyup() or core_keydown() and isn't * asked to save its state while still in the middle of * executing the OFF instruction... */ quit_flag = true; } double shell_random_seed() { struct timeval tv; gettimeofday(&tv, NULL); return ((tv.tv_sec * 1000000L + tv.tv_usec) & 0xffffffffL) / 4294967296.0; } uint4 shell_milliseconds() { struct timeval tv; gettimeofday(&tv, NULL); return (uint4) (tv.tv_sec * 1000L + tv.tv_usec / 1000); } struct print_growth_info { int y, height; print_growth_info(int yy, int hheight) : y(yy), height(hheight) {} }; static gboolean print_widget_grew(GtkWidget *w, GdkEventConfigure *event, gpointer cd) { print_growth_info *info = (print_growth_info *) cd; scroll_printout_to_bottom(); repaint_printout(0, info->y, 286, info->height); g_signal_handlers_disconnect_by_func(G_OBJECT(w), (gpointer) print_widget_grew, cd); delete info; return FALSE; } void shell_print(const char *text, int length, const char *bits, int bytesperline, int x, int y, int width, int height) { int xx, yy; int oldlength, newlength; for (yy = 0; yy < height; yy++) { int4 Y = (printout_bottom + 2 * yy) % PRINT_LINES; for (xx = 0; xx < 143; xx++) { int bit, px, py; if (xx < width) { char c = bits[(y + yy) * bytesperline + ((x + xx) >> 3)]; bit = (c & (1 << ((x + xx) & 7))) != 0; } else bit = 0; for (px = xx * 2; px < (xx + 1) * 2; px++) for (py = Y; py < Y + 2; py++) if (bit) print_bitmap[py * PRINT_BYTESPERLINE + (px >> 3)] |= 1 << (px & 7); else print_bitmap[py * PRINT_BYTESPERLINE + (px >> 3)] &= ~(1 << (px & 7)); } } oldlength = printout_bottom - printout_top; if (oldlength < 0) oldlength += PRINT_LINES; printout_bottom = (printout_bottom + 2 * height) % PRINT_LINES; newlength = oldlength + 2 * height; if (newlength >= PRINT_LINES) { int offset; printout_top = (printout_bottom + 2) % PRINT_LINES; newlength = PRINT_LINES - 2; if (newlength != oldlength) gtk_widget_set_size_request(print_widget, 286, newlength); scroll_printout_to_bottom(); offset = 2 * height - newlength + oldlength; if (print_gc == NULL) print_gc = gdk_gc_new(print_widget->window); gdk_draw_drawable(print_widget->window, print_gc, print_widget->window, 0, offset, 0, 0, 286, oldlength - offset); repaint_printout(0, newlength - 2 * height, 286, 2 * height); } else { gtk_widget_set_size_request(print_widget, 286, newlength); // The resize request does not take effect immediately; // if I call scroll_printout_to_bottom() now, the scrolling will take // place *before* the resizing, leaving the scroll bar in the wrong // position. // I work around this by using a callback to finish the job. g_signal_connect(G_OBJECT(print_widget), "configure-event", G_CALLBACK(print_widget_grew), (gpointer) new print_growth_info(oldlength, 2 * height)); } if (state.printerToTxtFile) { int err; char buf[1000]; if (print_txt == NULL) { print_txt = fopen(state.printerTxtFileName, "a"); if (print_txt == NULL) { err = errno; state.printerToTxtFile = 0; snprintf(buf, 1000, "Can't open \"%s\" for output:\n%s (%d)\nPrinting to text file disabled.", state.printerTxtFileName, strerror(err), err); show_message("Message", buf); goto done_print_txt; } #ifdef OLPC chmod(state.printerTxtFileName, 0664); #endif } shell_spool_txt(text, length, txt_writer, txt_newliner); done_print_txt:; } if (state.printerToGifFile) { int err; char buf[1000]; if (print_gif != NULL && gif_lines + height > state.printerGifMaxLength) { shell_finish_gif(gif_seeker, gif_writer); fclose(print_gif); print_gif = NULL; } if (print_gif == NULL) { while (1) { int len, p; gif_seq = (gif_seq + 1) % 10000; strcpy(print_gif_name, state.printerGifFileName); len = strlen(print_gif_name); /* Strip ".gif" extension, if present */ if (len >= 4 && strcasecmp(print_gif_name + len - 4, ".gif") == 0) { len -= 4; print_gif_name[len] = 0; } /* Strip ".[0-9]+", if present */ p = len; while (p > 0 && print_gif_name[p] >= '0' && print_gif_name[p] <= '9') p--; if (p < len && p >= 0 && print_gif_name[p] == '.') print_gif_name[p] = 0; /* Make sure we have enough space for the ".nnnn.gif" */ p = 1000 - 10; print_gif_name[p] = 0; p = strlen(print_gif_name); snprintf(print_gif_name + p, 6, ".%04d", gif_seq); strcat(print_gif_name, ".gif"); if (!is_file(print_gif_name)) break; } print_gif = fopen(print_gif_name, "w+"); if (print_gif == NULL) { err = errno; state.printerToGifFile = 0; snprintf(buf, 1000, "Can't open \"%s\" for output:\n%s (%d)\nPrinting to GIF file disabled.", print_gif_name, strerror(err), err); show_message("Message", buf); goto done_print_gif; } #ifdef OLPC chmod(print_gif_name, 0664); #endif if (!shell_start_gif(gif_writer, state.printerGifMaxLength)) { state.printerToGifFile = 0; show_message("Message", "Not enough memory for the GIF encoder.\nPrinting to GIF file disabled."); goto done_print_gif; } gif_lines = 0; } shell_spool_gif(bits, bytesperline, x, y, width, height, gif_writer); gif_lines += height; done_print_gif:; } } int shell_write(const char *buf, int4 buflen) { int4 written; if (export_file == NULL) return 0; written = fwrite(buf, 1, buflen, export_file); if (written != buflen) { char buf[1000]; fclose(export_file); export_file = NULL; snprintf(buf, 1000, "Writing \"%s\" failed.", export_file_name); show_message("Message", buf); return 0; } else return 1; } int shell_read(char *buf, int4 buflen) { int4 nread; if (import_file == NULL) return -1; nread = fread(buf, 1, buflen, import_file); if (nread != buflen && ferror(import_file)) { fclose(import_file); import_file = NULL; show_message("Message", "An error occurred; import was terminated prematurely."); return -1; } else return nread; } shell_bcd_table_struct *shell_get_bcd_table() { return NULL; } shell_bcd_table_struct *shell_put_bcd_table(shell_bcd_table_struct *bcdtab, uint4 size) { return bcdtab; } void shell_release_bcd_table(shell_bcd_table_struct *bcdtab) { free(bcdtab); } void shell_get_time_date(uint4 *time, uint4 *date, int *weekday) { struct timeval tv; gettimeofday(&tv, NULL); struct tm tms; localtime_r(&tv.tv_sec, &tms); if (time != NULL) *time = ((tms.tm_hour * 100 + tms.tm_min) * 100 + tms.tm_sec) * 100 + tv.tv_usec / 10000; if (date != NULL) *date = ((tms.tm_year + 1900) * 100 + tms.tm_mon + 1) * 100 + tms.tm_mday; if (weekday != NULL) *weekday = tms.tm_wday; } free42-nologo-1.4.77/gtk/shell_main.h000644 000765 000024 00000003334 12110237246 017704 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef SHELL_MAIN_H #define SHELL_MAIN_H 1 #include #define FILENAMELEN 256 extern GtkWidget *calc_widget; extern bool allow_paint; #define SHELL_VERSION 4 struct state_type { int extras; int printerToTxtFile; int printerToGifFile; char printerTxtFileName[FILENAMELEN]; char printerGifFileName[FILENAMELEN]; int printerGifMaxLength; char mainWindowKnown, printWindowKnown, printWindowMapped; int mainWindowX, mainWindowY; int printWindowX, printWindowY, printWindowHeight; char skinName[FILENAMELEN]; int singleInstance; }; extern state_type state; extern char free42dirname[FILENAMELEN]; #define KEYMAP_MAX_MACRO_LENGTH 31 typedef struct { bool ctrl; bool alt; bool shift; bool cshift; guint keyval; unsigned char macro[KEYMAP_MAX_MACRO_LENGTH + 1]; } keymap_entry; keymap_entry *parse_keymap_entry(char *line, int lineno); #endif free42-nologo-1.4.77/gtk/shell_skin.cc000644 000765 000024 00000050155 12110237246 020065 0ustar00thomasstaff000000 000000 /////////////////////////////////////////////////////////////////////////////// // Free42 -- an HP-42S calculator simulator // Copyright (C) 2004-2013 Thomas Okken // // 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, see http://www.gnu.org/licenses/. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include "shell_skin.h" #include "shell_main.h" #include "shell_loadimage.h" #include "core_main.h" /**************************/ /* Skin description stuff */ /**************************/ typedef struct { int x, y; } SkinPoint; typedef struct { int x, y, width, height; } SkinRect; typedef struct { int code, shifted_code; SkinRect sens_rect; SkinRect disp_rect; SkinPoint src; } SkinKey; #define SKIN_MAX_MACRO_LENGTH 63 typedef struct _SkinMacro { int code; unsigned char macro[SKIN_MAX_MACRO_LENGTH + 1]; struct _SkinMacro *next; } SkinMacro; typedef struct { SkinRect disp_rect; SkinPoint src; } SkinAnnunciator; static SkinRect skin; static SkinPoint display_loc; static SkinPoint display_scale; static SkinColor display_bg, display_fg; static SkinKey *keylist = NULL; static int nkeys = 0; static int keys_cap = 0; static SkinMacro *macrolist = NULL; static SkinAnnunciator annunciators[7]; static FILE *external_file; static long builtin_length; static long builtin_pos; static const unsigned char *builtin_file; static GdkPixbuf *skin_image = NULL; static int skin_y; static int skin_type; static const SkinColor *skin_cmap; static GdkPixbuf *disp_image = NULL; static char skin_label_buf[1024]; static int skin_label_pos; static keymap_entry *keymap = NULL; static int keymap_length; static bool display_enabled = true; /**********************************************************/ /* Linked-in skins; defined in the skins.c, which in turn */ /* is generated by skin2c.c under control of skin2c.conf */ /**********************************************************/ extern const int skin_count; extern const char *skin_name[]; extern const long skin_layout_size[]; extern const unsigned char *skin_layout_data[]; extern const long skin_bitmap_size[]; extern const unsigned char *skin_bitmap_data[]; /*******************/ /* Local functions */ /*******************/ static void addMenuItem(GtkMenu *menu, const char *name); static void selectSkinCB(GtkWidget *w, gpointer cd); static int skin_open(const char *name, int open_layout); static int skin_gets(char *buf, int buflen); static void skin_close(); static void addMenuItem(GtkMenu *menu, const char *name) { bool checked = false; if (state.skinName[0] == 0) { strcpy(state.skinName, name); checked = true; } else if (strcmp(state.skinName, name) == 0) checked = true; GtkWidget *w = gtk_check_menu_item_new_with_label(name); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), checked); // Apparently, there is no way to retrieve the label from a menu item, // so I have to store them and pass them to the callback explicitly. char *lbl = skin_label_buf + skin_label_pos; strcpy(lbl, name); skin_label_pos += strlen(name) + 1; g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(selectSkinCB), (gpointer) lbl); gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); gtk_widget_show(w); } static void selectSkinCB(GtkWidget *w, gpointer cd) { char *name = (char *) cd; if (strcmp(state.skinName, name) != 0) { int w, h; strcpy(state.skinName, name); skin_load(&w, &h); core_repaint_display(); gtk_widget_set_size_request(calc_widget, w, h); } } static int skin_open(const char *name, int open_layout) { int i; char namebuf[1024]; /* Look for built-in skin first */ for (i = 0; i < skin_count; i++) { if (strcmp(name, skin_name[i]) == 0) { external_file = NULL; builtin_pos = 0; if (open_layout) { builtin_length = skin_layout_size[i]; builtin_file = skin_layout_data[i]; } else { builtin_length = skin_bitmap_size[i]; builtin_file = skin_bitmap_data[i]; } return 1; } } /* name did not match a built-in skin; look for file */ snprintf(namebuf, 1024, "%s/%s.%s", free42dirname, name, open_layout ? "layout" : "gif"); external_file = fopen(namebuf, "r"); return external_file != NULL; } int skin_getchar() { if (external_file != NULL) return fgetc(external_file); else if (builtin_pos < builtin_length) return builtin_file[builtin_pos++]; else return EOF; } static int skin_gets(char *buf, int buflen) { int p = 0; int eof = -1; int comment = 0; while (p < buflen - 1) { int c = skin_getchar(); if (eof == -1) eof = c == EOF; if (c == EOF || c == '\n' || c == '\r') break; /* Remove comments */ if (c == '#') comment = 1; if (comment) continue; /* Suppress leading spaces */ if (p == 0 && isspace(c)) continue; buf[p++] = c; } buf[p++] = 0; return p > 1 || !eof; } static void skin_close() { if (external_file != NULL) fclose(external_file); } static int case_insens_comparator(const void *a, const void *b) { return strcasecmp(*(const char **) a, *(const char **) b); } void skin_menu_update(GtkWidget *w) { int i, j; GtkMenu *skin_menu = (GtkMenu *) gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)); GList *children = gtk_container_get_children(GTK_CONTAINER(skin_menu)); GList *item = children; while (item != NULL) { gtk_widget_destroy(GTK_WIDGET(item->data)); item = item->next; } g_list_free(children); skin_label_pos = 0; for (i = 0; i < skin_count; i++) addMenuItem(skin_menu, skin_name[i]); DIR *dir = opendir(free42dirname); if (dir == NULL) return; struct dirent *dent; char *skinname[100]; int nskins = 0; while ((dent = readdir(dir)) != NULL && nskins < 100) { int namelen = strlen(dent->d_name); char *skn; if (namelen < 7) continue; if (strcmp(dent->d_name + namelen - 7, ".layout") != 0) continue; skn = (char *) malloc(namelen - 6); // TODO - handle memory allocation failure memcpy(skn, dent->d_name, namelen - 7); skn[namelen - 7] = 0; skinname[nskins++] = skn; } closedir(dir); qsort(skinname, nskins, sizeof(char *), case_insens_comparator); bool have_separator = false; for (i = 0; i < nskins; i++) { for (j = 0; j < skin_count; j++) if (strcmp(skinname[i], skin_name[j]) == 0) goto skip; if (!have_separator) { GtkWidget *w = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(skin_menu), w); gtk_widget_show(w); have_separator = true; } addMenuItem(skin_menu, skinname[i]); skip: free(skinname[i]); } } void skin_load(int *width, int *height) { char line[1024]; int success; int lineno = 0; if (state.skinName[0] == 0) { fallback_on_1st_builtin_skin: strcpy(state.skinName, skin_name[0]); } /*************************/ /* Load skin description */ /*************************/ if (!skin_open(state.skinName, 1)) goto fallback_on_1st_builtin_skin; if (keylist != NULL) free(keylist); keylist = NULL; nkeys = 0; keys_cap = 0; while (macrolist != NULL) { SkinMacro *m = macrolist->next; free(macrolist); macrolist = m; } if (keymap != NULL) free(keymap); keymap = NULL; keymap_length = 0; int kmcap = 0; while (skin_gets(line, 1024)) { lineno++; if (*line == 0) continue; if (strncasecmp(line, "skin:", 5) == 0) { int x, y, width, height; if (sscanf(line + 5, " %d,%d,%d,%d", &x, &y, &width, &height) == 4){ skin.x = x; skin.y = y; skin.width = width; skin.height = height; } } else if (strncasecmp(line, "display:", 8) == 0) { int x, y, xscale, yscale; unsigned long bg, fg; if (sscanf(line + 8, " %d,%d %d %d %lx %lx", &x, &y, &xscale, &yscale, &bg, &fg) == 6) { display_loc.x = x; display_loc.y = y; display_scale.x = xscale; display_scale.y = yscale; display_bg.r = (unsigned char) (bg >> 16); display_bg.g = (unsigned char) (bg >> 8); display_bg.b = (unsigned char) bg; display_fg.r = (unsigned char) (fg >> 16); display_fg.g = (unsigned char) (fg >> 8); display_fg.b = (unsigned char) fg; } } else if (strncasecmp(line, "key:", 4) == 0) { char keynumbuf[20]; int keynum, shifted_keynum; int sens_x, sens_y, sens_width, sens_height; int disp_x, disp_y, disp_width, disp_height; int act_x, act_y; if (sscanf(line + 4, " %s %d,%d,%d,%d %d,%d,%d,%d %d,%d", keynumbuf, &sens_x, &sens_y, &sens_width, &sens_height, &disp_x, &disp_y, &disp_width, &disp_height, &act_x, &act_y) == 11) { int n = sscanf(keynumbuf, "%d,%d", &keynum, &shifted_keynum); if (n > 0) { if (n == 1) shifted_keynum = keynum; SkinKey *key; if (nkeys == keys_cap) { keys_cap += 50; keylist = (SkinKey *) realloc(keylist, keys_cap * sizeof(SkinKey)); // TODO - handle memory allocation failure } key = keylist + nkeys; key->code = keynum; key->shifted_code = shifted_keynum; key->sens_rect.x = sens_x; key->sens_rect.y = sens_y; key->sens_rect.width = sens_width; key->sens_rect.height = sens_height; key->disp_rect.x = disp_x; key->disp_rect.y = disp_y; key->disp_rect.width = disp_width; key->disp_rect.height = disp_height; key->src.x = act_x; key->src.y = act_y; nkeys++; } } } else if (strncasecmp(line, "macro:", 6) == 0) { char *tok = strtok(line + 6, " "); int len = 0; SkinMacro *macro = NULL; while (tok != NULL) { char *endptr; long n = strtol(tok, &endptr, 10); if (*endptr != 0) { /* Not a proper number; ignore this macro */ if (macro != NULL) { free(macro); macro = NULL; break; } } if (macro == NULL) { if (n < 38 || n > 255) /* Macro code out of range; ignore this macro */ break; macro = (SkinMacro *) malloc(sizeof(SkinMacro)); // TODO - handle memory allocation failure macro->code = n; } else if (len < SKIN_MAX_MACRO_LENGTH) { if (n < 1 || n > 37) { /* Key code out of range; ignore this macro */ free(macro); macro = NULL; break; } macro->macro[len++] = n; } tok = strtok(NULL, " "); } if (macro != NULL) { macro->macro[len++] = 0; macro->next = macrolist; macrolist = macro; } } else if (strncasecmp(line, "annunciator:", 12) == 0) { int annnum; int disp_x, disp_y, disp_width, disp_height; int act_x, act_y; if (sscanf(line + 12, " %d %d,%d,%d,%d %d,%d", &annnum, &disp_x, &disp_y, &disp_width, &disp_height, &act_x, &act_y) == 7) { if (annnum >= 1 && annnum <= 7) { SkinAnnunciator *ann = annunciators + (annnum - 1); ann->disp_rect.x = disp_x; ann->disp_rect.y = disp_y; ann->disp_rect.width = disp_width; ann->disp_rect.height = disp_height; ann->src.x = act_x; ann->src.y = act_y; } } } else if (strchr(line, ':') != NULL) { keymap_entry *entry = parse_keymap_entry(line, lineno); if (entry != NULL) { if (keymap_length == kmcap) { kmcap += 50; keymap = (keymap_entry *) realloc(keymap, kmcap * sizeof(keymap_entry)); // TODO - handle memory allocation failure } memcpy(keymap + (keymap_length++), entry, sizeof(keymap_entry)); } } } skin_close(); /********************/ /* Load skin bitmap */ /********************/ if (!skin_open(state.skinName, 0)) goto fallback_on_1st_builtin_skin; /* shell_loadimage() calls skin_getchar() to load the image from the * compiled-in or on-disk file; it calls skin_init_image(), * skin_put_pixels(), and skin_finish_image() to create the in-memory * representation. */ success = shell_loadimage(); skin_close(); if (!success) goto fallback_on_1st_builtin_skin; *width = skin.width; *height = skin.height; /********************************/ /* (Re)build the display bitmap */ /********************************/ if (disp_image != NULL) g_object_unref(disp_image); disp_image = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 131 * display_scale.x, 16 * display_scale.y); guint32 p = (display_bg.r << 24) | (display_bg.g << 16) | (display_bg.b << 8); gdk_pixbuf_fill(disp_image, p); } int skin_init_image(int type, int ncolors, const SkinColor *colors, int width, int height) { if (skin_image != NULL) { g_object_unref(skin_image); skin_image = NULL; } skin_image = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); skin_y = 0; skin_type = type; skin_cmap = colors; return 1; } void skin_put_pixels(unsigned const char *data) { guchar *pix = gdk_pixbuf_get_pixels(skin_image); int bytesperline = gdk_pixbuf_get_rowstride(skin_image); int width = gdk_pixbuf_get_width(skin_image); guchar *p = pix + skin_y * bytesperline; if (skin_type == IMGTYPE_MONO) { for (int x = 0; x < width; x++) { guchar c; if ((data[x >> 3] & (1 << (x & 7))) == 0) c = 0; else c = 255; *p++ = c; *p++ = c; *p++ = c; } } else if (skin_type == IMGTYPE_GRAY) { for (int x = 0; x < width; x++) { guchar c = data[x]; *p++ = c; *p++ = c; *p++ = c; } } else if (skin_type == IMGTYPE_COLORMAPPED) { for (int x = 0; x < width; x++) { guchar c = data[x]; *p++ = skin_cmap[c].r; *p++ = skin_cmap[c].g; *p++ = skin_cmap[c].b; } } else { // skin_type == IMGTYPE_TRUECOLOR int xx = 0; for (int x = 0; x < width; x++) { xx++; *p++ = data[xx++]; *p++ = data[xx++]; *p++ = data[xx++]; } } skin_y++; } void skin_finish_image() { // Nothing to do. } void skin_repaint() { gdk_draw_pixbuf(calc_widget->window, NULL, skin_image, skin.x, skin.y, 0, 0, skin.width, skin.height, GDK_RGB_DITHER_MAX, 0, 0); } void skin_repaint_annunciator(int which, bool state) { if (!display_enabled) return; SkinAnnunciator *ann = annunciators + (which - 1); if (state) gdk_draw_pixbuf(calc_widget->window, NULL, skin_image, ann->src.x, ann->src.y, ann->disp_rect.x, ann->disp_rect.y, ann->disp_rect.width, ann->disp_rect.height, GDK_RGB_DITHER_MAX, 0, 0); else gdk_draw_pixbuf(calc_widget->window, NULL, skin_image, ann->disp_rect.x, ann->disp_rect.y, ann->disp_rect.x, ann->disp_rect.y, ann->disp_rect.width, ann->disp_rect.height, GDK_RGB_DITHER_MAX, 0, 0); } void skin_find_key(int x, int y, bool cshift, int *skey, int *ckey) { int i; if (core_menu() && x >= display_loc.x && x < display_loc.x + 131 * display_scale.x && y >= display_loc.y + 9 * display_scale.y && y < display_loc.y + 16 * display_scale.y) { int softkey = (x - display_loc.x) / (22 * display_scale.x) + 1; *skey = -1 - softkey; *ckey = softkey; return; } for (i = 0; i < nkeys; i++) { SkinKey *k = keylist + i; int rx = x - k->sens_rect.x; int ry = y - k->sens_rect.y; if (rx >= 0 && rx < k->sens_rect.width && ry >= 0 && ry < k->sens_rect.height) { *skey = i; *ckey = cshift ? k->shifted_code : k->code; return; } } *skey = -1; *ckey = 0; } int skin_find_skey(int ckey) { int i; for (i = 0; i < nkeys; i++) if (keylist[i].code == ckey || keylist[i].shifted_code == ckey) return i; return -1; } unsigned char *skin_find_macro(int ckey) { SkinMacro *m = macrolist; while (m != NULL) { if (m->code == ckey) return m->macro; m = m->next; } return NULL; } unsigned char *skin_keymap_lookup(guint keyval, bool printable, bool ctrl, bool alt, bool shift, bool cshift, bool *exact) { int i; unsigned char *macro = NULL; for (i = 0; i < keymap_length; i++) { keymap_entry *entry = keymap + i; if (ctrl == entry->ctrl && alt == entry->alt && (printable || shift == entry->shift) && keyval == entry->keyval) { macro = entry->macro; if (cshift == entry->cshift) { *exact = true; return macro; } } } *exact = false; return macro; } void skin_repaint_key(int key, bool state) { SkinKey *k; if (key >= -7 && key <= -2) { /* Soft key */ if (!display_enabled) // Should never happen -- the display is only disabled during macro // execution, and softkey events should be impossible to generate // in that state. But, just staying on the safe side. return; key = -1 - key; int x = (key - 1) * 22 * display_scale.x; int y = 9 * display_scale.y; int width = 21 * display_scale.x; int height = 7 * display_scale.y; if (state) { // Construct a temporary pixbuf, create the inverted version of // the affected screen rectangle there, and blit it GdkPixbuf *tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); int s_bpl = gdk_pixbuf_get_rowstride(disp_image); int d_bpl = gdk_pixbuf_get_rowstride(tmpbuf); guchar *s1 = gdk_pixbuf_get_pixels(disp_image) + x * 3 + s_bpl * y; guchar *d1 = gdk_pixbuf_get_pixels(tmpbuf); for (int v = 0; v < height; v++) { guchar *src = s1; guchar *dst = d1; for (int h = 0; h < width; h++) { unsigned char r = *src++; unsigned char g = *src++; unsigned char b = *src++; if (r == display_bg.r && g == display_bg.g && b == display_bg.b) { *dst++ = display_fg.r; *dst++ = display_fg.g; *dst++ = display_fg.b; } else { *dst++ = display_bg.r; *dst++ = display_bg.g; *dst++ = display_bg.b; } } s1 += s_bpl; d1 += d_bpl; } gdk_draw_pixbuf(calc_widget->window, NULL, tmpbuf, 0, 0, display_loc.x + x, display_loc.y + y, width, height, GDK_RGB_DITHER_NONE, 0, 0); } else { // Repaint the screen gdk_draw_pixbuf(calc_widget->window, NULL, disp_image, x, y, display_loc.x + x, display_loc.y + y, width, height, GDK_RGB_DITHER_NONE, 0, 0); } return; } if (key < 0 || key >= nkeys) return; k = keylist + key; if (state) gdk_draw_pixbuf(calc_widget->window, NULL, skin_image, k->src.x, k->src.y, k->disp_rect.x, k->disp_rect.y, k->disp_rect.width, k->disp_rect.height, GDK_RGB_DITHER_MAX, k->disp_rect.x, k->disp_rect.y); else gdk_draw_pixbuf(calc_widget->window, NULL, skin_image, k->disp_rect.x, k->disp_rect.y, k->disp_rect.x, k->disp_rect.y, k->disp_rect.width, k->disp_rect.height, GDK_RGB_DITHER_MAX, k->disp_rect.x, k->disp_rect.y); } void skin_display_blitter(const char *bits, int bytesperline, int x, int y, int width, int height) { guchar *pix = gdk_pixbuf_get_pixels(disp_image); int disp_bpl = gdk_pixbuf_get_rowstride(disp_image); int sx = display_scale.x; int sy = display_scale.y; for (int v = y; v < y + height; v++) for (int h = x; h < x + width; h++) { SkinColor c; if ((bits[v * bytesperline + (h >> 3)] & (1 << (h & 7))) != 0) c = display_fg; else c = display_bg; for (int vv = v * sy; vv < (v + 1) * sy; vv++) { guchar *p = pix + disp_bpl * vv; for (int hh = h * sx; hh < (h + 1) * sx; hh++) { guchar *p2 = p + hh * 3; p2[0] = c.r; p2[1] = c.g; p2[2] = c.b; } } } if (allow_paint && display_enabled) gdk_draw_pixbuf(calc_widget->window, NULL, disp_image, x * sx, y * sy, display_loc.x + x * sx, display_loc.y + y * sy, width * sx, height * sy, GDK_RGB_DITHER_NONE, 0, 0); } void skin_repaint_display() { if (display_enabled) gdk_draw_pixbuf(calc_widget->window, NULL, disp_image, 0, 0, display_loc.x, display_loc.y, 131 * display_scale.x, 16 * display_scale.y, GDK_RGB_DITHER_NONE, 0, 0); } void skin_display_set_enabled(bool enable) { display_enabled = enable; } free42-nologo-1.4.77/gtk/shell_skin.h000644 000765 000024 00000003653 12110237246 017730 0ustar00thomasstaff000000 000000 /////////////////////////////////////////////////////////////////////////////// // Free42 -- an HP-42S calculator simulator // Copyright (C) 2004-2013 Thomas Okken // // 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, see http://www.gnu.org/licenses/. /////////////////////////////////////////////////////////////////////////////// #ifndef SHELL_SKIN_H #define SHELL_SKIN_H 1 #include void skin_menu_update(GtkWidget *w); void skin_load(int *width, int *height); typedef struct { unsigned char r, g, b, pad; } SkinColor; #define IMGTYPE_MONO 1 #define IMGTYPE_GRAY 2 #define IMGTYPE_COLORMAPPED 3 #define IMGTYPE_TRUECOLOR 4 int skin_getchar(); void skin_rewind(); int skin_init_image(int type, int ncolors, const SkinColor *colors, int width, int height); void skin_put_pixels(unsigned const char *data); void skin_finish_image(); void skin_repaint(); void skin_repaint_annunciator(int which, bool state); void skin_find_key(int x, int y, bool cshift, int *key, int *code); int skin_find_skey(int ckey); unsigned char *skin_find_macro(int ckey); unsigned char *skin_keymap_lookup(guint keyval, bool printable, bool ctrl, bool alt, bool shift, bool cshift, bool *exact); void skin_repaint_key(int key, bool state); void skin_display_blitter(const char *bits, int bytesperline, int x, int y, int width, int height); void skin_repaint_display(); void skin_display_set_enabled(bool enable); #endif free42-nologo-1.4.77/common/bcd.cc000644 000765 000024 00000004037 12110237247 017164 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ extern "C" { #include } #include "bcd2.h" char BCD::_buf[64]; BCD sqrt(const BCD& a) { BCD c; if (!BCDFloat::sqrt(&a._v, &c._v)) c._v = BCDFloat::nan(); return c; } const char* BCD::asStringFmt(Format fmt, int precision) const { const BCD* bp = this; BCD rv; if ((fmt & BCDFloat::format_truncate) == 0) { rv = round(precision); bp = &rv; } bp->_v.asStringFmt(_buf, fmt, precision); return _buf; } BCD pow(const BCD& a, int4 n) { int4 m; if (n == 0) return 1; m = (n < 0) ? -n : n; BCD2 s; if (m > 1) { BCD2 r = a; s = 1; /* Use binary exponentiation */ for (;;) { if (m & 1) s *= r; m >>= 1; if (!m) break; r *= r; } } else { s = a; } /* Compute the reciprocal if n is negative. */ BCD s1 = s.asBCD(); return n < 0 ? 1/s1 : s1; } free42-nologo-1.4.77/common/bcd.h000644 000765 000024 00000014700 12110237247 017024 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __bcd_h__ #define __bcd_h__ #include "bcdfloat.h" struct BCD { typedef BCDFloat::Format Format; // Constructors BCD() {} BCD(const char* s) : _v(s) {} BCD(int4 v) : _v(v) {} BCD(uint4 v) : _v(v) {} BCD(const BCDFloatData& bf) : _v(bf) {} int exponent() const { return _v.exp(); } void setExponent(int v) { _v.exp(v); } int digit(int n) const { return _v.d_[n]; } void digit(int n, int v) { _v.d_[n] = v; } // Arithmetic friend BCD operator+(const BCD& a, const BCD& b) { BCD c; BCDFloat::add(&a._v, &b._v, &c._v); return c; } friend BCD operator-(const BCD& a, const BCD& b) { BCD c; BCDFloat::sub(&a._v, &b._v, &c._v); return c; } friend BCD operator*(const BCD& a, const BCD& b) { BCD c; BCDFloat::mul(&a._v, &b._v, &c._v); return c; } friend BCD operator/(const BCD& a, const BCD& b) { BCD c; BCDFloat::div(&a._v, &b._v, &c._v); return c; } void operator+=(const BCD& b) { BCD c; BCDFloat::add(&_v, &b._v, &c._v); _v = c._v; } void operator-=(const BCD& b) { BCD c; BCDFloat::sub(&_v, &b._v, &c._v); _v = c._v; } void operator*=(const BCD& b) { BCD c; BCDFloat::mul(&_v, &b._v, &c._v); _v = c._v; } void operator/=(const BCD& b) { BCD c; BCDFloat::div(&_v, &b._v, &c._v); _v = c._v; } BCD operator-() const { BCD c(*this); c.negate(); return c; } void operator++() { *this += 1; } void operator--() { *this -= 1; } friend int4 ifloor(const BCD& a) { return BCDFloat::ifloor(&a._v); } friend int4 itrunc(const BCD& a) { return BCDFloat::itrunc(&a._v); } friend BCD floor(const BCD& a) { /* floor, largest integer <= a. * eg floor(2.1) = 2. * floor(-2.1) = -3. */ BCD t; if (a.isSpecial()) return a; BCDFloat::floor(&a._v, &t._v); return t; } friend BCD trunc(const BCD& a) { /* truncate towards zero. * trunc(2.1) = 2. * trunc(-2.1) = -2 */ if (a.isSpecial()) return a; BCD t; BCDFloat::trunc(&a._v, &t._v); return t; } friend BCD fabs(const BCD& a) { return (a.isNeg()) ? -a : a; } friend BCD frac(const BCD& a) { if (a.isSpecial()) return a; return a - trunc(a); } const char* asString() const { _v.asString(_buf); return _buf; } static BCD epsilon(int n) { BCD v; BCDFloat::epsilon(n, &v._v); return v; } const char* asStringFmt(Format fmt, int precision) const; bool isZero() const { return _v.isZero(); } bool isNeg() const { return _v.neg(); } bool isSpecial() const { return _v.isSpecial(); } bool isInf() const { return _v.isInf(); } bool isNan() const { return _v.isNan(); } bool isInteger() const { return _v.isInteger(); } void negate() { _v.negate(); } BCD round(int digits) const { BCD rv; _v._roundDigits(digits, &rv._v); return rv; } static BCD inf() { return BCDFloat::posInf(); } // Comparision friend bool operator==(const BCD& a, const BCD& b) { return BCDFloat::equal(&a._v, &b._v); } friend bool operator!=(const BCD& a, const BCD& b) { return !BCDFloat::equal(&a._v, &b._v); } friend bool operator<(const BCD& a, const BCD& b) { return BCDFloat::lt(&a._v, &b._v); } friend bool operator<=(const BCD& a, const BCD& b) { return BCDFloat::le(&a._v, &b._v); } friend bool operator>(const BCD& a, const BCD& b) { return BCDFloat::gt(&a._v, &b._v); } friend bool operator>=(const BCD& a, const BCD& b) { return BCDFloat::ge(&a._v, &b._v); } BCDFloat _v; static char _buf[64]; }; BCD pow(const BCD& a, int4 n); BCD sqrt(const BCD&); inline bool operator==(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a == *(BCD*)&b; } inline bool operator<(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a < *(BCD*)&b; } inline bool operator<=(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a <= *(BCD*)&b; } inline bool operator>(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a > *(BCD*)&b; } inline bool operator>=(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a >= *(BCD*)&b; } inline BCD operator-(const BCDFloatData& a, const BCDFloatData& b) { return *(BCD*)&a - *(BCD*)&b; } #endif // __bcd_h__ free42-nologo-1.4.77/common/bcd2.h000644 000765 000024 00000015735 12110237247 017117 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __bcd2_h__ #define __bcd2_h__ #include "bcdfloat2.h" #include "bcd.h" struct BCD2 { // Constructors BCD2() {} BCD2(int4 v) : _v(v) {} BCD2(uint4 v) : _v(v) {} BCD2(const BCDFloatData2& bf) : _v(bf) {} BCD2(const BCDFloatData& bf) : _v(bf) {} BCD2(const BCD& b) : _v(b._v) {} int exponent() const { return _v.exp(); } void setExponent(int v) { _v.exp(v); } int digit(int n) const { return _v.d_[n]; } void digit(int n, int v) { _v.d_[n] = v; } BCD asBCD() const { BCD v; _v.asBCD(&v._v); return v; } // Arithmetic friend BCD2 operator+(const BCD2& a, const BCD2& b) { BCD2 c; BCDFloat2::add(&a._v, &b._v, &c._v); return c; } friend BCD2 operator-(const BCD2& a, const BCD2& b) { BCD2 c; BCDFloat2::sub(&a._v, &b._v, &c._v); return c; } friend BCD2 operator*(const BCD2& a, const BCD2& b) { BCD2 c; BCDFloat2::mul(&a._v, &b._v, &c._v); return c; } friend BCD2 operator/(const BCD2& a, const BCD2& b) { BCD2 c; BCDFloat2::div(&a._v, &b._v, &c._v); return c; } void operator+=(const BCD2& b) { BCD2 c; BCDFloat2::add(&_v, &b._v, &c._v); _v = c._v; } void operator-=(const BCD2& b) { BCD2 c; BCDFloat2::sub(&_v, &b._v, &c._v); _v = c._v; } void operator*=(const BCD2& b) { BCD2 c; BCDFloat2::mul(&_v, &b._v, &c._v); _v = c._v; } void operator/=(const BCD2& b) { BCD2 c; BCDFloat2::div(&_v, &b._v, &c._v); _v = c._v; } BCD2 operator-() const { BCD2 c(*this); c.negate(); return c; } void operator++() { *this += 1; } void operator--() { *this -= 1; } friend int4 ifloor(const BCD2& a) { return BCDFloat2::ifloor(&a._v); } friend int4 itrunc(const BCD2& a) { return BCDFloat2::itrunc(&a._v); } friend BCD2 floor(const BCD2& a) { /* floor, largest integer <= a. * eg floor(2.1) = 2. * floor(-2.1) = -3. */ BCD2 t; if (a.isSpecial()) return a; BCDFloat2::floor(&a._v, &t._v); return t; } friend BCD2 trunc(const BCD2& a) { /* truncate towards zero. * trunc(2.1) = 2. * trunc(-2.1) = -2 */ if (a.isSpecial()) return a; BCD2 t; BCDFloat2::trunc(&a._v, &t._v); return t; } friend BCD2 fabs(const BCD2& a) { return (a.isNeg()) ? -a : a; } friend BCD2 frac(const BCD2& a) { if (a.isSpecial()) return a; return a - trunc(a); } static BCD2 epsilon(int n) { BCD2 v; BCDFloat2::epsilon(n, &v._v); return v; } bool isZero() const { return _v.isZero(); } bool isNeg() const { return _v.neg(); } bool isSpecial() const { return _v.isSpecial(); } bool isInf() const { return _v.isInf(); } bool isNan() const { return _v.isNan(); } bool isInteger() const { return _v.isInteger(); } void negate() { _v.negate(); } void makeInf() { _v.makeInf(); } // Comparision friend bool operator==(const BCD2& a, const BCD2& b) { return BCDFloat2::equal(&a._v, &b._v); } friend bool operator!=(const BCD2& a, const BCD2& b) { return !BCDFloat2::equal(&a._v, &b._v); } friend bool operator<(const BCD2& a, const BCD2& b) { return BCDFloat2::lt(&a._v, &b._v); } friend bool operator<=(const BCD2& a, const BCD2& b) { return BCDFloat2::le(&a._v, &b._v); } friend bool operator>(const BCD2& a, const BCD2& b) { return BCDFloat2::gt(&a._v, &b._v); } friend bool operator>=(const BCD2& a, const BCD2& b) { return BCDFloat2::ge(&a._v, &b._v); } friend BCD2 pow(const BCD2& a, int4 n) { int4 m; if (n == 0) return 1; m = (n < 0) ? -n : n; BCD2 s; if (m > 1) { BCD2 r = a; s = 1; /* Use binary exponentiation */ for (;;) { if (m & 1) s *= r; m >>= 1; if (!m) break; r *= r; } } else { s = a; } /* Compute the reciprocal if n is negative. */ if (n < 0) return 1/s; return s; } BCDFloat2 _v; }; inline bool operator==(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a == *(BCD2*)&b; } inline bool operator<(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a < *(BCD2*)&b; } inline bool operator<=(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a <= *(BCD2*)&b; } inline bool operator>(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a > *(BCD2*)&b; } inline bool operator>=(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a >= *(BCD2*)&b; } inline BCD2 operator-(const BCDFloatData2& a, const BCDFloatData2& b) { return *(BCD2*)&a - *(BCD2*)&b; } inline BCD2 sqrt(const BCD2& a) { BCD2 c; if (!BCDFloat2::sqrt(&a._v, &c._v)) c._v.makeNAN(); return c; } #endif // __bcd2_h__ free42-nologo-1.4.77/common/bcdfloat.cc000644 000765 000024 00000106070 12110237247 020212 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "bcdfloat.h" int BCDDecade[4] = { 1000, 100, 10, 1 }; unsigned short BCDFloat::posInfD_[P+1] = { 0, 0, 0, 0, 0, 0, 0, POS_INF_EXP }; unsigned short BCDFloat::negInfD_[P+1] = { 0, 0, 0, 0, 0, 0, 0, 0xA000 }; unsigned short BCDFloat::nanD_[P+1] = { 0, 0, 0, 0, 0, 0, 0, 0x4000 }; void BCDFloat::_init() { for (int i = 0; i <= P; ++i) d_[i] = 0; } int bcd_round(unsigned short* d, int pn) { // round d_[P] into the mantissa if (d[pn] >= 5000) { int i = pn-1; int v = d[i] + 1; while (v >= BASE) { d[i] = (unsigned short)(v - BASE); if (!i) { // shift for (i = pn; i > 0; --i) d[i] = d[i-1]; d[0] = 1; return 1; } v = d[--i]+1; } d[i] = v; } return 0; } int bcd_round25(unsigned short* d, int pn) { // round d_[P] into the mantissa and mask off digits after 25. int i = pn-1; int v; if (d[0] < 10) v = d[i] + (d[pn] >= 5000); else if (d[0] < 100) v = ((((int4) d[i]+5)*3277)>>15)*10; else if (d[0] < 1000) v = ((((int4) d[i]+50)*5243)>>19)*100; else v = ((((int4) d[i]+500)*8389)>>23)*1000; while (v >= BASE) { d[i] = (unsigned short)(v - BASE); if (!i) { // shift for (i = pn; i > 0; --i) d[i] = d[i-1]; d[0] = 1; return 1; } v = d[--i]+1; } d[i] = v; return 0; } void bcd_uadd(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { int ea = GET_EXP(a, pn); int eb = GET_EXP(b, pn); int d = ea - eb; int i; if (d <= pn) // otherwise `b' is insignificant { int ca; int v; int j = pn-d; i = pn-1; // copy in first insignificant digit used by final rounding v = 0; if (d > 0) v = b[j]; c[pn] = v; ca = 0; while (j > 0) // perform addition of overlapping terms { v = a[i] + b[--j] + ca; ca = 0; if (v >= BASE) { v -= BASE; ca = 1; } c[i] = v; --i; } while (i >= 0) // remainder non-overlap terms { v = a[i] + ca; ca = 0; if (v >= BASE) { v -= BASE; ca = 1; } c[i] = v; --i; } if (ca) { /* overall carry, shift down and round */ for (i = pn; i > 0; --i) c[i] = c[i-1]; c[0] = ca; ++ea; } if (bcd_round25(c, pn)) ++ea; if (ea > EXPLIMIT) { // overflow for (i = 0; i < pn; ++i) c[i] = 0; ea = POS_INF_EXP; } SET_EXP(c, pn, ea); } else { for (i = 0; i <= pn; ++i) c[i] = a[i]; CLEAR_SIGN(c, pn); } } void bcd_usub(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { int ea = GET_EXP(a, pn); int eb = GET_EXP(b, pn); bool neg = false; int i; int d = ea - eb; if (d <= pn) { int ca; int v; int j = pn-d; i = pn-1; ca = 0; v = 0; /* first insignificant digit my be used for rounding */ if (d > 0) { v = b[j]; if (v) { v = BASE - v; ca = 1; } } c[pn] = v; while (j > 0) { v = a[i] - b[--j] - ca; ca = 0; if (v < 0) { ca = 1; v += BASE; } c[i] = v; --i; } if (ca) { while (i >= 0) { v = a[i]; if (v) { // carry absorbed c[i] = v - 1; // copy remainder while (i > 0) { --i; c[i] = a[i]; } // NB: i == 0 break; } else c[i--] = BASE-1; } if (i < 0) // carry all the way up { /* overall borrow, need to complement number */ for (i = pn; i >= 0; --i) { v = BASE-1 - c[i] + ca; ca = 0; if (v >= BASE) { ca = 1; v -= BASE; } c[i] = v; } neg = true; } } else { // copy remainder of number over while (i >= 0) { c[i] = a[i]; --i; } } int e = ea; i = 0; while (c[i] == 0 && i <= pn) i++; if (i > 0) { if (i == pn+1) { /* is zero */ e = 0; } else { e -= i; if (e <= -EXPLIMIT) { /* underflow */ c[0] = 0; e = 0; } else { int j; for (j = 0; j <= pn - i; j++) c[j] = c[j + i]; for (; j <= pn; j++) c[j] = 0; } } } if (bcd_round25(c, pn)) ++e; if (e > EXPLIMIT) // not sure this can happen { for (i = 0; i < pn; ++i) c[i] = 0; ea = POS_INF_EXP; } SET_EXP(c, pn, e); if (neg) NEGATE_SIGN(c, pn); } else { /* `b' is insignificant */ for (i = 0; i <= pn; ++i) c[i] = a[i]; CLEAR_SIGN(c, pn); } } void bcd_add(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { int i; if (GET_SPECIAL(a, pn) | GET_SPECIAL(b, pn)) { for (i = 0; i < pn; ++i) c[i] = 0; c[pn] = NAN_EXP; /* inf + inf = inf * -inf + (-inf) = -inf */ if (!(GET_NAN(a,pn) | GET_NAN(b,pn))) { if (GET_INF(a,pn)) { if (!GET_INF(b,pn) || (a[pn] == b[pn])) { // inf + x = inf c[pn] = a[pn]; } } else { // b is inf c[pn] = b[pn]; } } } else { if (GET_ZERO_NORM(a, pn)) { for (i = 0; i <= pn; ++i) c[i] = b[i]; return; } if (GET_ZERO_NORM(b,pn)) { for (i = 0; i <= pn; ++i) c[i] = a[i]; return; } int ea = GET_EXP(a,pn); int eb = GET_EXP(b,pn); bool na = GET_NEG_NORM(a,pn); bool nb = GET_NEG_NORM(b,pn); if (na != nb) // sub { if (ea >= eb) bcd_usub(a, b, c, pn); else { bcd_usub(b, a, c, pn); na = nb; } } else { if (ea >= eb) bcd_uadd(a, b, c, pn); else bcd_uadd(b, a, c, pn); } if (na) NEGATE_SIGN(c,pn); } } void bcd_sub(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { int i; if (GET_SPECIAL(a, pn) | GET_SPECIAL(b, pn)) { for (i = 0; i < pn; ++i) c[i] = 0; /* all others -> nan */ c[pn] = NAN_EXP; if (!(GET_NAN(a,pn) | GET_NAN(b,pn))) { if (GET_INF(a,pn)) { if (!GET_INF(b,pn) || (a[pn] != b[pn])) { /* -inf - (inf) = -inf * inf - (-inf) = inf */ c[pn] = a[pn]; } } else { // b is inf c[pn] = NEG_INF_EXP; } } } else { if (GET_ZERO_NORM(a, pn)) { for (i = 0; i <= pn; ++i) c[i] = b[i]; NEGATE_SIGN(c,pn); return; } if (GET_ZERO_NORM(b,pn)) { for (i = 0; i <= pn; ++i) c[i] = a[i]; return; } bool na = GET_NEG_NORM(a,pn); bool nb = GET_NEG_NORM(b,pn); int ea = GET_EXP(a,pn); int eb = GET_EXP(b,pn); if (na == nb) // sub { if (ea >= eb) { bcd_usub(a, b, c, pn); if (na) NEGATE_SIGN(c,pn); } else { bcd_usub(b, a, c, pn); if (!na) NEGATE_SIGN(c,pn); } } else { if (ea >= eb) bcd_uadd(a, b, c, pn); else bcd_uadd(b, a, c, pn); if (na) NEGATE_SIGN(c,pn); } } } void bcd_mul(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { // zero flags assuming not special bool az = GET_ZERO_NORM(a,pn); bool bz = GET_ZERO_NORM(b,pn); // neg flag assuming non-zero and non-special int na = GET_NEG_BIT(a,pn); int nb = GET_NEG_BIT(b,pn); int i; for (i = 0; i <= pn; ++i) c[i] = 0; if (GET_SPECIAL(a,pn) | GET_SPECIAL(b,pn)) { /* all others -> nan */ c[pn] = NAN_EXP; if (!(GET_NAN(a,pn) | GET_NAN(b,pn))) { if ((GET_INF(a,pn) && (GET_INF(b,pn) || (!bz))) || !az) { // inf * inf -> inf // inf * non-zero -> inf // non-zero * inf -> inf c[pn] = POS_INF_EXP; if (na != nb) NEGATE_SIGN(c, pn); } } } else if (!az && !bz) { int ca; int i, j; int4 u, v; int ea = GET_EXP(a,pn); int eb = GET_EXP(b,pn); unsigned short acc[MAX_P+1]; for (i = 0; i <= pn; ++i) acc[i] = 0; for (i = pn-1; i >= 0; --i) { bcd_round(c, pn); for (j = pn; j > 0; --j) c[j] = c[j-1]; c[0] = 0; u = a[i]; if (!u) continue; ca = 0; for (j = pn; j > 0; --j) { v = b[j-1] * u + ca; ca = 0; if (v >= BASE) { ca = v / BASE; v = v - ca*BASE; } acc[j] = v; } acc[0] = ca; /* now add acc into c */ ca = 0; for (j = pn; j >= 0; --j) { v = c[j] + acc[j] + ca; ca = 0; if (v >= BASE) { ca = 1; v -= BASE; } c[j] = v; } /* won't be any overall carry */ } if (!c[0]) { for (i = 0; i < pn; ++i) c[i] = c[i+1]; c[pn] = 0; } else { ++ea; if (bcd_round25(c,pn)) ++ea; } ea += eb - 1; if (ea <= -EXPLIMIT || ea > EXPLIMIT) { for (i = 0; i <= pn; ++i) c[i] = 0; // underflow if (ea > EXPLIMIT) c[pn] = POS_INF_EXP; // overflow } else { SET_EXP(c,pn,ea); /* fix sign */ if (na != nb) NEGATE_SIGN(c,pn); } } } void bcd_div(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn) { int as = GET_SPECIAL(a,pn); int bs = GET_SPECIAL(b,pn); // zero flags assuming not special int az = GET_ZERO_NORM(a,pn) && !as; int bz = GET_ZERO_NORM(b,pn) && !bs; // neg assuming non-special int na = GET_NEG_BIT(a,pn) && !az; int nb = GET_NEG_BIT(b,pn) && !bz; int i; if (as | bs | az | bz) { for (i = 0; i < pn; ++i) c[i] = 0; /* all others -> nan */ c[pn] = NAN_EXP; if (!(GET_NAN(a,pn) | GET_NAN(b,pn))) { if (GET_INF(a,pn) | bz) { // inf/inf -> nan if (!GET_INF(b,pn) && !az) { // inf/x -> inf if (na == nb) c[pn] = POS_INF_EXP; else c[pn] = NEG_INF_EXP; } } else if (GET_INF(b,pn) | az) { // x/inf -> 0 c[pn] = 0; } } } else { int4 u, v; int ca; int j = 0; int4 q; int ea = GET_EXP(a,pn); int eb = GET_EXP(b,pn); unsigned short acc[MAX_P+1]; unsigned short b1[MAX_P+1]; u = BASE/(b[0]+1); if (u != 1) { /* prenormialise `a' and move into acc using spare digit */ ca = 0; for (i = pn; i > 0; --i) { v = a[i-1]*u + ca; ca = 0; if (v >= BASE) { ca = v/BASE; v -= ca*BASE; } acc[i] = v; } acc[0] = ca; /* prenormalise `b' into b1 */ ca = 0; for (i = pn-1; i >= 0; --i) { v = b[i]*u + ca; ca = 0; if (v >= BASE) { ca = v/BASE; v -= ca*BASE; } b1[i] = v; } } else { /* u is often 1 */ for (i = pn-1; i >= 0; --i) { acc[i+1] = a[i]; b1[i] = b[i]; } acc[0] = 0; } for (;;) { if (acc[0] == b1[0]) q = BASE-1; else { v = acc[0]*((int4)BASE) + acc[1]; q = v/b1[0]; while (b1[1]*q > ((v - q*b1[0])*BASE + acc[2])) --q; } if (!q && !j) { /* first quotient digit is zero. can gain extra * accuracy by ignoring this and adjusting exponent. */ --ea; } else { if (j == pn) { c[j] = q; break; } ca = 0; for (i = pn; i > 0; --i) { v = acc[i] - b1[i-1]*q - ca; ca = 0; if (v < 0) { ca = (-v + BASE-1)/BASE; v += ca*BASE; } acc[i] = v; } v = acc[0] - ca; if (v) { /* the infamous add back correction */ ca = 0; for (i = pn; i > 0; --i) { v = acc[i] + b1[i-1] + ca; ca = 0; if (v >= BASE) { ca = 1; v -= BASE; } acc[i] = v; } q--; } if (q == 0 && j == 0) --ea; else c[j++] = q; } // left shift for (i = 0; i < pn; ++i) acc[i] = acc[i+1]; acc[pn] = 0; } if (bcd_round25(c, pn)) ++ea; ea -= eb - 1; if (ea <= -EXPLIMIT || ea > EXPLIMIT) { for (i = 0; i <= pn; ++i) c[i] = 0; if (ea > EXPLIMIT) c[pn] = POS_INF_EXP; } else SET_EXP(c, pn, ea); if (na != nb) NEGATE_SIGN(c, pn); } } int bcd_cmp(const unsigned short* a, const unsigned short* b, int pn) { if (GET_NAN(a,pn) || GET_NAN(b,pn)) { // NaNs are not equal to anything, even themselves; // ALL comparisions involving them should return 'false' // (except '!=', which should return 'true'). // So, I return a special value here. return 2; } // zero flags assuming not special int az = GET_ZERO_NORM(a,pn) && !GET_INF(a,pn); int bz = GET_ZERO_NORM(b,pn) && !GET_INF(b,pn); // neg int na = GET_NEG_BIT(a,pn) && !az; int nb = GET_NEG_BIT(b,pn) && !bz; if (na != nb) return na ? -1 : 1; // If a and b are both negative, switch them. This way, I can // ignore signs in the remainder of this method. if (na) { const unsigned short* t = a; a = b; b = t; } if (GET_INF(a, pn)) return GET_INF(b,pn) ? 0 : 1; if (GET_INF(b,pn)) return -1; if (az) return bz ? 0 : -1; if (bz) return 1; int ea = GET_EXP(a,pn); int eb = GET_EXP(b,pn); if (ea != eb) return ea < eb ? -1 : 1; for (int i = 0; i < pn; i++) { int da = a[i]; int db = b[i]; if (da != db) return da < db ? -1 : 1; } return 0; } void bcd_fromUInt(unsigned short* d, int pn, uint4 v) { /* quicker to deal with cases separately */ if (v < BASE) { d[0] = v; d[pn] = 1; } else if (v < ((int4) BASE)*BASE) { d[0] = (unsigned short)(v/BASE); d[1] = (unsigned short)(v - d[0]*BASE); d[pn] = 2; } else { d[0] = (unsigned short)(v/(((int4) BASE)*BASE)); v -= d[0]*(((int4) BASE)*BASE); d[1] = (unsigned short)(v/BASE); d[2] = (unsigned short)(v - d[1]*BASE); d[pn] = 3; } } BCDFloat::BCDFloat(const char* s) { /* make a BCD float from a string. */ _init(); bool neg = false; if (*s == '-') { neg = true; ++s; } int i; int j = 0; int e = 0; /* find the end of the input string */ const char* endp = s; const char* startp = 0; const char* point = 0; bool begun = false; while (*endp && !ISEXP(*endp)) { if (*endp == '.') { if (point) break; point = endp; if (!startp) { startp = endp; begun = true; } } else { if (*endp < '0' || *endp > '9') break; if (*endp != '0' && !begun) { startp = endp; begun = true; } } ++endp; } if (!startp) { // check for Inf before fail //if (!strnicmp(s, "inf", 3)) if ((s[0] == 'i' || s[0] == 'I') && (s[1] == 'n' || s[1] == 'N') && (s[2] == 'f' || s[2] == 'F')) { *this = posInf(); if (neg) negate(); } return; } bool eneg = false; if (startp) { if (ISEXP(*endp)) { const char* p = endp + 1; if (*p == '-') { eneg = true; ++p; } else if (*p == '+') ++p; // allow E+1234 e = 0; char c; // Skip leading zeroes while ((c = *p++) == '0'); // Accept up to 6 digits; the reason why I don't stop after // 5 digits is because I want to make sure that 0.1e100000 // is converted to Inf, and not 1e9999 for (i = 0; i < 6; ++i) { if (c >= '0' && c <= '9') e = e * 10 + c - '0'; else break; c = *p++; } if (eneg) e = -e; } /* represent the decimal point by adjusting the exponent */ if (point) e += (int)(point - startp); else e += (int)(endp - startp); /* calculate the decade offset of the exponent remainder */ if (e >= 0) { i = (e&3); if (i) i = 4-i; } else i = ((-e) & 3); e += i; /* convert to 4dec */ e >>= 2; const char* p = startp; bool leading_zero = true; while (p != endp) { int d = 0; while (i < 4) { if (*p != '.') { d += (*p - '0')*BCDDecade[i]; ++i; } ++p; if (p == endp) break; } i = 0; if (leading_zero && d == 0) { e--; continue; } leading_zero = false; d_[j] = d; if (++j > P) // full up. break; } if (leading_zero) e = 0; while (j <= P) d_[j++] = 0; if (_round25()) e++; if (e <= -EXPLIMIT) _init(); else { if (e > EXPLIMIT) *this = posInf(); else exp(e); if (neg) negate(); } } } BCDFloat::BCDFloat(int8 n) { _init(); int i, z = 5; bool neg = n < 0; if (neg) n = -n; for (i = 4; i >= 0; i--) { int d = (int) (n % BASE); d_[i] = d; n /= BASE; if (d != 0) z = i; } if (z < 5) { if (z > 0) { for (i = 0; i < 5 - z; i++) d_[i] = d_[i + z]; for (i = 5 - z; i < 5; i++) d_[i] = 0; } d_[P] = 5 - z; if (neg) negate(); } } BCDFloat::BCDFloat(double d) { if (isnan(d)) { *this = BCDFloat::nan(); return; } if (isinf(d)) { *this = d > 0 ? BCDFloat::posInf() : BCDFloat::negInf(); return; } // Return exact results if d is representable exactly as a long long. // TODO: there are additional cases we should try to handle exactly, e.g. // 0.375. Basically, the idea would be to do the conversion by summing // exact equivalents for the values of d's nonzero bits, just like the // way the table-based binary-to-bcd conversion in the Free42 Binary // build works. This approach returns exact results for a very large set // of cases, including all representable integers. int8 n = (int8) d; if (d == n) { *this = BCDFloat(n); return; } bool neg = d < 0; if (neg) d = -d; if (d == 0) { _init(); } else { int exp = (((int) ::floor(log10(d))) & ~3) >> 2; d_[P] = (exp + 1) & EXPMASK; double m = pow(100.0, (double) exp); d /= m; d /= m; for (int i = 0; i < P; i++) { short s = (short) d; d_[i] = s; d = (d - s) * 10000; } if (neg) negate(); } if (_round25()) { // increase exponent int e = exp(); ++e; if (e == EXPLIMIT) { // Out of range; return Inf *this = !neg ? posInf() : negInf(); } else { exp(e); } } } void BCDFloat::_roundDigits(unsigned int precision, BCDFloat* v) const { if (!isSpecial() && !isZero() && precision < 25) { bool n = neg(); BCDFloat rv; // adjust precision to number of digits before 4radix. if (d_[0] >= 10) --precision; if (d_[0] >= 100) --precision; if (d_[0] >= 1000) --precision; --precision; // units int m = BCDDecade[precision & 3] * 5; rv.ldexp(m, exp() - (precision>>2) - 1); // ignores signs _uadd(this, &rv, v); // restore sign if needed if (n) v->setSign(); } else *v = *this; } void BCDFloat::_asString(char* buf, Format fmt, int precision) const { char* p = buf; if (isSpecial()) { if (isNan()) strcpy(p, "NaN"); else { // inf if (neg()) *p++ = '-'; strcpy(p, "Inf"); } } else { int i; bool scimode = (fmt & format_scimode) != 0; if (neg()) *p++ = '-'; char* point = 0; int v; int eadj = 0; int e = exp(); int pr = 0; // significant digits printed i = 0; if (e > 0) { /* if the exponent is less than our digits, we can * print out the number like an integer. */ if (e <= 5 && !scimode) { while (i < e) { v = d_[i]; if (!i) sprintf(p, "%d", v); else sprintf(p, "%04d", v); while (*p) { ++p; ++pr; } ++i; } e -= i; } else { /* otherwise we have a larger number. print out * as scientific form. */ v = d_[0]; char tb[8]; sprintf(tb, "%d", v); char* q = tb; *p++ = *q++; ++pr; point = p; *p++ = '.'; while (*q) { *p++ = *q++; ++pr; ++eadj; } ++i; --e; } } else { /* otherwise have small number */ *p++ = '0'; *p = 0; } // here we have i = number of 4digits printed // e = remains of exponent int n = P; while (!d_[n-1] && n > i) --n; if (i < n) { if (!point) { point = p; *p++ = '.'; } for (; i < n; ++i) { v = d_[i]; sprintf(p, "%04d", v); pr += 4; p += 4; if (precision && pr >= precision) { int over = pr - precision; p -= over; *p = 0; break; } } } /* tidy up */ if (point) while (p > point && (p[-1] == '0' || p[-1] == '.')) *--p = 0; // print exponent if (e || scimode) #ifdef CUSTOM_EXP_CHAR sprintf(p, "%c%d", CUSTOM_EXP_CHAR, e*4+eadj); #else sprintf(p, "e%d", e*4+eadj); #endif } } static unsigned int isqrt(unsigned int v) { /* integer root for x<= 9999 */ unsigned int x = 0; int b = 1<<7; do { x ^= b; if (x*x > v) x ^= b; } while (b >>=1); return x; } bool BCDFloat::sqrt(const BCDFloat* a, BCDFloat* r) { if (a->neg() || a->isNan()) return false; if (a->isInf()) { *r = *a; // sqrt(inf) = inf return true; } int rs; int4 v; int rodd; int4 q; // acc, t and u get one more digit of workspace. unsigned short acc[P+2]; int as; unsigned short t[P+2]; int ts; unsigned short u[P+2]; int us = 0; r->_init(); int e = a->exp(); v = isqrt(a->d_[0]); rodd = !(e & 1); r->d_[0] = v; if (rodd) r->d_[0] *= 100; rs = 1; as = 0; acc[0] = a->d_[0] - v*v; if (acc[0]) ++as; int i; int j = 1; int4 ca; for (;;) { /* bring in the next digit */ acc[as] = j < P ? a->d_[j++] : 0; q = 0; if (acc[0]) { ++as; /* t = 200*r if even, t=2*r if odd */ int m = rodd ? 2 : 200; ca = 0; ts = rs; for (i = rs; i > 0; --i) { v = ((int4) r->d_[i-1])*m + ca; ca = 0; if (v >= BASE) { ca = v/BASE; v -= ca*((int4)BASE); } t[i] = v; } t[0] = ca; ++ts; while (!t[0]) { for (i = 0; i < ts; ++i) t[i] = t[i+1]; --ts; } if (ts > P+1) { /* rarely, the tempory size can become bigger than * we can handle. this can only happen on the last * digit. if so, stop. */ break; } q = 0; if (ts == as) { if (ts > 1) q = (((int4) acc[0])*((int4)BASE) + acc[1])/(((int4) t[0])*((int4)BASE)+t[1]); else q = ((int4)acc[0])/t[0]; } else if (as > ts) { q = (((int4) acc[0])*((int4)BASE) + acc[1])/t[0]; } if (q) { if (q > 99) q = 99; /* t = t + q */ t[ts-1] += q; // cant carry for (;;) { /* u = t*q */ ca = 0; us = ts; for (i = ts; i > 0; --i) { v = t[i-1]*q + ca; ca = 0; if (v >= BASE) { ca = v/BASE; v -= ca*((int4)BASE); } u[i] = v; } u[0] = ca; if (ca) ++us; else for (i = 0; i < us; ++i) u[i] = u[i+1]; /* determine whether u > acc. if so then q was too * big. */ bool fail = us > as; if (!fail && us == as) { for (i = 0; i < as; ++i) { int d = u[i] - acc[i]; if (d > 0) { fail = true; break; } else if (d < 0) break; } } if (!fail) break; /* decrease q by 1 and try again */ q -= 1; --t[ts-1]; // adjust for new q } } } if (rodd) { /* can accommodate 2 more digits in current size */ r->d_[rs-1] += q; rodd = 0; if (rs == P+1) break; } else { r->d_[rs++] = q*100; rodd = 1; } if (q) { /* acc = acc - u. * wont borrow because u <= acc. */ int k; ca = 0; k = us; for (i = as-1; i >= 0; --i) { v = acc[i] - ca; if (k > 0) v -= u[--k]; ca = 0; if (v < 0) { v += BASE; ca = 1; } acc[i] = v; } while (!acc[0]) { for (i = 0; i < as; ++i) acc[i] = acc[i+1]; if (!--as) break; } } } e = e >= -1 ? (e + 1) / 2 : e / 2; if (r->_round25()) ++e; r->exp(e); return true; } bool BCDFloat::trunc(const BCDFloat* a, BCDFloat* c) { /* truncate towards zero. * trunc(2.1) = 2. * trunc(-2.1) = -2 */ *c = *a; int e = c->exp(); int i; for (i = P-1; i >= 0; --i) if (i >= e) c->d_[i] = 0; return true; } bool BCDFloat::floor(const BCDFloat* a, BCDFloat* c) { /* floor, largest integer <= a. * eg floor(2.1) = 2. * floor(-2.1) = -3. */ *c = *a; int e = c->exp(); int i; bool changed = false; for (i = P-1; i >= 0; --i) { if (i >= e) { if (c->d_[i]) changed = true; c->d_[i] = 0; } } if (c->neg() && changed) { /* need to subtract 1 */ for (i = P-1; i >= 0; --i) { if (c->d_[i]) { ++c->d_[i]; break; } } } return true; } int4 BCDFloat::asInt() const { // if we fit in an int, return it otherwise 0 int ea = exp(); int4 v = 0; int i = 0; while (i < ea && i < P) { if (v > 214748L) return 0; // too large, bail out. v*= BASE; v += d_[i]; ++i; } if (neg()) v = -v; return v; } bool BCDFloat::isInteger() const { if (isZero()) return true; int e = exp(); int i; for (i = P-1; i >= 0; --i) if (d_[i]) return e > i; return false; } void BCDFloat::mul2(const unsigned short* ad, int ea, const unsigned short* bd, int eb, unsigned short* cd, int& ec) { int ca; int i, j; int4 u, v; unsigned short acc[2*P+1]; for (i = 0; i < 2*P; ++i) cd[i] = 0; int cc = 0; for (i = 2*P-1; i >= 0; --i) { for (j = 2*P; j > 0; --j) cd[j] = cd[j-1]; cd[0] = cc; cc = 0; u = ad[i]; if (!u) continue; ca = 0; for (j = 2*P; j > 0; --j) { v = bd[j-1]*u + ca; ca = 0; if (v >= BASE) { ca = v / BASE; v = v - ca*((int4)BASE); } acc[j] = v; } acc[0] = ca; /* now add acc into c */ for (j = 2*P; j >= 0; --j) { v = cd[j] + acc[j] + cc; cc = 0; if (v >= BASE) { cc = 1; v -= BASE; } cd[j] = v; } } cd[0] += cc; // carry? ec = ea + eb; } free42-nologo-1.4.77/common/bcdfloat.h000644 000765 000024 00000026250 12110237247 020055 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __bcdfloat_h__ #define __bcdfloat_h__ #include "free42.h" #define NEG 0x8000 #define P 7 #define BASE 10000 #define EXPLIMIT (BASE/4) #define EXPMASK 0x1fff #define EXPMASKNEG (NEG|EXPMASK) /** * Here is the BCD float class. * * the BCD float is made of `P' 4dec digits. a 4dec digit is a chunk * of 4 decimal digits stored a a machine `short'. rather than use * a nibble per digit and base 10, we are using base 10,000 and thus * work with 4 decimal digits in one go. * * this is much more efficient because, not only is it faster, but * we are able to leverage the 16*16->32 and 32/16->16 multiply and * divide integer operations found on most modern processors as well * as 16 bit add & subtract. * * the exponent is stored in the last slot (ie P+1) and ranges from * -10000 to +9999. the mantissa is stored unsigned and the sign is the * top bit of the exponent. * * there are a number of important considerations for this representation: * * the postion of the radix must always correspond with the end of a 4dec * word. therefore up to one extra 4dec is wasted in the representation. * for example, the number 12345.67 will store as [0001][2345].[6700] and * utilise 3 4decs rather than two. this is the drawback of using 4decs. * bytes would waste up to 1 nibble and nibbles dont waste anything which * is why they are normally chosen for short digit (eg 6) representations. * for long digit (eg 20) this method is preferable. * * whence we take P=6 which always has the capacity for 21 decimal digits * of mantissa (and up to 24 when it gets lucky) and at P=7 we have * 1 spare 4dec word. * * the trick of storing the mantissa as the P+1'th slot is cunning. * most of the arithmetic proceeds by extracting the sign and exponent * before working with the mantissa. thus for the purposes of the internal * calculations, we can let the mantissa spill out into the last `digit' * and effectively have one extra digit of working precision. this is * especially useful for making sure we get the correct rounding at the * end when we put back the exponent. and also for accommodating any overall * numeric carry overflow before the number is shifted down. * * it has been suggested that, rather than use an extra 16 bit word, 5 words * are used for 20 digits without waste. this is possible but it involves * multiplying all 5 terms by 10, 100 or 1000 with carry, in order to align * the radix for operation. i didnt want this overhead, but it is true that * it would result in smaller representation. but dont forget that the * overall representation in size will be rounded up to a multiple of 4 bytes * anyway. * * lastly, floating point code is notorious for hiding bugs, often for years. * this code is new and there will be some "simple" cases that come out * plain wrong. thats the way with floating point. even CPU makers get it * wrong sometimes. */ #ifdef CUSTOM_EXP_CHAR #define ISEXP(_c) \ ((_c) == 'e' || (_c) == 'E' || (_c) == CUSTOM_EXP_CHAR) #else #define ISEXP(_c) \ ((_c) == 'e' || (_c) == 'E') #endif struct BCDFloatData { /* store P 4dec `digits', equivalent to P*4 decimal digits. * the last place is the exponent. */ unsigned short d_[P+1]; }; #define MAX_P 2*(P+1) #define POS_INF_EXP 0x2000 #define NEG_INF_EXP 0xA000 #define NAN_EXP 0x4000 #define GET_EXP(_d, _p) (((short)((_d)[_p] << 3)) >> 3) // #define SET_EXP(_d, _p, _v) ((_d)[_p] = (_v)) <- wrong, doesn't handle (POS|NEG)_INF_EXP #define SET_EXP(_d, _p, _v) ((_d)[_p] = (_v) == POS_INF_EXP || (_v) == NEG_INF_EXP ? (_v) : (_v) & EXPMASK) #define CLEAR_SIGN(_d, _p) ((_d)[_p] &= ~NEG) #define NEGATE_SIGN(_d, _p) ((_d)[_p] ^= NEG) #define GET_SPECIAL(_d, _p) ((_d)[_p]&0x6000) #define GET_NAN(_d, _p) ((_d)[_p]&NAN_EXP) #define GET_INF(_d, _p) ((_d)[_p]&0x2000) #define GET_NEG_BIT(_d, _p) ((_d)[_p]&NEG) // zero assuming normal (non-special) #define GET_ZERO_NORM(_d, _p) ((_d)[0] == 0) // is zero generally #define IS_ZERO(_d, _p) \ (GET_ZERO_NORM(_d, _p) && GET_SPECIAL(_d, _p) == 0) // negative assuming non-special #define GET_NEG_NORM(_d, _p) (GET_NEG_BIT(_d, _p) && !GET_ZERO_NORM(_d,_p)) int bcd_round(unsigned short* d, int pn); int bcd_round25(unsigned short* d, int pn); void bcd_uadd(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); void bcd_usub(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); void bcd_add(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); void bcd_sub(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); void bcd_mul(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); void bcd_div(const unsigned short* a, const unsigned short* b, unsigned short* c, int pn); int bcd_cmp(const unsigned short* a, const unsigned short* b, int pn); void bcd_fromUInt(unsigned short* d, int pn, uint4 v); extern int BCDDecade[4]; struct BCDFloat: public BCDFloatData { enum Format { format_normal = 0, format_scimode = 1, format_truncate = 2, // no rounding }; BCDFloat(int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7) { d_[0] = d0; d_[1] = d1; d_[2] = d2; d_[3] = d3; d_[4] = d4; d_[5] = d5; d_[6] = d6; d_[7] = d7; } BCDFloat() {} // Warning: not initialised. BCDFloat(const char* s); BCDFloat(int4 v) { _init(); if (v) { bool neg = v < 0; if (neg) v = -v; _fromUInt((uint4)v); if (neg) negate(); } } BCDFloat(int8); BCDFloat(double); BCDFloat(uint4 v) { _init(); if (v) _fromUInt(v); } BCDFloat(const BCDFloatData& d) { *this = *(BCDFloat*)&d; } // Features void asString(char* buf) const { _asString(buf, format_normal, 0); } void asStringFmt(char* buf, Format fmt, int precision) const { _asString(buf, fmt, precision); } int exp() const { return GET_EXP(d_, P); } void exp(int v) { SET_EXP(d_, P, v); } bool neg() const { return (d_[P]& NEG) && (d_[0] != 0 || isInf()); } void setSign() { d_[P] |= NEG; } void clearSign() { CLEAR_SIGN(d_, P); } void negate() { NEGATE_SIGN(d_, P); } bool isSpecial() const { return GET_SPECIAL(d_,P) != 0; } bool isZero() const { return IS_ZERO(d_, P); } bool isNan() const { return GET_NAN(d_,P) != 0; } bool isInf() const { return GET_INF(d_,P) != 0; } bool isInteger() const; void ldexp(unsigned int mant, int e) { // load the exp as a 4-block _init(); _fromUInt(mant); exp(e); } static void add(const BCDFloat* a, const BCDFloat* b, BCDFloat* c) { bcd_add(a->d_, b->d_, c->d_, P); } static void sub(const BCDFloat* a, const BCDFloat* b, BCDFloat* c) { bcd_sub(a->d_, b->d_, c->d_, P); } static void mul(const BCDFloat* a, const BCDFloat* b, BCDFloat* c) { bcd_mul(a->d_, b->d_, c->d_, P); } static void div(const BCDFloat* a, const BCDFloat* b, BCDFloat* c) { bcd_div(a->d_, b->d_, c->d_, P); } static bool sqrt(const BCDFloat* a, BCDFloat* ra); static bool lt(const BCDFloat* a, const BCDFloat* b) { /* true iff a < b */ return bcd_cmp(a->d_, b->d_, P) < 0; } static bool le(const BCDFloat* a, const BCDFloat* b) { /* true iff a <= b */ return bcd_cmp(a->d_, b->d_, P) <= 0; } static bool gt(const BCDFloat* a, const BCDFloat* b) { /* true iff a > b */ return bcd_cmp(a->d_, b->d_, P) == 1; } static bool ge(const BCDFloat* a, const BCDFloat* b) { /* true iff a >= b */ int res = bcd_cmp(a->d_, b->d_, P); return res == 0 || res == 1; } static bool equal(const BCDFloat* a, const BCDFloat* b) { return bcd_cmp(a->d_, b->d_, P) == 0; } static int4 ifloor(const BCDFloat* x) { BCDFloat a; floor(x, &a); return a.asInt(); } static int4 itrunc(const BCDFloat* x) { BCDFloat a; trunc(x, &a); return a.asInt(); } static bool floor(const BCDFloat* a, BCDFloat* c); static bool trunc(const BCDFloat* a, BCDFloat* c); void _init(); static void _uadd(const BCDFloat* a, const BCDFloat* b, BCDFloat* c) { bcd_uadd(a->d_, b->d_, c->d_, P); } int _round25() { return bcd_round25(d_, P); } void _asString(char* buf, Format fmt, int precision) const; void _fromUInt(uint4 v) { bcd_fromUInt(d_, P, v); } void _roundDigits(unsigned int precision, BCDFloat* v) const; static const BCDFloat& posInf() { return *(BCDFloat*)posInfD_; } static const BCDFloat& negInf() { return *(BCDFloat*)negInfD_; } static const BCDFloat& nan() { return *(BCDFloat*)nanD_; } static void epsilon(int n, BCDFloat* v) { // generate 10^-n, int m = BCDDecade[(n-1) & 3]; v->ldexp(m, -(n>>2)); } static void mul2(const unsigned short* ad, int ea, const unsigned short* bd, int eb, unsigned short* cd, int& ec); int4 asInt() const; static unsigned short posInfD_[P+1]; static unsigned short negInfD_[P+1]; static unsigned short nanD_[P+1]; }; #endif free42-nologo-1.4.77/common/bcdfloat2.cc000644 000765 000024 00000006326 12110237247 020277 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "bcdfloat2.h" void BCDFloat2::_init() { for (int i = 0; i <= P2; ++i) d_[i] = 0; } bool BCDFloat2::trunc(const BCDFloat2* a, BCDFloat2* c) { /* truncate towards zero. * trunc(2.1) = 2. * trunc(-2.1) = -2 */ *c = *a; int e = c->exp(); int i; for (i = P2-1; i >= 0; --i) if (i >= e) c->d_[i] = 0; return true; } bool BCDFloat2::floor(const BCDFloat2* a, BCDFloat2* c) { /* floor, largest integer <= a. * eg floor(2.1) = 2. * floor(-2.1) = -3. */ *c = *a; int e = c->exp(); int i; bool changed = false; for (i = P2-1; i >= 0; --i) { if (i >= e) { if (c->d_[i]) changed = true; c->d_[i] = 0; } } if (c->neg() && changed) { /* need to subtract 1 */ for (i = P2-1; i >= 0; --i) { if (c->d_[i]) { ++c->d_[i]; break; } } } return true; } int4 BCDFloat2::asInt() const { // if we fit in an int, return it otherwise 0 int ea = exp(); int4 v = 0; int i = 0; while (i < ea && i < P2) { if (v > 214748L) return 0; // too large, bail out. v*= BASE; v += d_[i]; ++i; } if (neg()) v = -v; return v; } bool BCDFloat2::isInteger() const { if (isZero()) return true; int e = exp(); int i; for (i = P2-1; i >= 0; --i) if (d_[i]) return e > i; return false; } bool BCDFloat2::sqrt(const BCDFloat2* a, BCDFloat2* r) { // use single precision version as approx. BCDFloat a1; BCDFloat r1; a->asBCD(&a1); bool res = BCDFloat::sqrt(&a1, &r1); BCDFloat2 rr(r1); BCDFloat2 t, t2; BCDFloat2 two(2); if (res) { if (!rr.isSpecial()) { for (int i = 0; i < 3; ++i) { div(a,&rr,&t); // t = a/x add(&t, &rr, &t2); // t2 = (a/x) + x div(&t2, &two, &rr); // x = (t2)/2 } } *r = rr; } return res; } free42-nologo-1.4.77/common/bcdfloat2.h000644 000765 000024 00000012712 12110237247 020135 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __bcdfloat2_h__ #define __bcdfloat2_h__ #include "bcdfloat.h" #define P2 (2*P+1) struct BCDFloatData2 { /* store P 4dec `digits', equivalent to P*4 decimal digits. * the last place is the exponent. */ unsigned short d_[P2+1]; }; struct BCDFloat2: public BCDFloatData2 { BCDFloat2() {} // Warning: not initialised. BCDFloat2(int4 v) { _init(); if (v) { bool neg = v < 0; if (neg) v = -v; _fromUInt((uint4)v); if (neg) negate(); } } BCDFloat2(uint4 v) { _init(); if (v) _fromUInt(v); } BCDFloat2(const BCDFloatData2& d) { *this = *(BCDFloat2*)&d; } BCDFloat2(const BCDFloatData& d) { int i; for (i = 0; i < P; ++i) d_[i] = d.d_[i]; while (i < P2) d_[i++] = 0; d_[P2] = d.d_[P]; } // Features int exp() const { return ((short)(d_[P2] << 3)) >> 3; } void exp(int v) { d_[P2] = v & EXPMASK; } bool neg() const { return (d_[P2]& NEG) && (d_[0] != 0 || isInf()); } void setSign() { d_[P2] |= NEG; } void clearSign() { d_[P2] &= ~NEG; } void negate() { d_[P2] ^= NEG; } bool isSpecial() const { return (d_[P2]&0x6000) != 0; } bool isZero() const { return d_[0] == 0 && !isSpecial(); } bool isNan() const { return (d_[P2]&0x4000) != 0; } bool isInf() const { return (d_[P2]&0x2000) != 0; } bool isInteger() const; void asBCD(BCDFloat* v) const { for (int i = 0; i <= P; ++i) v->d_[i] = d_[i]; int e = d_[P2]; if (v->_round25()) ++e; // XX overflow? v->d_[P] = e; } void ldexp(unsigned int mant, int e) { // load the exp as a 4-block _init(); _fromUInt(mant); exp(e); } static void add(const BCDFloat2* a, const BCDFloat2* b, BCDFloat2* c) { bcd_add(a->d_, b->d_, c->d_, P2); } static void sub(const BCDFloat2* a, const BCDFloat2* b, BCDFloat2* c) { bcd_sub(a->d_, b->d_, c->d_, P2); } static void mul(const BCDFloat2* a, const BCDFloat2* b, BCDFloat2* c) { bcd_mul(a->d_, b->d_, c->d_, P2); } static void div(const BCDFloat2* a, const BCDFloat2* b, BCDFloat2* c) { bcd_div(a->d_, b->d_, c->d_, P2); } void makeInf() { _init(); d_[P2] = POS_INF_EXP; } void makeNAN() { _init(); d_[P2] = NAN_EXP; } static bool sqrt(const BCDFloat2* a, BCDFloat2* ra); static bool lt(const BCDFloat2* a, const BCDFloat2* b) { /* true iff a < b */ return bcd_cmp(a->d_, b->d_, P2) < 0; } static bool le(const BCDFloat2* a, const BCDFloat2* b) { /* true iff a <= b */ return bcd_cmp(a->d_, b->d_, P2) <= 0; } static bool gt(const BCDFloat2* a, const BCDFloat2* b) { /* true iff a > b */ return bcd_cmp(a->d_, b->d_, P2) == 1; } static bool ge(const BCDFloat2* a, const BCDFloat2* b) { /* true iff a >= b */ int res = bcd_cmp(a->d_, b->d_, P2); return res == 0 || res == 1; } static bool equal(const BCDFloat2* a, const BCDFloat2* b) { return bcd_cmp(a->d_, b->d_, P2) == 0; } static int4 ifloor(const BCDFloat2* x) { BCDFloat2 a; floor(x, &a); return a.asInt(); } static int4 itrunc(const BCDFloat2* x) { BCDFloat2 a; trunc(x, &a); return a.asInt(); } static bool floor(const BCDFloat2* a, BCDFloat2* c); static bool trunc(const BCDFloat2* a, BCDFloat2* c); void _init(); int _round25() { return bcd_round25(d_, P2); } void _fromUInt(uint4 v) { bcd_fromUInt(d_, P2, v); } static void epsilon(int n, BCDFloat2* v) { // generate 10^-n, int m = BCDDecade[(n-1) & 3]; v->ldexp(m, -(n>>2)); } int4 asInt() const; }; #endif free42-nologo-1.4.77/common/bcdmath.cc000644 000765 000024 00000103706 12110237247 020041 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "bcdmath.h" #include "bcd2.h" #define BCD_CONST_PI 0 #define BCD_CONST_PI2 1 #define BCD_CONST_PIBY2 2 #define BCD_CONST_PIBY32 3 #define BCD_CONST_SINTAB 4 #define BCD_CONST_COSTAB (BCD_CONST_SINTAB+8) #define BCD_CONST_LN2 (BCD_CONST_COSTAB+8) #define BCD_CONST_ONEOTWO (BCD_CONST_LN2+1) #define BCD_CONST_LN10 (BCD_CONST_ONEOTWO+1) #define BCD_CONST_LN2OLN10 (BCD_CONST_ONEOTWO+2) #define BCD_CONST_ATANLIM (BCD_CONST_LN2OLN10+1) #define BCD_CONST_PIBY32A (BCD_CONST_ATANLIM+1) #define BCD_CONST_PIBY32B (BCD_CONST_PIBY32A+1) #define BCD_CONST_HUNDREDTH (BCD_CONST_PIBY32B+1) #define BCD_CONST_HALF (BCD_CONST_HUNDREDTH+1) #define BCD_CONST_TWOHALF (BCD_CONST_HALF+1) #define BCD_CONST_TEN (BCD_CONST_TWOHALF + 1) #define BCD_CONST_SINPOLY (BCD_CONST_TEN+1) #define BCD_CONST_COSPOLY (BCD_CONST_SINPOLY+7) #define BCD_CONST_LOGPOLY (BCD_CONST_COSPOLY+7) #define BCD_CONST_LOGCK (BCD_CONST_LOGPOLY+4) #define BCD_CONST_EXP1 (BCD_CONST_LOGCK+64) typedef unsigned short Dig[P+1]; typedef unsigned short Dig2[P2+1]; static const Dig constTable[] = { { 3, 1415, 9265, 3589, 7932, 3846, 2643, 1 }, // pi { 6, 2831, 8530, 7179, 5864, 7692, 5287, 1 }, // 2pi { 1, 5707, 9632, 6794, 8966, 1923, 1322, 1 }, // pi/2 { 981, 7477, 424, 6810, 3870, 1957, 6057, 0 }, // pi/32 /* table for sin & cos */ { 980, 1714, 329, 5606, 199, 4195, 5639, 0 }, // sin(pi/32) { 1950, 9032, 2016, 1282, 6784, 8284, 8685, 0 }, // sin(pi/16) { 2902, 8467, 7254, 4623, 6763, 6192, 3758, 0 }, // sin(3pi/32) { 3826, 8343, 2365, 897, 7172, 8459, 9840, 0 }, // sin(pi/8) { 4713, 9673, 6825, 9976, 4855, 6387, 6259, 0 }, // sin(5pi/32) { 5555, 7023, 3019, 6022, 2474, 2830, 8139, 0 }, // sin(3pi/16) { 6343, 9328, 4163, 6454, 9821, 5171, 6132, 0 }, // sin(7pi/32) { 7071, 678, 1186, 5475, 2440, 844, 3621, 0 }, // sin(pi/4) { 9951, 8472, 6672, 1968, 8624, 4836, 9531, 0 }, // cos(pi/32) { 9807, 8528, 403, 2304, 4912, 6182, 2361, 0 }, // cos(pi/16) { 9569, 4033, 5732, 2088, 6493, 5797, 8870, 0 }, // cos(3pi/32) { 9238, 7953, 2511, 2867, 5612, 8183, 1894, 0 }, // cos(pi/8) { 8819, 2126, 4348, 3550, 2971, 2756, 8637, 0 }, // cos(5pi/32) { 8314, 6961, 2302, 5452, 3707, 8788, 3776, 0 }, // cos(3pi/16) { 7730, 1045, 3362, 7369, 6081, 906, 6098, 0 }, // cos(7pi/32) { 7071, 678, 1186, 5475, 2440, 844, 3621, 0 }, // cos(pi/4) { 6931, 4718, 559, 9453, 941, 7232, 1215, 0 }, // ln(2) { 1, 200, 0, 0, 0, 0, 0, 1 }, // 1.02 { 2, 3025, 8509, 2994, 456, 8401, 7991, 1 }, // ln(10) { 3010, 2999, 5663, 9811, 9521, 3738, 8947, 0 }, // ln(2)/ln(10) { 1000, 0, 0, 0, 0, 0, 0, 0 }, // 0.1 atan limit { 981, 7477, 424, 0, 0, 0, 0, 0 }, // pi/32 part A { 6810, 3870, 1957, 6057, 2748, 4465, 1312, (-3)&EXPMASK }, // pi/32 part B NOTE NEG!! { 200, 0, 0, 0, 0, 0, 0, 0 }, // 0.02 limit used in ln1p { 5000, 0, 0, 0, 0, 0, 0, 0 }, // 0.5 { 2, 5000, 0, 0, 0, 0, 0, 1 }, // 2.5 { 10, 0, 0, 0, 0, 0, 0, 1 }, // 10 // sin(x) polynomial { 1666, 6666, 6666, 6666, 6666, 6666, 6365, 32768&EXPMASKNEG }, { 83, 3333, 3333, 3333, 3333, 3303, 1985, 0 }, { 1, 9841, 2698, 4126, 9840, 7789, 3780, 32768&EXPMASKNEG }, { 275, 5731, 9223, 9828, 9145, 4611, 7555, 32767&EXPMASKNEG }, { 2, 5052, 1083, 7671, 5305, 495, 7411, 65535&EXPMASKNEG }, { 160, 5903, 801, 9414, 1262, 5005, 7297, 32766&EXPMASKNEG }, { 7637, 5075, 3356, 6847, 5161, 2933, 1632, 65533&EXPMASKNEG }, // cos(x) polynomial { 4999, 9999, 9999, 9999, 9999, 9999, 4857, 32768&EXPMASKNEG }, { 416, 6666, 6666, 6666, 6666, 6153, 8238, 0 }, { 13, 8888, 8888, 8888, 8880, 5372, 4713, 32768&EXPMASKNEG }, { 2480, 1587, 3015, 8220, 374, 6779, 0, 32767&EXPMASKNEG }, { 27, 5573, 1920, 9146, 3795, 4381, 778, 65535&EXPMASKNEG }, { 2087, 6734, 8250, 8148, 2157, 8695, 5067, 32766&EXPMASKNEG }, { 11, 4543, 2933, 7975, 7024, 4861, 9252, 65534&EXPMASKNEG }, // log(x) polynomial { 833, 3333, 3333, 3333, 3333, 3200, 4980, 0 }, { 125, 0, 0, 0, 1767, 3000, 0, 0 }, { 22, 3214, 2856, 3634, 1290, 0, 0, 0 }, { 4, 3404, 1799, 7690, 0, 0, 0, 0 }, // log(ck) constants, ck = 1+k/64, k = 1..64; { 155, 418, 6535, 9652, 5415, 854, 460, 0 }, { 307, 7165, 8666, 7536, 8837, 1028, 2075, 0 }, { 458, 953, 6031, 2942, 316, 6679, 2676, 0 }, { 606, 2462, 1816, 4348, 4258, 606, 1320, 0 }, { 752, 2342, 1237, 5875, 2569, 8605, 3399, 0 }, { 896, 1215, 8689, 6871, 3261, 9951, 4693, 0 }, { 1037, 9679, 3681, 6435, 6482, 6061, 8037, 0 }, { 1177, 8303, 5656, 3834, 5453, 8794, 1094, 0 }, { 1315, 7635, 7788, 7192, 7258, 8716, 1286, 0 }, { 1451, 8200, 9844, 4978, 9728, 1935, 637, 0 }, { 1586, 503, 176, 6385, 8409, 3371, 1746, 0 }, { 1718, 5025, 6926, 6592, 2234, 98, 9460, 0 }, { 1849, 2233, 8494, 119, 9266, 3903, 5926, 0 }, { 1978, 2574, 3329, 9198, 8036, 2572, 711, 0 }, { 2105, 6476, 9107, 3496, 3766, 9552, 8127, 0 }, { 2231, 4355, 1314, 2097, 5576, 6295, 903, 0 }, { 2355, 6607, 1312, 7669, 907, 7588, 2189, 0 }, { 2478, 3616, 3904, 5812, 5678, 602, 7657, 0 }, { 2599, 5752, 4436, 9260, 6697, 2079, 4945, 0 }, { 2719, 3371, 5483, 6417, 5883, 1669, 4945, 0 }, { 2837, 6817, 3130, 6445, 9834, 6901, 2223, 0 }, { 2954, 6421, 2893, 8358, 7638, 6681, 9060, 0 }, { 3070, 2503, 5294, 9118, 6207, 5124, 5405, 0 }, { 3184, 5373, 1118, 5346, 1581, 247, 2135, 0 }, { 3297, 5328, 6372, 4679, 8181, 4422, 8119, 0 }, { 3409, 2658, 6970, 5932, 1030, 5089, 1997, 0 }, { 3519, 7642, 3157, 1781, 8465, 5447, 4562, 0 }, { 3629, 549, 3689, 3684, 5313, 7824, 3459, 0 }, { 3737, 1640, 9793, 5840, 8082, 1016, 8327, 0 }, { 3844, 1169, 8910, 3320, 3973, 4790, 624, 0 }, { 3949, 9380, 8240, 8689, 7810, 6394, 363, 0 }, { 4054, 6510, 8108, 1643, 8197, 8013, 1154, 0 }, { 4158, 2789, 5143, 7109, 6561, 3328, 8929, 0 }, { 4260, 8439, 5310, 9000, 6312, 4544, 8795, 0 }, { 4362, 3676, 6774, 9180, 7034, 9041, 3230, 0 }, { 4462, 8710, 2628, 4195, 1153, 2590, 1806, 0 }, { 4562, 3743, 3481, 5875, 9438, 805, 5381, 0 }, { 4660, 8972, 9924, 5992, 2455, 8619, 2475, 0 }, { 4758, 4590, 4869, 9639, 1426, 5209, 5863, 0 }, { 4855, 781, 5781, 7008, 780, 1791, 771, 0 }, { 4950, 7726, 6797, 8515, 1459, 7964, 5848, 0 }, { 5045, 5601, 752, 3952, 8705, 8308, 5317, 0 }, { 5139, 4575, 1102, 2343, 1680, 1006, 882, 0 }, { 5232, 4814, 3764, 5478, 3651, 6807, 2249, 0 }, { 5324, 6479, 8869, 4718, 4387, 3923, 7234, 0 }, { 5415, 9728, 2432, 7443, 7157, 6542, 3039, 0 }, { 5506, 4711, 7952, 6622, 7925, 9948, 1792, 0 }, { 5596, 1578, 7935, 4226, 8627, 888, 5005, 0 }, { 5685, 473, 5352, 6687, 1207, 8738, 7648, 0 }, { 5773, 1536, 5034, 8236, 431, 8112, 615, 0 }, { 5860, 4904, 5003, 5782, 890, 4119, 4362, 0 }, { 5947, 710, 7746, 6927, 8951, 4343, 5465, 0 }, { 6032, 9085, 1438, 842, 6234, 585, 1866, 0 }, { 6118, 154, 1105, 9929, 352, 9889, 7664, 0 }, { 6202, 4040, 9751, 8575, 2885, 1494, 6325, 0 }, { 6286, 865, 9422, 3741, 3774, 4308, 2057, 0 }, { 6369, 746, 2237, 692, 3162, 494, 4271, 0 }, { 6451, 3796, 1373, 5847, 166, 5228, 4961, 0 }, { 6533, 127, 2012, 7456, 3875, 8615, 8812, 0 }, { 6613, 9848, 2245, 3650, 826, 235, 8387, 0 }, { 6694, 3065, 3942, 6292, 6729, 8885, 2709, 0 }, { 6773, 9882, 3591, 8061, 4080, 9682, 6099, 0 }, { 6853, 400, 3098, 9194, 1654, 4048, 789, 0 }, { 6931, 4718, 559, 9453, 941, 7232, 1214, 0 }, // e = 2.7182 8182 8459 0452 3536 0287 4713 5266 2497 7572 470936999 { 2, 7182, 8182, 8459, 452, 3536, 287, 1 }, // exp(1) }; #define BCD2_CONST_LANCZOS 0 static const Dig2 constTable2[] = { // double size Lanczos terms for gamma //3.7948,6229,8882,1576,6137,0571,8385,6087,7748,2487,6086,1116,8266,2325,9950,6316,10 { 3, 7948, 6229, 8882, 1576, 6137, 571, 8385,6087,7748,2487,6086,1116,8266,2325, 2 }, //-6.1191,9133,3435,0268,9475,3695,8619,2303,8586,6169,7188,5413,0739,6546,3038,1464,6 { 6, 1191, 9133, 3435, 268, 9475, 3695, 8619,2303,8586,6169,7188,5413,739,6546, 32770 }, //3.1935,3139,9365,7178,9732,6587,2516,1815,7710,7349,5270,6969,8696,7561,8425,94 { 3, 1935, 3139, 9365, 7178, 9732, 6587, 2516,1815,7710,7349,5270,6969,8696,7562, 2 }, //-1.0648,2204,9659,4145,5971,0636,6767,3045,5636,6427,7798,2642,4908,6556,1951,4320,785 { 1, 648, 2204, 9659, 4145, 5971, 636, 6767,3045,5636,6427,7798,2642,4908,6556, 32770}, // 2.215,7659,2545,9700,1065,2640,0839,0578,0196,6530,8981,3632,5470,7872,885 { 2215, 7659, 2545, 9700, 1065, 2640, 839, 578,196,6530,8981,3632,5470,7872,885, 1 }, //-2.77,3434,9002,3102,0315,6808,5633,2632,6001,3314,1546,7408,3072,4481,5716,8215,697 { 277, 3434, 9002, 3102, 315, 6808, 5633, 2632,6001,3314,1546,7408,3072,4481,5717, 32769 }, //1.9,7504,4798,8896,0954,2464,2454,3527,7206,3456,0350,8210,3826,3387,9892,0783,963 { 19, 7504, 4798, 8896, 954, 2464, 2454, 3527,7206,3456,350,8210,3826,3387,9892, 1 }, // -7.351,4584,5326,3110,3427,1541,0972,9593,4702,3887,1286,0444,6989,7138,7333,92 { 7351, 4584, 5326, 3110, 3427, 1541, 972, 9593,4702,3887,1286,444,6989,7138,7334, 32768 }, // 1.25,0201,6315,9372,8926,0576,1395,0748,8131,6554,2830,4183,3646,7890,817 { 125, 201, 6315, 9372, 8926, 576, 1395, 748,8131,6554,2830,4183,3646,7890,817, 0 }, // -7.710,2871,8096,9904,7327,0526,2403,7596,8997,0952,8489,4696,4202,3767,5221,521 { 7710, 2871, 8096, 9904, 7327, 526, 2403, 7596,8997,952,8489,4696,4202,3767,5222, 40959 }, //1.0,9373,7115,9701,7175,1506,3503,3523,6971,8098,6730,4848,3630,9464,6226,8883,4744,5305 { 10, 9373, 7115, 9701, 7175, 1506, 3503, 3523,6971,8098,6730,4848,3630,9464,6227, 8191 }, //-1.1,2406,1022,3182,8735,6453,7307,4629,1188,2930,0182,3911,2707,2798,2287,6267,78 { 11, 2406, 1022, 3182, 8735, 6453, 7307, 4629,1188,2930,182,3911,2707,2798,2288, 40958 }, //-2.77,0957,5972,2463,9573,5873,7503,6522,6385,2612,8760,1919,8791,4529,9276,1905,83 { 277,957,5972,2463,9573,5873,7503, 6522,6385,2612,8760,1919,8791,4529,9276, 40956 }, }; BCD pi() { return *(const BCDFloat*)(constTable + BCD_CONST_PI); } // 2pi BCD pi2() { return *(const BCDFloat*)(constTable + BCD_CONST_PI2); } static void horner(const BCDFloat* coef, BCD& x, int n, BCD& res) { int i; const BCDFloat* cp = coef + n; res = 0; for (i = 0; i < n; ++i) { res = res * x; res += BCD(*--cp); } } static void sinPoly(const BCD& a, BCD& sa) { /* calculate sin(a) for |a| <= pi/32 * * p(y) = -0.166666666666666666666666636459634636681163368103 + 0.00833333333333333333330319847154774103100919421 *y - 0.000198412698412698407789377962065944968466530592 * y**2 + 2.755731922398289145461175553432349911142734e-6 * y**3 - 2.5052108376715305049574108322138191235704e-8 *y**4 + 1.60590308019414126250057297007175565961045303085e-10 *y**5 - 7.63750753356684751612933163194608455774099589e-13 *y**6 * then sin(x) ~ x(1+x^2*p(x^2)). relative error <= 1.25*10^-28. * * NOTES: * MiniMaxApproximation[ If[y > 0, (Sin[Sqrt[y]] - Sqrt[y])/y^(3/2), -1/6], {y, {0, Pi/32}, 6, 0}, WorkingPrecision -> 30][[2, 1]] * */ BCD y = a*a; BCD py; horner((const BCDFloat*)(constTable + BCD_CONST_SINPOLY), y, 7, py); sa = a*(1+y*py); } static void cosPoly(const BCD& a, BCD& ca) { /* * calculate sin(a) for |a| <= pi/32 * * p(y) = -0.499999999999999999999999485707905851100423176682 + 0.041666666666666666666153823812611360817535526463*y -0.001388888888888888805372471283461577523825976975*Power(y,2) + 0.000024801587301582200374677934077121988251491037*Power(y,3) - 2.75573192091463795438107775847721847637672e-7*Power(y,4) + 2.087673482508148215786955066969762215737996819883e-9*Power(y,5) - 1.1454329337975702448619252226154423156415441444e-11*Power(y,6) * * then cos(x) + 1+x^2*p(x^2). relative error 3*10^-26. * * NOTES: MiniMaxApproximation[ If[y > 0, (Cos[Sqrt[y]] - 1)/y, -1/2], {y, {0, Pi/32}, 6, 0}, WorkingPrecision -> 30][[2, 1]] * */ BCD y = a*a; BCD py; horner((const BCDFloat*)(constTable + BCD_CONST_COSPOLY), y, 7, py); ca = 1+y*py; } #define K 12 #define GG 12 static BCD _expm1(const BCD& a) { BCD t(1); int d = 2; BCD2 s(1); BCD2 s1; BCD ss; for (;;) { t *= a/d; s1 = s + t; ss = s1.asBCD(); if (ss == s.asBCD()) { ss *= a; break; } s = s1; ++d; } return ss; } void sincos(const BCD& v, BCD* sinv, BCD* cosv) { /* calculate sin(v), cos(v) or both as requested. */ BCD a; int4 k; int neg = v.isNeg(); /* arrange a >= 0 */ a = v; a._v.clearSign(); /* reduce argument to 0 <= a < 2pi using special means, * taking care of large arguments (eg sin(1e22)). its possible * that the answer cannot be calculated accurately, in which case * modtwopi bails out with NAN. */ a = modtwopi(a); if (a.isSpecial()) { if (sinv) *sinv = a; if (cosv) *cosv = a; return; } /* * reduce to k*pi/32 + a, where a < pi/32. use a lookup table * for sin(k*pi/32) and cos(k*pi/32). require 8 entries for each. */ BCD piby32(*(const BCDFloat*)(constTable + BCD_CONST_PIBY32)); k = ifloor(a/piby32); if (k > 0) { // subtract in two parts for accuracy. BCD piby32a(*(const BCDFloat*)(constTable + BCD_CONST_PIBY32A)); BCD piby32b(*(const BCDFloat*)(constTable + BCD_CONST_PIBY32B)); a = (a - k*piby32a) - k*piby32b; } /* now a <= pi/32, we use the polynomial approximations */ BCD sa; BCD ca; BCD sina, cosa; BCD* sap = 0; BCD* cap = 0; k &= 64-1; // wrap around at 2pi int4 q = k/16; // q is the quadrant. k &= 15; // index into table. if (k) // sap & cap = 0 for k = 0. { /* require both sin and cos */ sinPoly(a, sa); cosPoly(a, ca); if (k < 8) { BCD sk(*(const BCDFloat*)(constTable + BCD_CONST_SINTAB-1+k)); BCD ck(*(const BCDFloat*)(constTable + BCD_CONST_COSTAB-1+k)); sina = sk*ca + ck*sa; cosa = ck*ca - sk*sa; } else { BCD sk(*(const BCDFloat*)(constTable + BCD_CONST_SINTAB+15-k)); BCD ck(*(const BCDFloat*)(constTable + BCD_CONST_COSTAB+15-k)); sina = ck*ca + sk*sa; cosa = sk*ca - ck*sa; } sap = &sina; cap = &cosa; } if (sinv) { if ((q&1) == 0) { if (!sap) { sinPoly(a, sina); sap = &sina; } *sinv = sina; } else { if (!cap) { cosPoly(a, cosa); cap = &cosa; } *sinv = cosa; } if (neg ^ (q > 1)) *sinv = -*sinv; } if (cosv) { if ((q&1) == 0) { if (!cap) { cosPoly(a, cosa); } *cosv = cosa; } else { if (!sap) { sinPoly(a, sina); } *cosv = sina; } if (q == 1 || q == 2) *cosv = -*cosv; } } void sinhcosh(const BCD& a, BCD* sinha, BCD* cosha) { BCD v = exp(a); if (sinha) *sinha = (v - 1/v)/2; // XX unstable if (cosha) *cosha = (v + 1/v)/2; } BCD sin(const BCD& v) { BCD sinv; if (v.isSpecial()) return v; sincos(v, &sinv, 0); return sinv; } BCD cos(const BCD& v) { BCD cosv; if (v.isSpecial()) return v; sincos(v, 0, &cosv); return cosv; } BCD tan(const BCD& v) { BCD s, c; if (v.isSpecial()) return v; sincos(v, &s, &c); return s/c; } static BCD powfmod(const BCD& a, int4 n, const BCD& fm) { // ASSUME n >= 0 // ASSUME a >= 0, fm > 0 if (n == 0) return 1; BCD s; BCD r = a; s = 1; /* Use binary exponentiation */ for (;;) { if (n & 1) { s *= r; if (s >= fm) { BCD q = trunc(s/fm); s -= q*fm; } } n >>= 1; if (!n) break; r *= r; if (r >= fm) { BCD q = trunc(r/fm); r -= q*fm; } } return s; } BCD exp(const BCD& v) { /* write v = k*r + n*ln(2) * where |k*r| < ln(2) * * let k be some power of 2, eg 64 so that * * exp(v) = exp(kr + nln(2)) = exp(kr)*2^n * = exp(r)^64 * 2^n * * then r is small enough for taylor series. */ if (v.isSpecial()) { if (v.isInf() && v.isNeg()) return 0; // exp(-inf) = 0 return v; } if (v.isZero()) return 1; BCD ln2(*(const BCDFloat*)(constTable + BCD_CONST_LN2)); BCD n = trunc(v/ln2); if (n > 33218) { /* overflow */ return BCDFloat::posInf(); } else if (n < -33218) { /* underflow */ return 0; } int ni = itrunc(n); int k = 64; BCD r = (v - n*ln2)/k; BCD t1 = _expm1(r); BCD er = t1 + 1; BCD del = t1 - (er - 1); t1 = pow(er,k); er = t1 + t1/er*del*k; // correction factor return er*pow(BCD(2), ni); } static BCD _ln1p(const BCD& a) { /* otherwise use a series that converges for small arguments */ BCD s; BCD t; BCD s1; BCD x; BCD c; c = 2; s = a; x = -a; t = x; for (;;) { t *= x; s1 = s - t/c; if (s1 == s) break; c += 1; s = s1; } return s1; } static BCD logPoly(const BCD& r) { /* MiniMaxApproximation[ If[r > 0, Log[(1 + Sqrt[r]/2)/(1 - Sqrt[r]/2)]/Sqrt[r], 1], {r, {0, 1/128}, 3, 0}, WorkingPrecision -> 30] produces p(r), so ln((1+r/2)/(1-r/2)) ~ p(r^2)*r BUT, it's not very good, seeing about |e| ~ 1.25e-14 */ /* p(r) = r + 0.08333333333333335805 r^3 + 0.0124999999978878 r^5 + 0.002232197165 r^7 max error <= 0.27e-24. Elementary Algorithms. Jean-Michel Muller. p57. */ /* p(r) = r + 0.0833333333333333333332004980000 r^3 + 0.0125000000000000176730000000000 r^5 + 0.00223214285636341290000000000000 r^7 + 0.000434041799769000000000000000000 r^9 max error <= 0.80e-30. Elementary Algorithms. Jean-Michel Muller. p57. relative error max of 3e-28. */ BCD r2 = r*r; BCD pr; horner((const BCDFloat*)(constTable + BCD_CONST_LOGPOLY), r2, 4, pr); return (pr*r2 + 1)*r; } static BCD _log(const BCD& v, int4& p10, int4& p2, bool& neg) { if (v.isNeg()) return BCDFloat::nan(); if (v.isSpecial()) return v; if (v.isZero()) return BCDFloat::negInf(); BCD lna; BCD2 a2; neg = false; if (v < 1) { // v in (0,1) map to (1,inf) and negate final answer a2 = BCD2(1)/v; neg = true; if (a2.isInf()) { // This only happens if v = 1e-10000 p10 = 10000; p2 = 0; return 0; } } else a2 = v; // have a in [1,inf) // extract the power of 10 from the exponent p10 = (a2.exponent()-1)*4; a2.setExponent(1); // divide out the biggest digit to leave a number < 10 unsigned int d = a2.digit(0); int dnorm = 1; while (d >= 10) { ++p10; // adjust the 10's count dnorm *= 10; d /= 10; } // divide by 2 until < 2, keep a count of the 2's p2 = 0; while (d > 1) { ++p2; // adjust the 2s count dnorm <<= 1; d >>= 1; } // finally divide the original number by the dnorm to take out // the 10s and 2s, leaving a value not bigger than 2. if (dnorm > 1) a2 /= dnorm; // we have a in (1+delta,2] // ln(v) = ln(a) + p10*ln10 + p2*ln2; // find ck where ck = 1+k/64, k=1,2,..64 with |x-ck| <= 1/128 BCD2 a21 = a2 - 1; BCD half(*(const BCDFloat*)(constTable + BCD_CONST_HALF)); int4 k = ifloor(a21.asBCD()*64 + half); // round to nearest k // set r = 2*(a - ck)/(a + ck) BCD2 dk = BCD(k)/64; BCD r = ((a21 - dk)/(a2 + 1 + dk)).asBCD(); // now, ln(a/ck) = ln((1+r/2)/(1-r/2)) = logPoly(r); lna = logPoly(r*2); // ln(a) = ln(ck) + ln(a/ck); if (k) { BCD lnck(*(const BCDFloat*)(constTable + BCD_CONST_LOGCK + k - 1)); lna += lnck; } return lna; } BCD ln10constant() { return (*(const BCDFloat*)(constTable + BCD_CONST_LN10)); } BCD halfConstant() { return (*(const BCDFloat*)(constTable + BCD_CONST_HALF)); } BCD log(const BCD& v) { /* natural logarithm */ int4 p10, p2; bool neg; BCD lv = _log(v, p10, p2, neg); if (!lv.isSpecial()) { BCD ln10(*(const BCDFloat*)(constTable + BCD_CONST_LN10)); BCD ln2(*(const BCDFloat*)(constTable + BCD_CONST_LN2)); lv += p10*ln10 + p2*ln2; if (neg) lv.negate(); } return lv; } BCD log10(const BCD& v) { /* common log */ int4 p10, p2; bool neg; BCD lv = _log(v, p10, p2, neg); if (!lv.isSpecial()) { BCD ln2Oln10(*(const BCDFloat*)(constTable + BCD_CONST_LN2OLN10)); BCD ln10(*(const BCDFloat*)(constTable + BCD_CONST_LN10)); lv = lv/ln10 + p10 + p2*ln2Oln10; if (neg) lv.negate(); } return lv; } BCD atan(const BCD& v) { bool neg = v.isNeg(); if (v.isSpecial()) { if (v.isInf()) { BCD piby2(*(const BCDFloat*)(constTable + BCD_CONST_PIBY2)); if (neg) return (piby2*3)/2; return piby2; } return v; } /* arrange for a >= 0 */ BCD a; if (neg) a = -v; else a = v; /* reduce range to 0 <= a < 1, using atan(x) = pi/2 - atan(1/x) */ bool invert = (a > 1); if (invert) a = 1/a; /* reduce to small enough limit to use taylor series. * using * tan(x/2) = tan(x)/(1+sqrt(1+tan(x)^2)) */ BCD atanlim(*(const BCDFloat*)(constTable + BCD_CONST_ATANLIM)); int doubles = 0; while (a > atanlim) { ++doubles; a = a/(1+sqrt(1+a*a)); // at most 3 iterations. } /* now use taylor series * tan(x) = x(1-x^2/3+x^4/5-x^6/7...) */ BCD a2 = a*a; BCD t = a2; BCD s = 1 - t/3; int i; int j = 5; /* perform 9 more terms, error will be the first term not used. * ie x^21/21. */ for (i = 2; i < 11; ++i) { t *= a2; if ((i & 1) == 0) s += t/j; else s -= t/j; j += 2; } s = s*a; while (doubles) { s = s + s; --doubles; } if (invert) { BCD piby2(*(const BCDFloat*)(constTable + BCD_CONST_PIBY2)); s = piby2 - s; } if (neg) { s = -s; } return s; } BCD atan2(const BCD& y, const BCD& x) { BCD r; if (x.isZero()) { r = BCD(*(const BCDFloat*)(constTable + BCD_CONST_PIBY2)); if (y.isNeg()) { r = -r; } } else { r = atan(y/x); if (x.isNeg()) { BCD pi(*(const BCDFloat*)(constTable + BCD_CONST_PI)); if (y.isNeg()) { r = r - pi; } else { r = r + pi; } } } return r; } BCD asin(const BCD& v) { if (v.isSpecial()) return v; if (v < -1 || v > 1) { return BCDFloat::nan(); } BCD r = atan(v/(1+sqrt(1-v*v))); r = r + r; return r; } BCD acos(const BCD& v) { if (v.isSpecial()) return v; if (v < -1 || v > 1) { return BCDFloat::nan(); } if (v == 1) return 0; BCD r = atan((1-v)/sqrt(1-v*v)); r = r + r; return r; } BCD pow(const BCD& x, const BCD& y) { if (y.isZero()) { if (x.isZero()) return BCDFloat::nan(); return 1; } /* check for x^n */ if (y.isInteger()) { int4 n = ifloor(y); if (n) return pow(x, n); /* otherwise power is too large */ if (x == 1) return x; bool a = (fabs(x) > 1); if (a == y.isNeg()) return 0; return BCDFloat::posInf(); } /* otherwise use logs */ return exp(log(x)*y); } /* here are the first 1060 decimal digits of 1/2pi */ static const unsigned short inv2pi[] = { 1591, 5494, 3091, 8953, 3576, 8883, 7633, 7251, 4362, 344, 5964, 5740, 4564, 4874, 7667, 3440, 5889, 6797, 6342, 2653, 5090, 1138, 276, 6253, 859, 5607, 2842, 7267, 5795, 8036, 8929, 1184, 6114, 5786, 5287, 7967, 4107, 3169, 9839, 2292, 3996, 6937, 4090, 7757, 3077, 7463, 9692, 5307, 6887, 1739, 2896, 2173, 9766, 1693, 3623, 9024, 1723, 6290, 1183, 2380, 1142, 2269, 9755, 7159, 4046, 1890, 869, 267, 3956, 1204, 8941, 936, 9378, 4408, 5528, 7230, 9994, 6443, 4002, 4867, 2347, 7394, 5961, 898, 3230, 9678, 3074, 9061, 6698, 6462, 8046, 9944, 8652, 1878, 8157, 4786, 5669, 6424, 1038, 9958, 7413, 9348, 6099, 8386, 8099, 1999, 6244, 2875, 5851, 7117, 8858, 4311, 1751, 8767, 1605, 4654, 7536, 9880, 973, 9460, 3647, 5933, 3768, 593, 249, 4496, 6353, 532, 7156, 7755, 322, 324, 7778, 1639, 7166, 229, 4674, 8119, 5981, 6584, 606, 168, 303, 5998, 1339, 1198, 7498, 8327, 8665, 4435, 2797, 5507, 16, 2406, 7756, 4388, 8495, 7131, 880, 1221, 9937, 6147, 6813, 7776, 4737, 8906, 3306, 8046, 4579, 7848, 1761, 3124, 2731, 4069, 9607, 7502, 4500, 2977, 5985, 7089, 569, 279, 6785, 1315, 2521, 16, 3177, 4602, 924, 8116, 624, 561, 4562, 314, 6484, 892, 4845, 9191, 4352, 1157, 5407, 5562, 87, 1526, 6068, 221, 7159, 1407, 5747, 4582, 7225, 9774, 6285, 3998, 7515, 5329, 3908, 1398, 1772, 4093, 5825, 4797, 733, 2871, 9040, 6999, 7590, 7657, 7078, 4934, 7039, 3589, 8280, 8717, 3425, 6403, 6689, 5116, 6254, 5705, 9433, 2763, 1268, 6500, 2612, 2717, 9711, 5321, 1259, 9504, 3866, 7945, 376, 2556, 836, 3171, 1695, 2597, 5812, 8224, 9416, 2333, 4314, 5106, 1235 }; /* double precision version of 2pi */ static const unsigned short pi2dp[] = { 6, 2831, 8530, 7179, 5864, 7692, 5286, 7665, 5900, 5768, 3943, 3879, 8750, 2116, 4194 }; BCD modtwopi(const BCD& a) { BCD pi2(*(const BCDFloat*)(constTable + BCD_CONST_PI2)); if (a < pi2) return a; unsigned short xd[2*P+1]; int i; /* copy digits of manstissa as double precision */ for (i = 0; i < P; ++i) xd[i] = a.digit(i); while (i <= 2*P) // clear extended digits. { xd[i] = 0; ++i; } int ex = a.exponent(); unsigned short bd[2*P+1]; int eb; int excess = 0; /* see if the exponent is large enough to consider it separately */ if (ex > P) { /* yes. consider our number as X * BASE^E where X is an integer. * in this case we can calculate f = (BASE^E) mod 2pi accurately * and form X*f. */ excess = ex - P; // remaining exponent ex = P; unsigned short fd[2*P+1]; int ef; /* do we have enough table? */ if (excess + 2*P+1 > (int)(sizeof(inv2pi)/sizeof(inv2pi[0]))) { /* oh dear, digits required off end of table. give up. */ return BCDFloat::nan(); } /* find base^ex mod 2pi using the table */ BCDFloat::mul2(inv2pi + excess, 0, pi2dp, 1, fd, ef); /* multiply exponent excess mod 2pi into X. note, all * calculations here must be double precision. this is because * we are only interested in the fractional part and the * integer part is the size of 1 precision already. */ BCDFloat::mul2(fd, ef, xd, ex, bd, eb); for (i = 0; i < 2*P+1; ++i) xd[i] = bd[i]; ex = eb; } /* need to divide by 2pi and extract the fractional part. * this is done by multiplying by 1/2pi using our table digits * we had to have for the exponent extraction. */ BCDFloat::mul2(xd, ex, inv2pi, 0, bd, eb); BCD b; /* pick out final single precision fraction */ for (i = 0; i < P; ++i) b.digit(i, bd[i+eb]); b.digit(P, 0); // no exponent. return b * pi2; } BCD hypot(const BCD& a, const BCD& b) { // return sqrt(a^2 + b^2) // use numerically stable method, Numerical Recipies 3rd p226 BCD fa, fb, t; fa = fabs(a); fb = fabs(b); if (a.isZero()) return fb; if (b.isZero()) return fa; if (fa >= fb) { t = b/a; return fa*sqrt(t*t+1); } else { t = a/b; return fb*sqrt(t*t+1); } } BCD fmod(const BCD& a, const BCD& b) { BCD v = a; bool neg = v.isNeg(); if (neg) v.negate(); BCD m = b; if (m.isNeg()) m.negate(); BCD c; if (v < b) { c = v; } else { int em = m.exponent() - 1; int ev = v.exponent() - 1; int t = 1; v.setExponent(t); while (!v.isInteger()) { v.setExponent(++t); --ev; } t = 1; m.setExponent(t); while (em > ev || !m.isInteger()) { m.setExponent(++t); --em; } // peform mod of mantissa c = v - m*trunc(v/m); int e = ev - em; if (e > 0) { // fold in exponent c *= powfmod(BCD(10), e<<2, m); if (c > m) c -= trunc(c/m)*m; } if (em) c.setExponent(c.exponent() + em); } // restore sign if (neg) c.negate(); if (a.isInteger() && b.isInteger() && !c.isInteger()) { // Numerator and denominator are both integral; // in this case we force the result to be integral as well. BCD half(*(const BCDFloat*)(constTable + BCD_CONST_HALF)); if (c < 0) c = trunc(c - half); else c = trunc(c + half); } return c; } BCD ln1p(const BCD& a) { /* calculate log(a+1) without numerical instability near * a == 0. */ /* first test special cases. */ if (a.isSpecial()) return a; if (a.isZero()) return 0; // ln(1) == 0 if (a == -1) return BCDFloat::negInf(); // ln(0) = -inf //if (a < -1) return BCDFloat::nan(); // ln(-x) = nan /* if x > 0.01, then the usual log calculation will give correct * results. */ BCD pointzerotwo(*(const BCDFloat*)(constTable + BCD_CONST_HUNDREDTH)); if (fabs(a) > pointzerotwo) return log(1+a); /* otherwise use a series that converges for small arguments */ return _ln1p(a); } const BCDFloat2* lanczConstants() { return (const BCDFloat2*)(constTable2 + BCD2_CONST_LANCZOS); } static void _gammaFactorialAux(const BCD& z, BCD& t1, BCD& t2, BCD& s) { /* calculate gamma(z+1) = z! for z > 0 * using lanczos expansion with precomputed coefficients. * * NOTE: accuracy degrades as z increases. * * calculate t1, t2 and s where: * gamma(z + 1) = exp(t1*log(t2)-t2)*s; */ BCD2 t, s2; int i; const BCDFloat2* lancz = lanczConstants(); s2 = lancz[0]; t = 1; for (i = 1; i <= K; ++i) { t *= (z+(1-i))/(z + i); s2 += t*lancz[i]; } s = s2.asBCD(); s *= 2; BCD half(*(const BCDFloat*)(constTable + BCD_CONST_HALF)); t1 = z + half; t2 = t1 + GG; } static BCD _gammaFactorial(const BCD& z) { BCD t1, t2, s; _gammaFactorialAux(z, t1, t2, s); return exp(t1*log(t2)-t2)*s; } BCD gammaFactorial(const BCD& c) { /* return c! (c factorial) and also the gamma function * where c! = gamma(c+1). */ /* deal with special cases */ if (c.isSpecial()) return c; if (c >= 3249) return BCDFloat::posInf(); if (c.isInteger()) { if (c.isZero()) return 1; if (c.isNeg()) return BCDFloat::nan(); int v = ifloor(c); if (!v) /* too large for integer. answer must be infinite */ return BCDFloat::posInf(); /* calculate integer factorial */ BCD f = c; BCD x = c; while (v > 1) { --x; f *= x; --v; } return f; } if (c.isNeg()) { /* use reflection formula */ BCD z1 = -c; BCD pi(*(const BCDFloat*)(constTable + BCD_CONST_PI)); BCD z2 = z1*pi; return z2/sin(z2)/_gammaFactorial(z1); } else { return _gammaFactorial(c); } } BCD expm1(const BCD& a) { /* first test special cases. */ if (a.isSpecial() || a.isZero()) return a; // exp(0)-1 == 0 /* if |x| > 0.01, then the usual calculation will give correct * results. */ BCD pointzerotwo(*(const BCDFloat*)(constTable + BCD_CONST_HUNDREDTH)); if (fabs(a) >= pointzerotwo) return exp(a)-1; return _expm1(a); } BCD sinh(const BCD& a) { BCD v = exp(a); return (v - 1/v)/2; // XX unstable } BCD cosh(const BCD& a) { BCD v = exp(a); return (v + 1/v)/2; } BCD tanh(const BCD& a) { BCD v = exp(a); v = v*v; return (v - 1)/(v + 1); } BCD ceil(const BCD& a) { if (a.isInteger()) return a; return floor(a+1); } free42-nologo-1.4.77/common/bcdmath.h000644 000765 000024 00000003623 12110237247 017700 0ustar00thomasstaff000000 000000 /** * Copyright (c) 2005-2009 voidware ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef __bcdmath_h__ #define __bcdmath_h__ #include "bcd.h" BCD pi(); BCD pi2(); // 2pi BCD ln10constant(); BCD halfConstant(); BCD sin(const BCD&); BCD cos(const BCD&); BCD tan(const BCD&); BCD exp(const BCD&); BCD log(const BCD&); BCD atan(const BCD&); BCD pow(const BCD&, const BCD&); BCD atan2(const BCD& y, const BCD& x); BCD asin(const BCD&); BCD acos(const BCD&); BCD modtwopi(const BCD&); BCD log10(const BCD&); BCD hypot(const BCD& a, const BCD& b); BCD fmod(const BCD& a, const BCD& b); BCD ln1p(const BCD&); BCD expm1(const BCD&); BCD gammaFactorial(const BCD&); BCD gammaln(const BCD&); BCD sinh(const BCD&); BCD cosh(const BCD&); BCD tanh(const BCD&); BCD ceil(const BCD&); void sincos(const BCD& v, BCD* sinv, BCD* cosv); void sinhcosh(const BCD& a, BCD* sinha, BCD* cosha); #endif // bcdmath free42-nologo-1.4.77/common/core_commands1.cc000644 000765 000024 00000106175 12110237247 021334 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands1.h" #include "core_commands2.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_math1.h" #include "core_sto_rcl.h" #include "core_variables.h" #include "shell.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 1 */ /********************************************************/ int docmd_clx(arg_struct *arg) { free_vartype(reg_x); reg_x = new_real(0); mode_disable_stack_lift = true; return ERR_NONE; } int docmd_enter(arg_struct *arg) { vartype *v = dup_vartype(reg_x); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = v; mode_disable_stack_lift = true; return ERR_NONE; } int docmd_swap(arg_struct *arg) { vartype *temp = reg_x; reg_x = reg_y; reg_y = temp; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_rdn(arg_struct *arg) { vartype *temp = reg_x; reg_x = reg_y; reg_y = reg_z; reg_z = reg_t; reg_t = temp; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_chs(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { vartype_real *r = (vartype_real *) reg_x; r->x = -(r->x); break; } case TYPE_COMPLEX: { vartype_complex *c = (vartype_complex *) reg_x; c->re = -(c->re); c->im = -(c->im); break; } case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; int4 sz = rm->rows * rm->columns; int4 i; for (i = 0; i < sz; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; if (!disentangle((vartype *) rm)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < sz; i++) rm->array->data[i] = -(rm->array->data[i]); break; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; int4 sz = cm->rows * cm->columns * 2; int4 i; if (!disentangle((vartype *) cm)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < sz; i++) cm->array->data[i] = -(cm->array->data[i]); break; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; } if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } static void docmd_div_completion(int error, vartype *res) { if (error == ERR_NONE) binary_result(res); } int docmd_div(arg_struct *arg) { return generic_div(reg_x, reg_y, docmd_div_completion); } static void docmd_mul_completion(int error, vartype *res) { if (error == ERR_NONE) binary_result(res); } int docmd_mul(arg_struct *arg) { return generic_mul(reg_x, reg_y, docmd_mul_completion); } int docmd_sub(arg_struct *arg) { vartype *res; int error = generic_sub(reg_x, reg_y, &res); if (error == ERR_NONE) binary_result(res); return error; } int docmd_add(arg_struct *arg) { vartype *res; int error = generic_add(reg_x, reg_y, &res); if (error == ERR_NONE) binary_result(res); return error; } int docmd_lastx(arg_struct *arg) { vartype *v = dup_vartype(reg_lastx); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_complex(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; vartype *v; if (flags.f.polar) { phloat re, im; generic_p2r(((vartype_real *) reg_y)->x, ((vartype_real *) reg_x)->x, &re, &im); v = new_complex(re, im); } else { v = new_complex(((vartype_real *) reg_y)->x, ((vartype_real *) reg_x)->x); } if (v == NULL) return ERR_INSUFFICIENT_MEMORY; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = v; free_vartype(reg_y); reg_y = reg_z; reg_z = dup_vartype(reg_t); break; } case TYPE_COMPLEX: { vartype *new_x = new_real(0); vartype *new_y = new_real(0); if (new_x == NULL || new_y == NULL) { free_vartype(new_x); free_vartype(new_y); return ERR_INSUFFICIENT_MEMORY; } if (flags.f.polar) { phloat r, phi; generic_r2p(((vartype_complex *) reg_x)->re, ((vartype_complex *) reg_x)->im, &r, &phi); if (p_isinf(r) != 0) { if (flags.f.range_error_ignore) r = POS_HUGE_PHLOAT; else { free_vartype(new_x); free_vartype(new_y); return ERR_OUT_OF_RANGE; } } ((vartype_real *) new_y)->x = r; ((vartype_real *) new_x)->x = phi; } else { ((vartype_real *) new_y)->x = ((vartype_complex *) reg_x)->re; ((vartype_real *) new_x)->x = ((vartype_complex *) reg_x)->im; } free_vartype(reg_lastx); reg_lastx = reg_x; free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = new_y; reg_x = new_x; break; } case TYPE_REALMATRIX: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; else { vartype_realmatrix *re_m = (vartype_realmatrix *) reg_y; vartype_realmatrix *im_m = (vartype_realmatrix *) reg_x; vartype_complexmatrix *cm; int4 sz, i; if (re_m->rows != im_m->rows || re_m->columns != im_m->columns) return ERR_DIMENSION_ERROR; sz = re_m->rows * re_m->columns; for (i = 0; i < sz; i++) if (re_m->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < sz; i++) if (im_m->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; cm = (vartype_complexmatrix *) new_complexmatrix(re_m->rows, re_m->columns); if (cm == NULL) return ERR_INSUFFICIENT_MEMORY; if (flags.f.polar) { for (i = 0; i < sz; i++) { generic_p2r(re_m->array->data[i], im_m->array->data[i], &cm->array->data[2 * i], &cm->array->data[2 * i + 1]); } } else { for (i = 0; i < sz; i++) { cm->array->data[2 * i] = re_m->array->data[i]; cm->array->data[2 * i + 1] = im_m->array->data[i]; } } free_vartype(reg_lastx); reg_lastx = reg_x; free_vartype(reg_y); reg_y = reg_z; reg_z = dup_vartype(reg_t); reg_x = (vartype *) cm; break; } } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; int4 rows = cm->rows; int4 columns = cm->columns; int4 sz = rows * columns; int4 i; vartype_realmatrix *re_m = (vartype_realmatrix *) new_realmatrix(rows, columns); vartype_realmatrix *im_m; if (re_m == NULL) return ERR_INSUFFICIENT_MEMORY; im_m = (vartype_realmatrix *) new_realmatrix(rows, columns); if (im_m == NULL) { free_vartype((vartype *) re_m); return ERR_INSUFFICIENT_MEMORY; } if (flags.f.polar) { for (i = 0; i < sz; i++) { phloat r, phi; generic_r2p(cm->array->data[2 * i], cm->array->data[2 * i + 1], &r, &phi); if (p_isinf(r) != 0) if (flags.f.range_error_ignore) r = POS_HUGE_PHLOAT; else { free_vartype((vartype *) re_m); free_vartype((vartype *) im_m); return ERR_OUT_OF_RANGE; } re_m->array->data[i] = r; im_m->array->data[i] = phi; } } else { for (i = 0; i < sz; i++) { re_m->array->data[i] = cm->array->data[2 * i]; im_m->array->data[i] = cm->array->data[2 * i + 1]; } } free_vartype(reg_lastx); reg_lastx = reg_x; free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = (vartype *) re_m; reg_x = (vartype *) im_m; break; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; } if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_sto(arg_struct *arg) { return generic_sto(arg, 0); } int docmd_sto_div(arg_struct *arg) { return generic_sto(arg, '/'); } int docmd_sto_mul(arg_struct *arg) { return generic_sto(arg, '*'); } int docmd_sto_sub(arg_struct *arg) { return generic_sto(arg, '-'); } int docmd_sto_add(arg_struct *arg) { return generic_sto(arg, '+'); } int docmd_rcl(arg_struct *arg) { vartype *v; int err = generic_rcl(arg, &v); if (err == ERR_NONE) recall_result(v); return err; } /* Temporary for use by docmd_rcl_div() & docmd_rcl_mul() */ static vartype *temp_v; static void docmd_rcl_div_completion(int error, vartype *res) { free_vartype(temp_v); if (error == ERR_NONE) unary_result(res); } int docmd_rcl_div(arg_struct *arg) { int err = generic_rcl(arg, &temp_v); if (err != ERR_NONE) return err; return generic_div(temp_v, reg_x, docmd_rcl_div_completion); } static void docmd_rcl_mul_completion(int error, vartype *res) { free_vartype(temp_v); if (error == ERR_NONE) unary_result(res); } int docmd_rcl_mul(arg_struct *arg) { int err = generic_rcl(arg, &temp_v); if (err != ERR_NONE) return err; return generic_mul(temp_v, reg_x, docmd_rcl_mul_completion); } int docmd_rcl_sub(arg_struct *arg) { vartype *v, *w; int err = generic_rcl(arg, &v); if (err != ERR_NONE) return err; err = generic_sub(v, reg_x, &w); free_vartype(v); if (err == ERR_NONE) unary_result(w); return err; } int docmd_rcl_add(arg_struct *arg) { vartype *v, *w; int err = generic_rcl(arg, &v); if (err != ERR_NONE) return err; err = generic_add(v, reg_x, &w); free_vartype(v); if (err == ERR_NONE) unary_result(w); return err; } int docmd_fix(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num > 11) return ERR_INVALID_DATA; flags.f.digits_bit3 = (num & 8) != 0; flags.f.digits_bit2 = (num & 4) != 0; flags.f.digits_bit1 = (num & 2) != 0; flags.f.digits_bit0 = (num & 1) != 0; flags.f.fix_or_all = 1; flags.f.eng_or_all = 0; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_sci(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num > 11) return ERR_INVALID_DATA; flags.f.digits_bit3 = (num & 8) != 0; flags.f.digits_bit2 = (num & 4) != 0; flags.f.digits_bit1 = (num & 2) != 0; flags.f.digits_bit0 = (num & 1) != 0; flags.f.fix_or_all = 0; flags.f.eng_or_all = 0; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_eng(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num > 11) return ERR_INVALID_DATA; flags.f.digits_bit3 = (num & 8) != 0; flags.f.digits_bit2 = (num & 4) != 0; flags.f.digits_bit1 = (num & 2) != 0; flags.f.digits_bit0 = (num & 1) != 0; flags.f.fix_or_all = 0; flags.f.eng_or_all = 1; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_all(arg_struct *arg) { flags.f.digits_bit3 = 0; flags.f.digits_bit2 = 0; flags.f.digits_bit1 = 0; flags.f.digits_bit0 = 0; flags.f.fix_or_all = 1; flags.f.eng_or_all = 1; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_null(arg_struct *arg) { return ERR_NONE; } int docmd_asto(arg_struct *arg) { /* I'm lazy enough to spot that ASTO has exactly the same side effects as * STO with the first 6 characters of ALPHA in ST X (as long as the * destination is not ST X or IND ST X). If you're also aware that * new_string() constructs a string of no more than 6 characters, * you'll see that this code does the job quite nicely. */ vartype *s = new_string(reg_alpha, reg_alpha_length); if (s == NULL) return ERR_INSUFFICIENT_MEMORY; if (arg->type == ARGTYPE_STK && arg->val.stk == 'X') { // Special case for ASTO ST X free_vartype(reg_x); reg_x = s; return ERR_NONE; } else { int err; if (arg->type == ARGTYPE_IND_STK && arg->val.stk == 'X') { // Special case for ASTO IND ST X err = resolve_ind_arg(arg); if (err != ERR_NONE) { free_vartype(s); return err; } } vartype *saved_x = reg_x; reg_x = s; err = docmd_sto(arg); free_vartype(s); reg_x = saved_x; return err; } } int docmd_arcl(arg_struct *arg) { /* Do some contortions to use docmd_rcl() to get the variable, * and do it without affecting the stack. */ vartype *saved_x = dup_vartype(reg_x); if (saved_x == NULL) return ERR_INSUFFICIENT_MEMORY; int saved_nostacklift = flags.f.stack_lift_disable; int saved_trace = flags.f.trace_print; flags.f.stack_lift_disable = 1; flags.f.trace_print = 0; int err = docmd_rcl(arg); flags.f.stack_lift_disable = saved_nostacklift; flags.f.trace_print = saved_trace; vartype *v; if (err != ERR_NONE) { free_vartype(saved_x); return err; } else { v = reg_x; reg_x = saved_x; } /* Convert the variable to a string, using the same conversion * used when displaying the variable in 'normal' mode -- except * for strings, which we append with no quotes. */ if (v->type == TYPE_STRING) { vartype_string *s = (vartype_string *) v; append_alpha_string(s->text, s->length, 0); } else { char buf[100]; int bufptr = vartype2string(v, buf, 100); append_alpha_string(buf, bufptr, 0); } free_vartype(v); if (flags.f.alpha_mode && !program_running()) set_alpha_entry(true); if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); return ERR_NONE; } int docmd_cla(arg_struct *arg) { reg_alpha_length = 0; set_alpha_entry(false); return ERR_NONE; } int docmd_deg(arg_struct *arg) { flags.f.grad = 0; flags.f.rad = 0; shell_annunciators(-1, -1, -1, -1, 0, 0); return ERR_NONE; } int docmd_rad(arg_struct *arg) { flags.f.grad = 0; flags.f.rad = 1; shell_annunciators(-1, -1, -1, -1, 0, 1); return ERR_NONE; } int docmd_grad(arg_struct *arg) { flags.f.grad = 1; flags.f.rad = 0; shell_annunciators(-1, -1, -1, -1, 1, 1); return ERR_NONE; } int docmd_rect(arg_struct *arg) { flags.f.polar = 0; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_polar(arg_struct *arg) { flags.f.polar = 1; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_size(arg_struct *arg) { if (arg->type != ARGTYPE_NUM) return ERR_INVALID_TYPE; return dimension_array("REGS", 4, arg->val.num, 1); } int docmd_quiet(arg_struct *arg) { flags.f.audio_enable = !flags.f.audio_enable; return ERR_NONE; } int docmd_cpxres(arg_struct *arg) { flags.f.real_result_only = 0; return ERR_NONE; } int docmd_realres(arg_struct *arg) { flags.f.real_result_only = 1; return ERR_NONE; } int docmd_keyasn(arg_struct *arg) { flags.f.local_label = 0; return ERR_NONE; } int docmd_lclbl(arg_struct *arg) { flags.f.local_label = 1; return ERR_NONE; } int docmd_rdxdot(arg_struct *arg) { flags.f.decimal_point = 1; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_rdxcomma(arg_struct *arg) { flags.f.decimal_point = 0; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_clsigma(arg_struct *arg) { vartype *regs = recall_var("REGS", 4); vartype_realmatrix *r; int4 first = mode_sigma_reg; int4 last = first + (flags.f.all_sigma ? 13 : 6); int4 size, i; if (regs == NULL) return ERR_SIZE_ERROR; if (regs->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; r = (vartype_realmatrix *) regs; size = r->rows * r->columns; if (last > size) return ERR_SIZE_ERROR; for (i = first; i < last; i++) { r->array->is_string[i] = 0; r->array->data[i] = 0; } flags.f.log_fit_invalid = 0; flags.f.exp_fit_invalid = 0; flags.f.pwr_fit_invalid = 0; return ERR_NONE; } int docmd_clp(arg_struct *arg) { return clear_prgm(arg); } int docmd_clv(arg_struct *arg) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type == ARGTYPE_STR) { /* When EDITN is active, don't allow the matrix being * edited to be deleted. */ if (matedit_mode == 3 && arg->length == matedit_length) { bool equal = true; for (int i = 0; i < arg->length; i++) if (arg->val.text[i] != matedit_name[i]) { equal = false; break; } if (equal) return ERR_RESTRICTED_OPERATION; } purge_var(arg->val.text, arg->length); remove_shadow(arg->val.text, arg->length); return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_clst(arg_struct *arg) { free_vartype(reg_x); free_vartype(reg_y); free_vartype(reg_z); free_vartype(reg_t); reg_x = new_real(0); reg_y = new_real(0); reg_z = new_real(0); reg_t = new_real(0); return ERR_NONE; } int docmd_clrg(arg_struct *arg) { vartype *regs = recall_var("REGS", 4); if (regs == NULL) return ERR_NONEXISTENT; if (regs->type == TYPE_REALMATRIX) { vartype_realmatrix *rm; int4 sz, i; if (!disentangle(regs)) return ERR_INSUFFICIENT_MEMORY; rm = (vartype_realmatrix *) regs; sz = rm->rows * rm->columns; for (i = 0; i < sz; i++) rm->array->data[i] = 0; for (i = 0; i < sz; i++) rm->array->is_string[i] = 0; return ERR_NONE; } else if (regs->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm; int4 sz, i; if (!disentangle(regs)) return ERR_INSUFFICIENT_MEMORY; cm = (vartype_complexmatrix *) regs; sz = 2 * cm->rows * cm->columns; for (i = 0; i < sz; i++) cm->array->data[i] = 0; return ERR_NONE; } else { /* Should not happen; STO does not allow anything other * than a matrix to be stored in 'REGS'. */ return ERR_INTERNAL_ERROR; } } int docmd_del(arg_struct *arg) { if (arg->type != ARGTYPE_NUM) return ERR_INVALID_TYPE; clear_prgm_lines(arg->val.num); return ERR_NONE; } int docmd_clkeys(arg_struct *arg) { clear_custom_menu(); return ERR_NONE; } int docmd_cllcd(arg_struct *arg) { clear_display(); flush_display(); flags.f.message = 1; flags.f.two_line_message = 1; return ERR_NONE; } int docmd_clmenu(arg_struct *arg) { clear_prgm_menu(); return ERR_NONE; } int docmd_clall(arg_struct *arg) { vartype *regs; /* Clear all registers */ free_vartype(reg_x); free_vartype(reg_y); free_vartype(reg_z); free_vartype(reg_t); free_vartype(reg_lastx); reg_x = new_real(0); reg_y = new_real(0); reg_z = new_real(0); reg_t = new_real(0); reg_lastx = new_real(0); reg_alpha_length = 0; /* Exit all menus (even leaving the matrix editor * is guaranteed not to fail because there's 0 in X, * and that is always valid). */ set_menu(MENULEVEL_APP, MENU_NONE); flags.f.prgm_mode = 0; /* Clear all programs and variables */ clear_all_prgms(); goto_dot_dot(); purge_all_vars(); regs = new_realmatrix(25, 1); store_var("REGS", 4, regs); /* Clear the CUSTOM and programmable menus */ clear_custom_menu(); clear_prgm_menu(); return ERR_NONE; } int docmd_percent(arg_struct *arg) { if (reg_x->type == TYPE_STRING || reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL || reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; else { vartype_real *x = (vartype_real *) reg_x; vartype_real *y = (vartype_real *) reg_y; phloat res = x->x * y->x; if (p_isinf(res)) { /* Try different evaluation order */ res = (x->x / 100.0) * y->x; if (p_isinf(res)) return ERR_OUT_OF_RANGE; } else res /= 100.0; vartype *new_x = new_real(res); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = new_x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } } int docmd_pi(arg_struct *arg) { vartype *v = new_real(PI); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } static int mappable_to_deg(phloat x, phloat *y) { phloat r; int inf; r = rad_to_deg(x); if ((inf = p_isinf(r)) != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *y = r; return ERR_NONE; } int docmd_to_deg(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_to_deg, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_to_rad(phloat x, phloat *y) { *y = deg_to_rad(x); return ERR_NONE; } int docmd_to_rad(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_to_rad, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_to_hr(phloat x, phloat *y) { int neg = x < 0; phloat res; #ifdef BCD_MATH const phloat point01(1, 100); const phloat point36(36, 100); #endif if (neg) x = -x; if (x == x + 1) res = x; else { #ifdef BCD_MATH if (x < point01) res = x / point36; else { phloat xh = floor(x); phloat t = (x - xh) * 100; phloat xm = floor(t); phloat xs = (t - xm) * 100; res = xh + xm / 60 + xs / 3600; } #else if (x < 0.01) res = x / 0.36; else { int8 ix, ixhr; phloat h = floor(x); x -= h; //ix = (int8) (x * 1000000000000.0 + 0.5); x = (x * 1000000000000.0 + 0.5); ix = to_int8(x); ixhr = ix % LL(10000000000); ix /= LL(10000000000); ixhr += (ix % 100) * LL(6000000000); res = h + ixhr / 360000000000.0; } #endif } *y = neg ? -res : res; return ERR_NONE; } int docmd_to_hr(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_to_hr, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_to_hms(phloat x, phloat *y) { int neg = x < 0; phloat r, t; if (neg) x = -x; r = floor(x); x = (x - r) * 60; t = floor(x); r += t / 100 + (x - t) * 3 / 500; /* Round-off may have caused the minutes or seconds to reach 60; * detect this and fix... */ r = fix_hms(r); *y = neg ? -r : r; return ERR_NONE; } int docmd_to_hms(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_to_hms, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_to_rec(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { if (reg_y->type == TYPE_REAL || reg_y->type == TYPE_COMPLEX) { /* Note: the strange behavior re: real number in X, and * complex number in Y, is for bug-compatibility with the * real HP-42S. It's not very useful, but it doesn't really * hurt either, I suppose. If I ever implement an "enhanced" * Free42 mode, I'll probably make that combination of * arguments return ERR_INVALID_TYPE, or at least offer the * option of selecting that behavior. */ phloat r = ((vartype_real *) reg_x)->x; phloat phi = reg_y->type == TYPE_REAL ? ((vartype_real *) reg_y)->x : ((vartype_complex *) reg_y)->re; phloat x, y; vartype *vx, *vy; generic_p2r(r, phi, &x, &y); vx = new_real(x); if (vx == NULL) return ERR_INSUFFICIENT_MEMORY; vy = new_real(y); if (vy == NULL) { free_vartype(vx); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_y); reg_y = vy; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = vx; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else /* The original HP-42S has a bug here: it accepts real and complex * matrices, and also complex numbers, in Y, when X is real. * I allow the Y-is-complex while X-is-real behavior (see above), * but the Y-is-matrix case does not yield anything useful or even * recognizable on the real HP-42S, so I feel I'm not going to * break anything (that wasn't broken to begin with) by returning * an error message here. */ return ERR_INVALID_TYPE; } else if (reg_x->type == TYPE_COMPLEX) { vartype_complex *c = (vartype_complex *) reg_x; phloat x, y; vartype *v; generic_p2r(c->re, c->im, &x, &y); v = new_complex(x, y); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_to_pol(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { if (reg_y->type == TYPE_REAL || reg_y->type == TYPE_COMPLEX) { /* Note: the strange behavior re: real number in X, and * complex number in Y, is for bug-compatibility with the * real HP-42S. It's not very useful, but it doesn't really * hurt either, I suppose. If I ever implement an "enhanced" * Free42 mode, I'll probably make that combination of * arguments return ERR_INVALID_TYPE, or at least offer the * option of selecting that behavior. */ phloat x = ((vartype_real *) reg_x)->x; phloat y = reg_y->type == TYPE_REAL ? ((vartype_real *) reg_y)->x : ((vartype_complex *) reg_y)->re; phloat r, phi; vartype *vx, *vy; generic_r2p(x, y, &r, &phi); if (p_isinf(r)) { if (flags.f.range_error_ignore) r = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } vx = new_real(r); if (vx == NULL) return ERR_INSUFFICIENT_MEMORY; vy = new_real(phi); if (vy == NULL) { free_vartype(vx); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_y); reg_y = vy; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = vx; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else /* The original HP-42S has a bug here: it accepts real and complex * matrices, and also complex numbers, in Y, when X is real. * I allow the Y-is-complex while X-is-real behavior (see above), * but the Y-is-matrix case does not yield anything useful or even * recognizable on the real HP-42S, so I feel I'm not going to * break anything (that wasn't broken to begin with) by returning * an error message here. */ return ERR_INVALID_TYPE; } else if (reg_x->type == TYPE_COMPLEX) { vartype_complex *c = (vartype_complex *) reg_x; phloat r, phi; vartype *v; generic_r2p(c->re, c->im, &r, &phi); if (p_isinf(r)) { if (flags.f.range_error_ignore) r = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_complex(r, phi); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_ip(phloat x, phloat *y) { if (x < 0) *y = -floor(-x); else *y = floor(x); return ERR_NONE; } int docmd_ip(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_ip, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_fp(phloat x, phloat *y) { if (x < 0) *y = x + floor(-x); else *y = x - floor(x); return ERR_NONE; } int docmd_fp(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_fp, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static phloat rnd_multiplier; static int mappable_rnd_r(phloat x, phloat *y) { if (flags.f.fix_or_all) { if (flags.f.eng_or_all) *y = x; else { phloat t = x; int neg = t < 0; if (neg) t = -t; if (t > 1e20) *y = x; else { t = floor(t * rnd_multiplier + 0.5) / rnd_multiplier; *y = neg ? -t : t; } } return ERR_NONE; } else { phloat t = x; int neg; phloat scale; if (t == 0) *y = 0; else { if (t < 0) { t = -t; neg = 1; } else neg = 0; scale = pow(10, floor(log10(t))); if (scale > t) { /* Theoretically, this can't happen, but due to limited * precision, the log of something like 9.999999999+ * might be computed as 1 instead of 0.99999999+, * hence the extra check. */ scale /= 10; } t = floor(t / scale * rnd_multiplier + 0.5) / rnd_multiplier * scale; if (p_isinf(t)) { if (flags.f.range_error_ignore) *y = neg ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } else *y = neg ? -t : t; } return ERR_NONE; } } static int mappable_rnd_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { int err = mappable_rnd_r(xre, yre); if (err != ERR_NONE) return err; return mappable_rnd_r(xim, yim); } int docmd_rnd(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else { vartype *v; int err; int digits = 0; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; rnd_multiplier = pow(10.0, digits); err = map_unary(reg_x, &v, mappable_rnd_r, mappable_rnd_c); if (err == ERR_NONE) unary_result(v); return err; } } int docmd_abs(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { vartype *r; phloat x = ((vartype_real *) reg_x)->x; if (x < 0) x = -x; r = new_real(x); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(r); return ERR_NONE; } case TYPE_COMPLEX: { vartype *r; phloat a = hypot(((vartype_complex *) reg_x)->re, ((vartype_complex *) reg_x)->im); if (p_isinf(a) == 0) r = new_real(a); else if (flags.f.range_error_ignore) r = new_real(POS_HUGE_PHLOAT); else return ERR_OUT_OF_RANGE; if (r == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(r); return ERR_NONE; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; case TYPE_REALMATRIX: { if (!is_pure_real(reg_x)) return ERR_ALPHA_DATA_IS_INVALID; vartype_realmatrix *src; vartype_realmatrix *dst; int4 size, i; src = (vartype_realmatrix *) reg_x; dst = (vartype_realmatrix *) new_realmatrix(src->rows, src->columns); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; size = src->rows * src->columns; for (i = 0; i < size; i++) { phloat x = src->array->data[i]; if (x < 0) x = -x; dst->array->data[i] = x; } unary_result((vartype *) dst); return ERR_NONE; } case TYPE_COMPLEXMATRIX: return ERR_INVALID_TYPE; default: return ERR_INTERNAL_ERROR; } } static int mappable_sign(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat h = hypot(xre, xim); if (h == 0) { *yre = 0; *yim = 0; } else if (p_isinf(h) == 0) { *yre = xre / h; *yim = xim / h; } else { xre /= 10000; xim /= 10000; h = hypot(xre, xim); *yre = xre / h; *yim = xim / h; } return ERR_NONE; } int docmd_sign(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { /* Note that this implementation has sign(0) = 1, which is not * how most programming languages handle zero. This is a holdover * from the HP-41C, where SIGN did double duty for recognizing * strings: they would return zero and could be recognized in this * way. * The HP-42S has the STR? function for recognizing strings, but * it still supports the HP-41C's SIGN behavior for compatibility. * And so does Free42, of course. */ vartype *r = new_real(((vartype_real *) reg_x)->x < 0 ? -1 : 1); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(r); return ERR_NONE; } case TYPE_STRING: { vartype *r = new_real(0); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(r); return ERR_NONE; } case TYPE_REALMATRIX: { /* Can't use the mapper here because it won't handle strings. */ vartype_realmatrix *src; vartype_realmatrix *dst; int4 size, i; src = (vartype_realmatrix *) reg_x; dst = (vartype_realmatrix *) new_realmatrix(src->rows, src->columns); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; size = src->rows * src->columns; for (i = 0; i < size; i++) { if (src->array->is_string[i]) dst->array->data[i] = 0; else dst->array->data[i] = src->array->data[i] < 0 ? -1 : 1; } unary_result((vartype *) dst); return ERR_NONE; } case TYPE_COMPLEX: case TYPE_COMPLEXMATRIX: { vartype *v; map_unary(reg_x, &v, NULL, mappable_sign); unary_result((vartype *) v); return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } } int docmd_mod(arg_struct *arg) { if (reg_x->type == TYPE_REAL && reg_y->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; phloat y = ((vartype_real *) reg_y)->x; phloat res; vartype *v; if (x == 0) res = y; else if (y == 0) res = 0; else { res = fmod(y, x); if (res != 0 && ((x > 0 && y < 0) || (x < 0 && y > 0))) res += x; } v = new_real(res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } free42-nologo-1.4.77/common/core_commands1.h000644 000765 000024 00000006213 12110237247 021166 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS1_H #define CORE_COMMANDS1_H 1 #include "free42.h" #include "core_globals.h" int docmd_clx(arg_struct *arg); int docmd_enter(arg_struct *arg); int docmd_swap(arg_struct *arg); int docmd_rdn(arg_struct *arg); int docmd_chs(arg_struct *arg); int docmd_div(arg_struct *arg); int docmd_mul(arg_struct *arg); int docmd_sub(arg_struct *arg); int docmd_add(arg_struct *arg); int docmd_lastx(arg_struct *arg); int docmd_complex(arg_struct *arg); int docmd_sto(arg_struct *arg); int docmd_sto_div(arg_struct *arg); int docmd_sto_mul(arg_struct *arg); int docmd_sto_add(arg_struct *arg); int docmd_sto_sub(arg_struct *arg); int docmd_rcl(arg_struct *arg); int docmd_rcl_div(arg_struct *arg); int docmd_rcl_mul(arg_struct *arg); int docmd_rcl_sub(arg_struct *arg); int docmd_rcl_add(arg_struct *arg); int docmd_fix(arg_struct *arg); int docmd_sci(arg_struct *arg); int docmd_eng(arg_struct *arg); int docmd_all(arg_struct *arg); int docmd_null(arg_struct *arg); int docmd_asto(arg_struct *arg); int docmd_arcl(arg_struct *arg); int docmd_cla(arg_struct *arg); int docmd_deg(arg_struct *arg); int docmd_rad(arg_struct *arg); int docmd_grad(arg_struct *arg); int docmd_rect(arg_struct *arg); int docmd_polar(arg_struct *arg); int docmd_size(arg_struct *arg); int docmd_quiet(arg_struct *arg); int docmd_cpxres(arg_struct *arg); int docmd_realres(arg_struct *arg); int docmd_keyasn(arg_struct *arg); int docmd_lclbl(arg_struct *arg); int docmd_rdxdot(arg_struct *arg); int docmd_rdxcomma(arg_struct *arg); int docmd_clsigma(arg_struct *arg); int docmd_clp(arg_struct *arg); int docmd_clv(arg_struct *arg); int docmd_clst(arg_struct *arg); int docmd_clrg(arg_struct *arg); int docmd_del(arg_struct *arg); int docmd_clkeys(arg_struct *arg); int docmd_cllcd(arg_struct *arg); int docmd_clmenu(arg_struct *arg); int docmd_clall(arg_struct *arg); int docmd_percent(arg_struct *arg); int docmd_pi(arg_struct *arg); int docmd_to_deg(arg_struct *arg); int docmd_to_rad(arg_struct *arg); int docmd_to_hr(arg_struct *arg); int docmd_to_hms(arg_struct *arg); int docmd_to_rec(arg_struct *arg); int docmd_to_pol(arg_struct *arg); int docmd_ip(arg_struct *arg); int docmd_fp(arg_struct *arg); int docmd_rnd(arg_struct *arg); int docmd_abs(arg_struct *arg); int docmd_sign(arg_struct *arg); int docmd_mod(arg_struct *arg); #ifdef BIGSTACK int docmd_drop(arg_struct *arg); #endif #endif free42-nologo-1.4.77/common/core_commands2.cc000644 000765 000024 00000143166 12110237247 021336 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands1.h" #include "core_commands2.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_math1.h" #include "core_math2.h" #include "core_sto_rcl.h" #include "core_variables.h" #include "shell.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 2 */ /********************************************************/ static const char *virtual_flags = /* 00-49 */ "00000000000000000000000000010000000000000000011101" /* 50-99 */ "00010000000000010000000001000000000000000000000000"; int docmd_sf(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (num >= 36 && num <= 80) return ERR_RESTRICTED_OPERATION; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_SF, num); else { flags.farray[num] = 1; if (num == 30) /* This is the stack_lift_disable flag. * Since we automatically enable stack lift after every command, * unless mode_disable_stack_lift is set, we must intervene for * this to actually have the intended effect. */ mode_disable_stack_lift = true; return ERR_NONE; } } int docmd_cf(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (num >= 36 && num <= 80) return ERR_RESTRICTED_OPERATION; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_CF, num); else { flags.farray[num] = 0; return ERR_NONE; } } int docmd_fs_t(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_FS_T, num); else return flags.farray[num] ? ERR_YES : ERR_NO; } int docmd_fc_t(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_FC_T, num); else return flags.farray[num] ? ERR_NO : ERR_YES; } int docmd_fsc_t(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (num >= 36 && num <= 80) return ERR_RESTRICTED_OPERATION; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_FSC_T, num); else { err = flags.farray[num] ? ERR_YES : ERR_NO; flags.farray[num] = 0; return err; } } int docmd_fcc_t(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 100) return ERR_NONEXISTENT; if (num >= 36 && num <= 80) return ERR_RESTRICTED_OPERATION; if (virtual_flags[num] == '1') return virtual_flag_handler(FLAGOP_FCC_T, num); else { err = flags.farray[num] ? ERR_NO : ERR_YES; flags.farray[num] = 0; return err; } } int docmd_comb(arg_struct *arg) { if (reg_x->type == TYPE_REAL && reg_y->type == TYPE_REAL) { phloat y = ((vartype_real *) reg_y)->x; phloat x = ((vartype_real *) reg_x)->x; phloat r = 1, q = 1; vartype *v; if (x < 0 || x != floor(x) || x == x - 1 || y < 0 || y != floor(y)) return ERR_INVALID_DATA; if (y < x) return ERR_INVALID_DATA; if (x > y / 2) x = y - x; while (q <= x) { r *= y--; if (p_isinf(r)) { if (flags.f.range_error_ignore) { r = POS_HUGE_PHLOAT; break; } else return ERR_OUT_OF_RANGE; } r /= q++; } v = new_real(r); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_perm(arg_struct *arg) { if (reg_x->type == TYPE_REAL && reg_y->type == TYPE_REAL) { phloat y = ((vartype_real *) reg_y)->x; phloat x = ((vartype_real *) reg_x)->x; phloat r = 1; vartype *v; if (x < 0 || x != floor(x) || x == x - 1 || y < 0 || y != floor(y)) return ERR_INVALID_DATA; if (y < x) return ERR_INVALID_DATA; while (x > 0) { r *= y--; if (p_isinf(r)) { if (flags.f.range_error_ignore) { r = POS_HUGE_PHLOAT; break; } else return ERR_OUT_OF_RANGE; } x--; } v = new_real(r); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_fact(phloat x, phloat *y) { phloat f = 1; if (x < 0 || x != floor(x)) return ERR_INVALID_DATA; while (x > 1) { f *= x--; if (p_isinf(f)) { if (flags.f.range_error_ignore) { *y = POS_HUGE_PHLOAT; return ERR_NONE; } else return ERR_OUT_OF_RANGE; } } *y = f; return ERR_NONE; } int docmd_fact(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_fact, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_gamma(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_COMPLEX || reg_x->type == TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; else { vartype *v; int err = map_unary(reg_x, &v, math_gamma, NULL); if (err == ERR_NONE) unary_result(v); return err; } } int docmd_ran(arg_struct *arg) { vartype *v = new_real(math_random()); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_seed(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; int i; if (x == 0) random_number = shell_random_seed(); else random_number = x - floor(x); for (i = 0; i < 10; i++) math_random(); return ERR_NONE; } else if (arg->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_lbl(arg_struct *arg) { if (!flags.f.message) fly_goose(); return ERR_NONE; } int docmd_rtn(arg_struct *arg) { if (program_running()) { int newprgm; int4 newpc; pop_rtn_addr(&newprgm, &newpc); if (newprgm == -3) return return_to_integ(0); else if (newprgm == -2) return return_to_solve(0); else if (newprgm == -1) { if (pc >= prgms[current_prgm].size) /* It's an END; go to line 0 */ pc = -1; return ERR_STOP; } else { current_prgm = newprgm; pc = newpc; return ERR_NONE; } } else { clear_all_rtns(); pc = -1; return ERR_NONE; } } int docmd_input(arg_struct *arg) { vartype *v; int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } err = generic_rcl(arg, &v); if (err == ERR_NONEXISTENT) { v = new_real(0); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } else if (err != ERR_NONE) return err; input_length = 0; input_arg = *arg; if (arg->type == ARGTYPE_NUM) { char2buf(input_name, 11, &input_length, 'R'); if (arg->val.num < 10) char2buf(input_name, 11, &input_length, '0'); input_length += int2string(arg->val.num, input_name + input_length, 11 - input_length); } else if (arg->type == ARGTYPE_STK) { string2buf(input_name, 11, &input_length, "ST ", 3); char2buf(input_name, 11, &input_length, arg->val.stk); } else if (arg->type == ARGTYPE_STR) { string2buf(input_name, 11, &input_length, arg->val.text, arg->length); } else { free_vartype(v); return ERR_INVALID_TYPE; } recall_result(v); return ERR_STOP; } int view_helper(arg_struct *arg, int print) { int err; char buf[22]; int bufptr = 0, part2; vartype *v; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } switch (arg->type) { case ARGTYPE_NUM: { int num = arg->val.num; char2buf(buf, 22, &bufptr, 'R'); if (num < 10) char2buf(buf, 22, &bufptr, '0'); bufptr += int2string(num, buf + bufptr, 22 - bufptr); break; } case ARGTYPE_STK: string2buf(buf, 22, &bufptr, "ST ", 3); char2buf(buf, 22, &bufptr, arg->val.stk); break; case ARGTYPE_STR: string2buf(buf, 22, &bufptr, arg->val.text, arg->length); break; } char2buf(buf, 22, &bufptr, '='); part2 = bufptr; err = generic_rcl(arg, &v); if (err != ERR_NONE) return err; bufptr += vartype2string(v, buf + bufptr, 22 - bufptr); free_vartype(v); clear_row(0); draw_string(0, 0, buf, bufptr); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; if (print && (flags.f.printer_enable || !program_running())) { if (flags.f.printer_exists) print_wide(buf, part2, buf + part2, bufptr - part2); else return ERR_STOP; } return ERR_NONE; } int docmd_view(arg_struct *arg) { return view_helper(arg, 1); } static void aview_helper() { #define DISP_ROWS 2 #define DISP_COLUMNS 22 int line_start[DISP_ROWS]; int line_length[DISP_ROWS]; int line = 0; int i; line_start[0] = 0; for (i = 0; i < reg_alpha_length; i++) { if (reg_alpha[i] == 10) { if (line == DISP_ROWS - 1) break; line_length[line] = i - line_start[line]; line_start[++line] = i + 1; } else if (i == line_start[line] + DISP_COLUMNS) { if (line == DISP_ROWS - 1) break; line_length[line] = i - line_start[line]; line_start[++line] = i; } } line_length[line] = i - line_start[line]; flags.f.message = 1; flags.f.two_line_message = line == 1; clear_row(0); if (flags.f.two_line_message || program_running()) clear_row(1); for (i = 0; i <= line; i++) draw_string(0, i, reg_alpha + line_start[i], line_length[i]); flush_display(); } int docmd_aview(arg_struct *arg) { aview_helper(); if (flags.f.printer_enable || !program_running()) { if (flags.f.printer_exists) docmd_pra(arg); else return ERR_STOP; } return ERR_NONE; } int docmd_xeq(arg_struct *arg) { if (program_running()) { int oldprgm = current_prgm; int4 oldpc = pc; int error = docmd_gto(arg); if (error != ERR_NONE) return error; return push_rtn_addr(oldprgm, oldpc); } else { int err = docmd_gto(arg); if (err != ERR_NONE) return err; clear_all_rtns(); return ERR_RUN; } } int docmd_prompt(arg_struct *arg) { aview_helper(); if (flags.f.printer_enable && flags.f.printer_exists && (flags.f.trace_print || flags.f.normal_print)) docmd_pra(arg); return ERR_STOP; } int docmd_pse(arg_struct *arg) { if (program_running()) { int saved_command = pending_command; pending_command = CMD_NONE; redisplay(); pending_command = saved_command; #ifdef OLD_PSE shell_delay(1000); if (mode_goose >= 0) mode_goose = -1 - mode_goose; #else mode_pause = true; #endif } return ERR_NONE; } static int generic_loop_helper(phloat *x, bool isg) { phloat t; int8 i, j, k; int s; if (*x == *x + 1) { /* Too big to do anything useful with; this is what the real * HP-42S does in this case: */ return isg == (*x < 0) ? ERR_YES : ERR_NO; } /* Break number up as follows: II.JJJKKRRRRR * The sign goes with I; everything else is considered positive. */ t = *x; if (t < 0) { t = -t; s = -1; } else s = 1; i = to_int8(t); t = (t - i) * 100000; /* The 0.0000005 is a precaution to prevent the loop increment * value from being taken to be 1 lower than what the user intended; * this can happen because the decimal fractions used here cannot, * in general, be represented exactly in binary, so that what should * be 10.00902 may actually end up being approximated as something * fractionally lower -- and 10.0090199999999+ would be interpreted * as having a loop increment of 1, not the 2 that was intended. * By adding 0.0000005 before truncating, we effectively round to * 7 decimals, which is all that a real HP-42S would have left after * the multiplication by 100000. So, we sacrifice some of the range * of an IEEE-754 double, but maintain HP-42S compatibility. */ #ifndef BCD_MATH t = t + 0.0000005; #endif k = to_int8(t); j = k / 100; k -= j * 100; if (k == 0) k = 1; /* Update the 'real' loop control value separately from the components * we have just separated out. I'm very paranoid about cumulative errors, * so I don't rebuild the loop control value from i, j, k, etc. * This way is computationally cheaper, anyway. */ if (isg) { if (*x < 0 && *x > -k) *x = -(*x) + k - 2 * i; else *x += k; } else { if (*x > 0 && *x < k) *x = -(*x) - k + 2 * i; else *x -= k; } /* Now we do what you would expect ISG/DSE to do... */ if (isg) { if (s == -1) i = k - i; else i = k + i; return i > j ? ERR_NO : ERR_YES; } else { if (s == -1) i = -i - k; else i = i - k; return i <= j ? ERR_NO : ERR_YES; } } static int generic_loop(arg_struct *arg, bool isg) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } switch (arg->type) { case ARGTYPE_NUM: { vartype *regs = recall_var("REGS", 4); if (regs == NULL) return ERR_SIZE_ERROR; else if (regs->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) regs; int4 size = rm->rows * rm->columns; int4 index = arg->val.num; if (index >= size) return ERR_SIZE_ERROR; if (rm->array->is_string[index]) return ERR_ALPHA_DATA_IS_INVALID; else { if (!disentangle(regs)) return ERR_INSUFFICIENT_MEMORY; return generic_loop_helper(&rm->array->data[index], isg); } } else if (regs->type == TYPE_COMPLEXMATRIX) { return ERR_INVALID_TYPE; } else { /* This should never happen; STO should prevent * "REGS" from being any other type than a real or * complex matrix. */ return ERR_INTERNAL_ERROR; } } case ARGTYPE_STK: { vartype *v; switch (arg->val.stk) { case 'X': v = reg_x; break; case 'Y': v = reg_y; break; case 'Z': v = reg_z; break; case 'T': v = reg_t; break; case 'L': v = reg_lastx; break; } if (v->type == TYPE_REAL) return generic_loop_helper(&((vartype_real *) v)->x, isg); else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } case ARGTYPE_STR: { vartype *v = recall_var(arg->val.text, arg->length); if (v->type == TYPE_REAL) return generic_loop_helper(&((vartype_real *) v)->x, isg); else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } default: return ERR_INTERNAL_ERROR; } } int docmd_isg(arg_struct *arg) { return generic_loop(arg, true); } int docmd_dse(arg_struct *arg) { return generic_loop(arg, false); } int docmd_aip(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { char buf[44]; int size = ip2revstring(((vartype_real *) reg_x)->x, buf, 44); append_alpha_string(buf, size, 1); if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_xtoa(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x < 0) x = -x; if (x >= 256) return ERR_INVALID_DATA; append_alpha_char(to_char(x)); } else if (reg_x->type == TYPE_STRING) { vartype_string *s = (vartype_string *) reg_x; append_alpha_string(s->text, s->length, 0); } else if (reg_x->type == TYPE_REALMATRIX) { vartype_realmatrix *m = (vartype_realmatrix *) reg_x; int4 size = m->rows * m->columns; int4 i; char buf[44]; int buflen = 0; for (i = size - 1; i >= 0; i--) { if (m->array->is_string[i]) { int j; for (j = phloat_length(m->array->data[i]) - 1; j >= 0; j--) { buf[buflen++] = phloat_text(m->array->data[i])[j]; if (buflen == 44) goto done; } } else { phloat d = m->array->data[i]; if (d < 0) d = -d; if (d >= 256) buf[buflen++] = (char) 255; else buf[buflen++] = to_char(d); if (buflen == 44) goto done; } } done: append_alpha_string(buf, buflen, 1); } else return ERR_INVALID_TYPE; if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); return ERR_NONE; } int docmd_agraph(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; phloat y = ((vartype_real *) reg_y)->x; draw_pattern(x, y, reg_alpha, reg_alpha_length); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } case TYPE_COMPLEX: { phloat x = ((vartype_complex *) reg_x)->re; phloat y = ((vartype_complex *) reg_y)->im; draw_pattern(x, y, reg_alpha, reg_alpha_length); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; int4 size = 2 * cm->rows * cm->columns; int4 i; for (i = 0; i < size; i += 2) draw_pattern(cm->array->data[i], cm->array->data[i + 1], reg_alpha, reg_alpha_length); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } static void pixel_helper(phloat dx, phloat dy) { dx = dx < 0 ? -floor(-dx + 0.5) : floor(dx + 0.5); dy = dy < 0 ? -floor(-dy + 0.5) : floor(dy + 0.5); int x = dx < -132 ? -132 : dx > 132 ? 132 : to_int(dx); int y = dy < -132 ? -132 : dy > 132 ? 132 : to_int(dy); int i; int dot = 1; if (x < 0) { x = -x; if (x >= 1 && x <= 131) for (i = 0; i < 16; i++) draw_pixel(x - 1, i); dot = 0; } if (y < 0) { y = -y; if (y >= 1 && y <= 16) for (i = 0; i < 131; i++) draw_pixel(i, y - 1); dot = 0; } if (dot && x >= 1 && x <= 131 && y >= 1 && y <= 16) draw_pixel(x - 1, y - 1); } int docmd_pixel(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { if (reg_y->type == TYPE_REAL) { pixel_helper(((vartype_real *) reg_x)->x, ((vartype_real *) reg_y)->x); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } else if (reg_x->type == TYPE_COMPLEX) { pixel_helper(((vartype_complex *) reg_x)->re, ((vartype_complex *) reg_x)->im); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } else if (reg_x->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *m = (vartype_complexmatrix *) reg_x; int4 size = 2 * m->rows * m->columns; int4 i; for (i = 0; i < size; i += 2) pixel_helper(m->array->data[i], m->array->data[i + 1]); flush_display(); flags.f.message = flags.f.two_line_message = 1; return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_beep(arg_struct *arg) { tone(8); tone(5); tone(9); tone(8); return ERR_NONE; } int docmd_tone(arg_struct *arg) { int err; int4 num; err = arg_to_num(arg, &num); if (err != ERR_NONE) return err; if (num >= 10) return ERR_INVALID_DATA; tone(num); return ERR_NONE; } int docmd_mvar(arg_struct *arg) { return ERR_NONE; } int docmd_varmenu(arg_struct *arg) { int saved_prgm = current_prgm; int prgm; int4 pc; int command; arg_struct arg2; int i; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (!find_global_label(arg, &prgm, &pc)) return ERR_LABEL_NOT_FOUND; pc += get_command_length(prgm, pc); current_prgm = prgm; get_next_command(&pc, &command, &arg2, 0); current_prgm = saved_prgm; if (command != CMD_MVAR) return ERR_NO_MENU_VARIABLES; varmenu_length = arg->length; for (i = 0; i < varmenu_length; i++) varmenu[i] = arg->val.text[i]; varmenu_row = 0; varmenu_role = 0; return set_menu_return_err(MENULEVEL_APP, MENU_VARMENU); } int docmd_getkey(arg_struct *arg) { mode_getkey = true; return ERR_NONE; } int docmd_menu(arg_struct *arg) { set_menu(MENULEVEL_PLAIN, MENU_PROGRAMMABLE); mode_plainmenu_sticky = true; return ERR_NONE; } int docmd_x_eq_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x == 0 ? ERR_YES : ERR_NO; } int docmd_x_ne_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x != 0 ? ERR_YES : ERR_NO; } int docmd_x_lt_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x < 0 ? ERR_YES : ERR_NO; } int docmd_x_gt_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x > 0 ? ERR_YES : ERR_NO; } int docmd_x_le_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x <= 0 ? ERR_YES : ERR_NO; } int docmd_x_ge_0(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; return ((vartype_real *) reg_x)->x >= 0 ? ERR_YES : ERR_NO; } int docmd_x_eq_y(arg_struct *arg) { if (reg_x->type != reg_y->type) return ERR_NO; switch (reg_x->type) { case TYPE_REAL: { vartype_real *x = (vartype_real *) reg_x; vartype_real *y = (vartype_real *) reg_y; return x->x == y->x ? ERR_YES : ERR_NO; } case TYPE_COMPLEX: { vartype_complex *x = (vartype_complex *) reg_x; vartype_complex *y = (vartype_complex *) reg_y; return x->re == y->re && x->im == y->im ? ERR_YES : ERR_NO; } case TYPE_REALMATRIX: { vartype_realmatrix *x = (vartype_realmatrix *) reg_x; vartype_realmatrix *y = (vartype_realmatrix *) reg_y; int4 sz, i; if (x->rows != y->rows || x->columns != y->columns) return ERR_NO; sz = x->rows * x->columns; for (i = 0; i < sz; i++) { int xstr = x->array->is_string[i]; int ystr = y->array->is_string[i]; if (xstr != ystr) return ERR_NO; if (xstr) { if (!string_equals(phloat_text(x->array->data[i]), phloat_length(x->array->data[i]), phloat_text(y->array->data[i]), phloat_length(y->array->data[i]))) return ERR_NO; } else { if (x->array->data[i] != y->array->data[i]) return ERR_NO; } } return ERR_YES; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *x = (vartype_complexmatrix *) reg_x; vartype_complexmatrix *y = (vartype_complexmatrix *) reg_y; int4 sz, i; if (x->rows != y->rows || x->columns != y->columns) return ERR_NO; sz = 2 * x->rows * x->columns; for (i = 0; i < sz; i++) if (x->array->data[i] != y->array->data[i]) return ERR_NO; return ERR_YES; } case TYPE_STRING: { vartype_string *x = (vartype_string *) reg_x; vartype_string *y = (vartype_string *) reg_y; if (string_equals(x->text, x->length, y->text, y->length)) return ERR_YES; else return ERR_NO; } default: /* Can't happen */ return ERR_INTERNAL_ERROR; } } int docmd_x_ne_y(arg_struct *arg) { int err = docmd_x_eq_y(arg); switch (err) { case ERR_YES: return ERR_NO; case ERR_NO: return ERR_YES; default: return err; } } int docmd_x_lt_y(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; if (((vartype_real *) reg_x)->x < ((vartype_real *) reg_y)->x) return ERR_YES; else return ERR_NO; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } int docmd_x_gt_y(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; if (((vartype_real *) reg_x)->x > ((vartype_real *) reg_y)->x) return ERR_YES; else return ERR_NO; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } int docmd_x_le_y(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; if (((vartype_real *) reg_x)->x <= ((vartype_real *) reg_y)->x) return ERR_YES; else return ERR_NO; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } int docmd_x_ge_y(arg_struct *arg) { switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; if (((vartype_real *) reg_x)->x >= ((vartype_real *) reg_y)->x) return ERR_YES; else return ERR_NO; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } typedef struct { const char *text; int length; } sigma_label_spec; static const sigma_label_spec sigma_labels[] = { { "\005X=", 3 }, { "\005X^2=", 5 }, { "\005Y=", 3 }, { "\005Y^2=", 5 }, { "\005XY=", 4 }, { "N=", 2 }, { "\005LN(X)=", 7 }, { "\005LN(X)^2=", 9 }, { "\005LN(Y)=", 7 }, { "\005LN(Y)^2=", 9 }, { "\005LN(X)LN(Y)=", 12 }, { "\005XLN(Y)=", 8 }, { "\005YLN(X)=", 8 } }; int docmd_prsigma(arg_struct *arg) { vartype *regs = recall_var("REGS", 4); vartype_realmatrix *rm; int nr; int4 size, max, i; char buf[100]; int bufptr; int dispmode; int digits = 0; if (regs == NULL) return ERR_NONEXISTENT; if (regs->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; rm = (vartype_realmatrix *) regs; nr = flags.f.all_sigma ? 13 : 6; size = rm->rows * rm->columns; max = mode_sigma_reg + nr; if (max > size) return ERR_SIZE_ERROR; if (!flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; if (flags.f.fix_or_all) dispmode = flags.f.eng_or_all ? 3 : 0; else dispmode = flags.f.eng_or_all ? 2 : 1; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; shell_annunciators(-1, -1, 1, -1, -1, -1); print_text(NULL, 0, 1); for (i = 0; i < nr; i++) { int4 j = i + mode_sigma_reg; if (rm->array->is_string[j]) { bufptr = 0; char2buf(buf, 100, &bufptr, '"'); string2buf(buf, 100, &bufptr, phloat_text(rm->array->data[j]), phloat_length(rm->array->data[j])); char2buf(buf, 100, &bufptr, '"'); } else bufptr = easy_phloat2string(rm->array->data[j], buf, 100, 0); print_wide(sigma_labels[i].text, sigma_labels[i].length, buf, bufptr); } shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } int docmd_prp(arg_struct *arg) { int prgm_index; if (arg->type == ARGTYPE_LBLINDEX) prgm_index = labels[arg->val.num].prgm; else if (arg->type == ARGTYPE_STR) { if (arg->length == 0) prgm_index = current_prgm; else { int4 pc; if (!find_global_label(arg, &prgm_index, &pc)) return ERR_LABEL_NOT_FOUND; } } else return ERR_INVALID_TYPE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; return print_program(prgm_index, -1, -1, 0); } static vartype *prv_var; static int4 prv_index; static int prv_worker(int interrupted); int docmd_prv(arg_struct *arg) { if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; else { vartype *v = recall_var(arg->val.text, arg->length); char lbuf[32], rbuf[100]; int llen = 0, rlen = 0; if (v == NULL) return ERR_NONEXISTENT; if (!flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; shell_annunciators(-1, -1, 1, -1, -1, -1); string2buf(lbuf, 8, &llen, arg->val.text, arg->length); char2buf(lbuf, 8, &llen, '='); rlen = vartype2string(v, rbuf, 100); print_wide(lbuf, llen, rbuf, rlen); if (v->type == TYPE_REALMATRIX || v->type == TYPE_COMPLEXMATRIX) { prv_var = v; prv_index = 0; mode_interruptible = prv_worker; mode_stoppable = true; return ERR_INTERRUPTIBLE; } else { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } } } static int prv_worker(int interrupted) { char lbuf[32], rbuf[100]; int llen = 0, rlen = 0; int4 i, j, sz; if (interrupted) { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_STOP; } if (prv_var->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) prv_var; i = prv_index / rm->columns; j = prv_index % rm->columns; sz = rm->rows * rm->columns; llen = int2string(i + 1, lbuf, 32); char2buf(lbuf, 32, &llen, ':'); llen += int2string(j + 1, lbuf + llen, 32 - llen); char2buf(lbuf, 32, &llen, '='); if (rm->array->is_string[prv_index]) { rlen = 0; char2buf(rbuf, 100, &rlen, '"'); string2buf(rbuf, 100, &rlen, phloat_text(rm->array->data[prv_index]), phloat_length(rm->array->data[prv_index])); char2buf(rbuf, 100, &rlen, '"'); } else rlen = easy_phloat2string(rm->array->data[prv_index], rbuf, 100, 0); print_wide(lbuf, llen, rbuf, rlen); } else /* prv_var->type == TYPE_COMPLEXMATRIX) */ { vartype_complexmatrix *cm = (vartype_complexmatrix *) prv_var; vartype_complex cpx; cpx.type = TYPE_COMPLEX; i = prv_index / cm->columns; j = prv_index % cm->columns; sz = cm->rows * cm->columns; llen = int2string(i + 1, lbuf, 32); char2buf(lbuf, 32, &llen, ':'); llen += int2string(j + 1, lbuf + llen, 32 - llen); char2buf(lbuf, 32, &llen, '='); cpx.re = cm->array->data[2 * prv_index]; cpx.im = cm->array->data[2 * prv_index + 1]; rlen = vartype2string((vartype *) &cpx, rbuf, 100); print_wide(lbuf, llen, rbuf, rlen); } if (++prv_index < sz) return ERR_INTERRUPTIBLE; else { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } } int docmd_prstk(arg_struct *arg) { char buf[100]; int len; if (!flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; shell_annunciators(-1, -1, 1, -1, -1, -1); print_text(NULL, 0, 1); len = vartype2string(reg_t, buf, 100); print_wide("T=", 2, buf, len); len = vartype2string(reg_z, buf, 100); print_wide("Z=", 2, buf, len); len = vartype2string(reg_y, buf, 100); print_wide("Y=", 2, buf, len); len = vartype2string(reg_x, buf, 100); print_wide("X=", 2, buf, len); shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } int docmd_pra(arg_struct *arg) { // arg == NULL if we're called to do TRACE mode auto-print if (arg != NULL && !flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; shell_annunciators(-1, -1, 1, -1, -1, -1); if (reg_alpha_length == 0) print_text(NULL, 0, 1); else { int line_start = 0; int width = flags.f.double_wide_print ? 12 : 24; int i; for (i = 0; i < reg_alpha_length; i++) { if (reg_alpha[i] == 10) { print_text(reg_alpha + line_start, i - line_start, 1); line_start = i + 1; } else if (i == line_start + width) { print_text(reg_alpha + line_start, i - line_start, 1); line_start = i; } } if (line_start < reg_alpha_length || (line_start > 0 && reg_alpha[line_start - 1] == 10)) print_text(reg_alpha + line_start, reg_alpha_length - line_start, 1); } shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } int docmd_prx(arg_struct *arg) { // arg == NULL if we're called to do TRACE mode auto-print if (arg != NULL && !flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; else { char buf[100]; int len; shell_annunciators(-1, -1, 0, -1, -1, -1); len = vartype2string(reg_x, buf, 100); if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_STRING) print_right(buf, len, "***", 3); else { /* Normally we print X right-justified, but if it doesn't fit on * one line, we print it left-justified, because having the excess * go near the right margin looks weird and confusing. */ int left = len > (flags.f.double_wide_print ? 12 : 24); print_lines(buf, len, left); } if (arg != NULL && (reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX)) { prv_var = reg_x; prv_index = 0; mode_interruptible = prv_worker; mode_stoppable = true; return ERR_INTERRUPTIBLE; } else { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } } } static int prusr_state; static int prusr_index; static int prusr_worker(int interrupted); int docmd_prusr(arg_struct *arg) { if (!flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; else { shell_annunciators(-1, -1, 1, -1, -1, -1); print_text(NULL, 0, 1); prusr_state = 0; prusr_index = vars_count - 1; mode_interruptible = prusr_worker; mode_stoppable = true; return ERR_INTERRUPTIBLE; } } static int prusr_worker(int interrupted) { if (interrupted) { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_STOP; } if (prusr_state == 0) { char lbuf[8]; char rbuf[100]; int llen, rlen; if (prusr_index < 0) { if (vars_count > 0) print_text(NULL, 0, 1); prusr_state = 1; prusr_index = 0; goto state1; } llen = 0; string2buf(lbuf, 8, &llen, vars[prusr_index].name, vars[prusr_index].length); char2buf(lbuf, 8, &llen, '='); rlen = vartype2string(vars[prusr_index].value, rbuf, 100); print_wide(lbuf, llen, rbuf, rlen); prusr_index--; } else { char buf[13]; int len; state1: len = 0; if (prusr_index >= labels_count) { shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } if (labels[prusr_index].length == 0) { if (prusr_index == labels_count - 1) string2buf(buf, 13, &len, ".END.", 5); else string2buf(buf, 13, &len, "END", 3); } else { string2buf(buf, 13, &len, "LBL \"", 5); string2buf(buf, 13, &len, labels[prusr_index].name, labels[prusr_index].length); char2buf(buf, 13, &len, '"'); } print_text(buf, len, 1); prusr_index++; } return ERR_INTERRUPTIBLE; } int docmd_list(arg_struct *arg) { if (arg->type != ARGTYPE_NUM) return ERR_INVALID_TYPE; if (arg->val.num == 0) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; return print_program(current_prgm, pc, arg->val.num, 0); } int docmd_adv(arg_struct *arg) { if (flags.f.printer_exists && (flags.f.printer_enable || !program_running())) { shell_annunciators(-1, -1, 1, -1, -1, -1); print_text(NULL, 0, 1); shell_annunciators(-1, -1, 0, -1, -1, -1); } return ERR_NONE; } int docmd_prlcd(arg_struct *arg) { if (!flags.f.printer_enable && program_running()) return ERR_NONE; if (!flags.f.printer_exists) return ERR_PRINTING_IS_DISABLED; else { shell_annunciators(-1, -1, 1, -1, -1, -1); print_display(); shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_NONE; } } int docmd_delay(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x < 0) x = -x; if (x >= 1.95) return ERR_INVALID_DATA; else /* We don't actually use the delay value... */ return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_pon(arg_struct *arg) { flags.f.printer_exists = 1; flags.f.printer_enable = 1; return ERR_NONE; } int docmd_poff(arg_struct *arg) { flags.f.printer_exists = 0; flags.f.printer_enable = 0; return ERR_NONE; } int docmd_man(arg_struct *arg) { flags.f.trace_print = 0; flags.f.normal_print = 0; return ERR_NONE; } int docmd_norm(arg_struct *arg) { flags.f.trace_print = 0; flags.f.normal_print = 1; return ERR_NONE; } int docmd_trace(arg_struct *arg) { flags.f.trace_print = 1; flags.f.normal_print = 0; return ERR_NONE; } int docmd_gto(arg_struct *arg) { int running = program_running(); if (!running) clear_all_rtns(); if (arg->type == ARGTYPE_NUM || arg->type == ARGTYPE_LCLBL) { if (!running || arg->target == -1) arg->target = find_local_label(arg); if (arg->target == -2) return ERR_LABEL_NOT_FOUND; else { pc = arg->target; prgm_highlight_row = 1; return ERR_NONE; } } if (arg->type == ARGTYPE_STR) { int new_prgm; int4 new_pc; if (find_global_label(arg, &new_prgm, &new_pc)) { current_prgm = new_prgm; pc = new_pc; prgm_highlight_row = 1; return ERR_NONE; } else return ERR_LABEL_NOT_FOUND; } if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; if (arg->type == ARGTYPE_NUM) { int4 target_pc = find_local_label(arg); if (target_pc == -2) return ERR_LABEL_NOT_FOUND; else { pc = target_pc; prgm_highlight_row = 1; return ERR_NONE; } } else { int newprgm; int4 newpc; if (find_global_label(arg, &newprgm, &newpc)) { current_prgm = newprgm; pc = newpc; prgm_highlight_row = 1; return ERR_NONE; } else return ERR_LABEL_NOT_FOUND; } } if (arg->type == ARGTYPE_LBLINDEX) { int labelindex = arg->val.num; current_prgm = labels[labelindex].prgm; pc = labels[labelindex].pc; prgm_highlight_row = 1; return ERR_NONE; } return ERR_INTERNAL_ERROR; } int docmd_end(arg_struct *arg) { return docmd_rtn(arg); } int docmd_number(arg_struct *arg) { vartype *new_x = new_real(arg->val_d); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; if (flags.f.stack_lift_disable) free_vartype(reg_x); else { free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = reg_x; } reg_x = new_x; return ERR_NONE; } int docmd_string(arg_struct *arg) { int append = arg->length > 0 && arg->val.text[0] == 127; if (append) { append_alpha_string(arg->val.text + 1, arg->length - 1, 0); if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); } else { reg_alpha_length = 0; append_alpha_string(arg->val.text, arg->length, 0); } return ERR_NONE; } int docmd_gtodot(arg_struct *arg) { if (arg->type == ARGTYPE_NUM) { pc = line2pc(arg->val.num); clear_all_rtns(); prgm_highlight_row = 1; return ERR_NONE; } else if (arg->type == ARGTYPE_STR) { int new_prgm; int4 new_pc; if (find_global_label(arg, &new_prgm, &new_pc)) { current_prgm = new_prgm; pc = new_pc; clear_all_rtns(); prgm_highlight_row = 1; return ERR_NONE; } else return ERR_LABEL_NOT_FOUND; } else if (arg->type == ARGTYPE_LBLINDEX) { int labelindex = arg->val.num; current_prgm = labels[labelindex].prgm; pc = labels[labelindex].pc; clear_all_rtns(); prgm_highlight_row = 1; return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_gtodotdot(arg_struct *arg) { goto_dot_dot(); return ERR_NONE; } int docmd_stop(arg_struct *arg) { return ERR_STOP; } int docmd_newmat(arg_struct *arg) { vartype *m; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) reg_x)->x; if (x <= -2147483648.0 || x >= 2147483648.0) return ERR_DIMENSION_ERROR; int4 xx = to_int4(x); if (xx == 0) return ERR_DIMENSION_ERROR; if (xx < 0) xx = -xx; phloat y = ((vartype_real *) reg_y)->x; if (y <= -2147483648.0 || y >= 2147483648.0) return ERR_DIMENSION_ERROR; int4 yy = to_int4(y); if (yy == 0) return ERR_DIMENSION_ERROR; if (yy < 0) yy = -yy; m = new_realmatrix(yy, xx); if (m == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(m); return ERR_NONE; } } int docmd_rup(arg_struct *arg) { vartype *temp = reg_x; reg_x = reg_t; reg_t = reg_z; reg_z = reg_y; reg_y = temp; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_real_t(arg_struct *arg) { return reg_x->type == TYPE_REAL ? ERR_YES : ERR_NO; } int docmd_cpx_t(arg_struct *arg) { return reg_x->type == TYPE_COMPLEX ? ERR_YES : ERR_NO; } int docmd_str_t(arg_struct *arg) { return reg_x->type == TYPE_STRING ? ERR_YES : ERR_NO; } int docmd_mat_t(arg_struct *arg) { return reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX ? ERR_YES : ERR_NO; } int docmd_dim_t(arg_struct *arg) { int4 rows, columns; if (reg_x->type == TYPE_REALMATRIX) { rows = ((vartype_realmatrix *) reg_x)->rows; columns = ((vartype_realmatrix *) reg_x)->columns; } else if (reg_x->type == TYPE_COMPLEXMATRIX) { rows = ((vartype_complexmatrix *) reg_x)->rows; columns = ((vartype_complexmatrix *) reg_x)->columns; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; vartype *new_y = new_real(rows); vartype *new_x = new_real(columns); if (new_x == NULL || new_y == NULL) { free_vartype(new_x); free_vartype(new_y); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_lastx); reg_lastx = reg_x; free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = new_y; reg_x = new_x; return ERR_NONE; } static int assign_helper(int num, arg_struct *arg) { if (arg->type == ARGTYPE_COMMAND) { /* For backward compatibility only; we don't allow this type * of assignment command to be created anymore, but we do want * programs that already contain such commands to continue * working. */ const command_spec *cs = cmdlist(arg->val.cmd); assign_custom_key(num, cs->name, cs->name_length); } else assign_custom_key(num, arg->val.text, arg->length); flags.f.local_label = 0; return ERR_NONE; } int docmd_asgn01(arg_struct *arg) { return assign_helper(1, arg); } int docmd_asgn02(arg_struct *arg) { return assign_helper(2, arg); } int docmd_asgn03(arg_struct *arg) { return assign_helper(3, arg); } int docmd_asgn04(arg_struct *arg) { return assign_helper(4, arg); } int docmd_asgn05(arg_struct *arg) { return assign_helper(5, arg); } int docmd_asgn06(arg_struct *arg) { return assign_helper(6, arg); } int docmd_asgn07(arg_struct *arg) { return assign_helper(7, arg); } int docmd_asgn08(arg_struct *arg) { return assign_helper(8, arg); } int docmd_asgn09(arg_struct *arg) { return assign_helper(9, arg); } int docmd_asgn10(arg_struct *arg) { return assign_helper(10, arg); } int docmd_asgn11(arg_struct *arg) { return assign_helper(11, arg); } int docmd_asgn12(arg_struct *arg) { return assign_helper(12, arg); } int docmd_asgn13(arg_struct *arg) { return assign_helper(13, arg); } int docmd_asgn14(arg_struct *arg) { return assign_helper(14, arg); } int docmd_asgn15(arg_struct *arg) { return assign_helper(15, arg); } int docmd_asgn16(arg_struct *arg) { return assign_helper(16, arg); } int docmd_asgn17(arg_struct *arg) { return assign_helper(17, arg); } int docmd_asgn18(arg_struct *arg) { return assign_helper(18, arg); } int docmd_on(arg_struct *arg) { flags.f.continuous_on = 1; return ERR_NONE; } int docmd_off(arg_struct *arg) { #ifdef IPHONE if (!off_enabled()) { squeak(); return ERR_STOP; } #endif if (program_running() && no_keystrokes_yet) return ERR_SUSPICIOUS_OFF; set_running(false); shell_powerdown(); return ERR_NONE; } int docmd_key1g(arg_struct *arg) { assign_prgm_key(1, 1, arg); return ERR_NONE; } int docmd_key2g(arg_struct *arg) { assign_prgm_key(2, 1, arg); return ERR_NONE; } int docmd_key3g(arg_struct *arg) { assign_prgm_key(3, 1, arg); return ERR_NONE; } int docmd_key4g(arg_struct *arg) { assign_prgm_key(4, 1, arg); return ERR_NONE; } int docmd_key5g(arg_struct *arg) { assign_prgm_key(5, 1, arg); return ERR_NONE; } int docmd_key6g(arg_struct *arg) { assign_prgm_key(6, 1, arg); return ERR_NONE; } int docmd_key7g(arg_struct *arg) { assign_prgm_key(7, 1, arg); return ERR_NONE; } int docmd_key8g(arg_struct *arg) { assign_prgm_key(8, 1, arg); return ERR_NONE; } int docmd_key9g(arg_struct *arg) { assign_prgm_key(9, 1, arg); return ERR_NONE; } int docmd_key1x(arg_struct *arg) { assign_prgm_key(1, 0, arg); return ERR_NONE; } int docmd_key2x(arg_struct *arg) { assign_prgm_key(2, 0, arg); return ERR_NONE; } int docmd_key3x(arg_struct *arg) { assign_prgm_key(3, 0, arg); return ERR_NONE; } int docmd_key4x(arg_struct *arg) { assign_prgm_key(4, 0, arg); return ERR_NONE; } int docmd_key5x(arg_struct *arg) { assign_prgm_key(5, 0, arg); return ERR_NONE; } int docmd_key6x(arg_struct *arg) { assign_prgm_key(6, 0, arg); return ERR_NONE; } int docmd_key7x(arg_struct *arg) { assign_prgm_key(7, 0, arg); return ERR_NONE; } int docmd_key8x(arg_struct *arg) { assign_prgm_key(8, 0, arg); return ERR_NONE; } int docmd_key9x(arg_struct *arg) { assign_prgm_key(9, 0, arg); return ERR_NONE; } int docmd_vmsto(arg_struct *arg) { /* STO variant that is invoked from a VARMENU */ int err = docmd_sto(arg); if (err == ERR_NONE) err = view_helper(arg, 0); if (err == ERR_NONE) mode_varmenu = true; return err; } int docmd_vmsto2(arg_struct *arg) { /* Special-purpose STO variant that is invoked from the Solver's VARMENU. * It saves the previous value of the target variable; this feature is used * by docmd_vmsolve() to provide the second initial guess (the first is * taken from the target variable, that is, the variable named as SOLVE's * parameter). */ vartype *v; if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; v = recall_var(arg->val.text, arg->length); if (v == NULL || v->type != TYPE_REAL) remove_shadow(arg->val.text, arg->length); else put_shadow(arg->val.text, arg->length, ((vartype_real *) v)->x); return docmd_vmsto(arg); } int docmd_sigma_reg(arg_struct *arg) { if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_NUM) return ERR_INTERNAL_ERROR; mode_sigma_reg = (int4) arg->val.num; if (mode_sigma_reg < 0) mode_sigma_reg = -mode_sigma_reg; return ERR_NONE; } int docmd_sigma_reg_t(arg_struct *arg) { vartype *v = new_real(mode_sigma_reg); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_cld(arg_struct *arg) { if (flags.f.message && mode_goose >= 0) mode_goose = -1 - mode_goose; flags.f.message = 0; flags.f.two_line_message = 0; return ERR_NONE; } free42-nologo-1.4.77/common/core_commands2.h000644 000765 000024 00000011527 12110237247 021173 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS2_H #define CORE_COMMANDS2_H 1 #include "free42.h" #include "core_globals.h" int docmd_sf(arg_struct *arg); int docmd_cf(arg_struct *arg); int docmd_fs_t(arg_struct *arg); int docmd_fc_t(arg_struct *arg); int docmd_fsc_t(arg_struct *arg); int docmd_fcc_t(arg_struct *arg); int docmd_comb(arg_struct *arg); int docmd_perm(arg_struct *arg); int docmd_fact(arg_struct *arg); int docmd_gamma(arg_struct *arg); int docmd_ran(arg_struct *arg); int docmd_seed(arg_struct *arg); int docmd_lbl(arg_struct *arg); int docmd_rtn(arg_struct *arg); int docmd_input(arg_struct *arg); int view_helper(arg_struct *arg, int print); int docmd_view(arg_struct *arg); int docmd_aview(arg_struct *arg); int docmd_xeq(arg_struct *arg); int docmd_prompt(arg_struct *arg); int docmd_pse(arg_struct *arg); int docmd_isg(arg_struct *arg); int docmd_dse(arg_struct *arg); int docmd_aip(arg_struct *arg); int docmd_xtoa(arg_struct *arg); int docmd_agraph(arg_struct *arg); int docmd_pixel(arg_struct *arg); int docmd_beep(arg_struct *arg); int docmd_tone(arg_struct *arg); int docmd_mvar(arg_struct *arg); int docmd_varmenu(arg_struct *arg); int docmd_getkey(arg_struct *arg); int docmd_menu(arg_struct *arg); int docmd_x_eq_0(arg_struct *arg); int docmd_x_ne_0(arg_struct *arg); int docmd_x_lt_0(arg_struct *arg); int docmd_x_gt_0(arg_struct *arg); int docmd_x_le_0(arg_struct *arg); int docmd_x_ge_0(arg_struct *arg); int docmd_x_eq_y(arg_struct *arg); int docmd_x_ne_y(arg_struct *arg); int docmd_x_lt_y(arg_struct *arg); int docmd_x_gt_y(arg_struct *arg); int docmd_x_le_y(arg_struct *arg); int docmd_x_ge_y(arg_struct *arg); int docmd_prsigma(arg_struct *arg); int docmd_prp(arg_struct *arg); int docmd_prv(arg_struct *arg); int docmd_prstk(arg_struct *arg); int docmd_pra(arg_struct *arg); int docmd_prx(arg_struct *arg); int docmd_prusr(arg_struct *arg); int docmd_list(arg_struct *arg); int docmd_adv(arg_struct *arg); int docmd_prlcd(arg_struct *arg); int docmd_delay(arg_struct *arg); int docmd_pon(arg_struct *arg); int docmd_poff(arg_struct *arg); int docmd_man(arg_struct *arg); int docmd_norm(arg_struct *arg); int docmd_trace(arg_struct *arg); int docmd_gto(arg_struct *arg); int docmd_end(arg_struct *arg); int docmd_number(arg_struct *arg); int docmd_string(arg_struct *arg); int docmd_gtodot(arg_struct *arg); int docmd_gtodotdot(arg_struct *arg); int docmd_stop(arg_struct *arg); int docmd_newmat(arg_struct *arg); int docmd_rup(arg_struct *arg); int docmd_real_t(arg_struct *arg); int docmd_cpx_t(arg_struct *arg); int docmd_str_t(arg_struct *arg); int docmd_mat_t(arg_struct *arg); int docmd_dim_t(arg_struct *arg); int docmd_asgn01(arg_struct *arg); int docmd_asgn02(arg_struct *arg); int docmd_asgn03(arg_struct *arg); int docmd_asgn04(arg_struct *arg); int docmd_asgn05(arg_struct *arg); int docmd_asgn06(arg_struct *arg); int docmd_asgn07(arg_struct *arg); int docmd_asgn08(arg_struct *arg); int docmd_asgn09(arg_struct *arg); int docmd_asgn10(arg_struct *arg); int docmd_asgn11(arg_struct *arg); int docmd_asgn12(arg_struct *arg); int docmd_asgn13(arg_struct *arg); int docmd_asgn14(arg_struct *arg); int docmd_asgn15(arg_struct *arg); int docmd_asgn16(arg_struct *arg); int docmd_asgn17(arg_struct *arg); int docmd_asgn18(arg_struct *arg); int docmd_on(arg_struct *arg); int docmd_off(arg_struct *arg); int docmd_key1g(arg_struct *arg); int docmd_key2g(arg_struct *arg); int docmd_key3g(arg_struct *arg); int docmd_key4g(arg_struct *arg); int docmd_key5g(arg_struct *arg); int docmd_key6g(arg_struct *arg); int docmd_key7g(arg_struct *arg); int docmd_key8g(arg_struct *arg); int docmd_key9g(arg_struct *arg); int docmd_key1x(arg_struct *arg); int docmd_key2x(arg_struct *arg); int docmd_key3x(arg_struct *arg); int docmd_key4x(arg_struct *arg); int docmd_key5x(arg_struct *arg); int docmd_key6x(arg_struct *arg); int docmd_key7x(arg_struct *arg); int docmd_key8x(arg_struct *arg); int docmd_key9x(arg_struct *arg); int docmd_vmsto(arg_struct *arg); int docmd_vmsto2(arg_struct *arg); int docmd_sigma_reg(arg_struct *arg); int docmd_sigma_reg_t(arg_struct *arg); int docmd_cld(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_commands3.cc000644 000765 000024 00000116227 12110237247 021335 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands1.h" #include "core_commands2.h" #include "core_commands3.h" #include "core_commands4.h" #include "core_display.h" #include "core_helpers.h" #include "core_linalg1.h" #include "core_main.h" #include "core_math2.h" #include "core_sto_rcl.h" #include "core_variables.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 3 */ /********************************************************/ static int mappable_acosh_r(phloat x, phloat *y) { if (x >= 1) { *y = acosh(x); return ERR_NONE; } else return ERR_INVALID_DATA; } static int mappable_acosh_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { return math_acosh(xre, xim, yre, yim); } int docmd_acosh(arg_struct *arg) { vartype *v; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x < 1) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { phloat re, im; int err = math_acosh(x, 0, &re, &im); if (err != ERR_NONE) return err; v = new_complex(re, im); } } else v = new_real(acosh(x)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } else { int err = map_unary(reg_x, &v, mappable_acosh_r, mappable_acosh_c); if (err != ERR_NONE) return err; } unary_result(v); return ERR_NONE; } int docmd_aleng(arg_struct *arg) { vartype *v = new_real(reg_alpha_length); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_aoff(arg_struct *arg) { flags.f.alpha_mode = 0; set_menu(MENULEVEL_ALPHA, MENU_NONE); return ERR_NONE; } int docmd_aon(arg_struct *arg) { flags.f.alpha_mode = 1; mode_alpha_entry = false; set_menu(MENULEVEL_ALPHA, MENU_ALPHA1); return ERR_NONE; } int docmd_arot(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat x; char buf[44]; int i, j; if (reg_alpha_length == 0) goto done; x = ((vartype_real *) reg_x)->x; if (x < 0) x = -floor(-x); else x = floor(x); j = to_int(fmod(x, reg_alpha_length)); if (j == 0) goto done; if (j < 0) j += reg_alpha_length; for (i = 0; i < reg_alpha_length; i++) buf[i] = reg_alpha[i]; for (i = 0; i < reg_alpha_length; i++) reg_alpha[i] = buf[(i + j) % reg_alpha_length]; done: if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_ashf(arg_struct *arg) { int i; reg_alpha_length -= 6; if (reg_alpha_length < 0) reg_alpha_length = 0; for (i = 0; i < reg_alpha_length; i++) reg_alpha[i] = reg_alpha[i + 6]; if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); return ERR_NONE; } static int mappable_asinh_r(phloat x, phloat *y) { *y = asinh(x); return ERR_NONE; } static int mappable_asinh_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { return math_asinh(xre, xim, yre, yim); } int docmd_asinh(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else { vartype *v; int err = map_unary(reg_x, &v, mappable_asinh_r, mappable_asinh_c); if (err == ERR_NONE) unary_result(v); return err; } } static int mappable_atanh_r(phloat x, phloat *y) { if (x == 1 || x == -1) return ERR_INVALID_DATA; *y = atanh(x); /* Theoretically, you could go out of range, but in practice, * you can't get close enough to the critical values to cause * trouble. */ return ERR_NONE; } int docmd_atanh(arg_struct *arg) { vartype *v; int err; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x == 1 || x == -1) return ERR_INVALID_DATA; if (x < -1 || x > 1) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { phloat tre, tim; err = math_atanh(x, 0, &tre, &tim); if (err != ERR_NONE) return err; v = new_complex(tre, tim); } } else v = new_real(atanh(x)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else { err = map_unary(reg_x, &v, mappable_atanh_r, math_atanh); if (err == ERR_NONE) unary_result(v); return err; } } int docmd_atox(arg_struct *arg) { vartype *v = new_real(reg_alpha_length == 0 ? 0 : (unsigned char) reg_alpha[0]); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; if (reg_alpha_length > 0) { int i; reg_alpha_length--; for (i = 0; i < reg_alpha_length; i++) reg_alpha[i] = reg_alpha[i + 1]; } if (flags.f.trace_print && flags.f.printer_exists) docmd_pra(NULL); recall_result(v); return ERR_NONE; } static int mappable_cosh_r(phloat x, phloat *y) { int inf; *y = cosh(x); if ((inf = p_isinf(*y)) != 0) { if (flags.f.range_error_ignore) *y = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } static int mappable_cosh_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat sinhxre, coshxre; phloat sinxim, cosxim; int inf; sinhxre = sinh(xre); coshxre = cosh(xre); sincos(xim, &sinxim, &cosxim); *yre = coshxre * cosxim; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = sinhxre * sinxim; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_cosh(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_cosh_r, mappable_cosh_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } /* NOTE: it is possible to generalize the cross product to more than * 3 dimensions... Something for Free42++ perhaps? */ int docmd_cross(arg_struct *arg) { if (reg_x->type == TYPE_STRING || reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_COMPLEX && reg_y->type == TYPE_COMPLEX) { vartype_complex *left = (vartype_complex *) reg_y; vartype_complex *right = (vartype_complex *) reg_x; vartype *v; phloat d = left->re * right->im - left->im * right->re; int inf; if ((inf = p_isinf(d)) != 0) { if (flags.f.range_error_ignore) d = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_real(d); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_REALMATRIX && reg_y->type == TYPE_REALMATRIX) { vartype_realmatrix *left = (vartype_realmatrix *) reg_y; vartype_realmatrix *right = (vartype_realmatrix *) reg_x; int4 ls = left->rows * left->columns; int4 rs = right->rows * right->columns; int4 i; int inf; phloat xl, yl = 0, zl = 0, xr, yr = 0, zr = 0; phloat xres, yres, zres; vartype_realmatrix *res; if (ls > 3 || rs > 3) return ERR_DIMENSION_ERROR; for (i = 0; i < ls; i++) if (left->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < rs; i++) if (right->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; switch (ls) { case 3: zl = left->array->data[2]; case 2: yl = left->array->data[1]; case 1: xl = left->array->data[0]; } switch (rs) { case 3: zr = right->array->data[2]; case 2: yr = right->array->data[1]; case 1: xr = right->array->data[0]; } xres = yl * zr - zl * yr; if ((inf = p_isinf(xres)) != 0) { if (flags.f.range_error_ignore) xres = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } yres = zl * xr - xl * zr; if ((inf = p_isinf(yres)) != 0) { if (flags.f.range_error_ignore) yres = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } zres = xl * yr - yl * xr; if ((inf = p_isinf(zres)) != 0) { if (flags.f.range_error_ignore) zres = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } res = (vartype_realmatrix *) new_realmatrix(1, 3); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; res->array->data[0] = xres; res->array->data[1] = yres; res->array->data[2] = zres; binary_result((vartype *) res); return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_custom(arg_struct *arg) { if (mode_plainmenu == MENU_CUSTOM1 || mode_plainmenu == MENU_CUSTOM2 || mode_plainmenu == MENU_CUSTOM3) set_menu(MENULEVEL_PLAIN, MENU_NONE); else set_menu(MENULEVEL_PLAIN, MENU_CUSTOM1); return ERR_NONE; } int docmd_delr(arg_struct *arg) { vartype *m, *newx; vartype_realmatrix *rm; vartype_complexmatrix *cm; int4 rows, columns, i, j, n, newi; int err, refcount; int interactive; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; if (m->type == TYPE_REALMATRIX) { rm = (vartype_realmatrix *) m; rows = rm->rows; columns = rm->columns; refcount = rm->array->refcount; } else { cm = (vartype_complexmatrix *) m; rows = cm->rows; columns = cm->columns; refcount = cm->array->refcount; } if (rows == 1) return ERR_DIMENSION_ERROR; if (matedit_i >= rows) matedit_i = rows - 1; if (matedit_j >= columns) matedit_j = columns - 1; n = (matedit_i + 1) * columns + matedit_j; if (matedit_i == rows - 1) { /* Deleting the bottom row; we need to move up one row, and * the next IJ value we show in X is going to be from *two* rows * higher: if you delete row 4 and it's not the bottom row, then * we'll show the value that's now in row 5; if it is the bottom * row, we move up one, and show the value that's now in row 3. */ newi = matedit_i - 1; n -= 2 * columns; } else newi = matedit_i; interactive = matedit_mode == 2 || matedit_mode == 3; if (interactive) { if (m->type == TYPE_REALMATRIX) { if (rm->array->is_string[n]) newx = new_string(phloat_text(rm->array->data[n]), phloat_length(rm->array->data[n])); else newx = new_real(rm->array->data[n]); } else newx = new_complex(cm->array->data[2 * n], cm->array->data[2 * n + 1]); if (newx == NULL) return ERR_INSUFFICIENT_MEMORY; } if (refcount == 1) { /* We have this array to ourselves so we can modify it in place * We shuffle the array elements around so that if the reallocation * succeeds, everything is in the right place, but if it fails, we * still have all the data so we can roll everything back. And best * of all, no temporary memory allocations needed! */ if (m->type == TYPE_REALMATRIX) { for (j = 0; j < columns; j++) { phloat tempd = rm->array->data[matedit_i * columns + j]; char tempc = rm->array->is_string[matedit_i * columns + j]; for (i = matedit_i; i < rows - 1; i++) { rm->array->data[i * columns + j] = rm->array->data[(i + 1) * columns + j]; rm->array->is_string[i * columns + j] = rm->array->is_string[(i + 1) * columns + j]; } rm->array->data[(rows - 1) * columns + j] = tempd; rm->array->is_string[(rows - 1) * columns + j] = tempc; } err = dimension_array_ref(m, rows - 1, columns); if (err != ERR_NONE) { /* Dang! Now we have to rotate everything back to where * it was before. */ for (j = 0; j < columns; j++) { phloat tempd = rm->array->data[(rows - 1) * columns + j]; char tempc = rm->array->is_string[(rows - 1) * columns + j]; for (i = rows - 1; i > matedit_i; i--) { rm->array->data[i * columns + j] = rm->array->data[(i - 1) * columns + j]; rm->array->is_string[i * columns + j] = rm->array->is_string[(i - 1) * columns + j]; } rm->array->data[matedit_i * columns + j] = tempd; rm->array->is_string[matedit_i * columns + j] = tempc; } if (interactive) free_vartype(newx); return err; } } else { for (j = 0; j < 2 * columns; j++) { phloat tempd = cm->array->data[matedit_i * 2 * columns + j]; for (i = matedit_i; i < rows - 1; i++) cm->array->data[i * 2 * columns + j] = cm->array->data[(i + 1) * 2 * columns + j]; cm->array->data[(rows - 1) * 2 * columns + j] = tempd; } err = dimension_array_ref(m, rows - 1, columns); if (err != ERR_NONE) { /* Dang! Now we have to rotate everything back to where * it was before. */ for (j = 0; j < 2 * columns; j++) { phloat tempd = cm->array->data[(rows - 1) * 2 * columns + j]; for (i = rows - 1; i > matedit_i; i--) cm->array->data[i * 2 * columns + j] = cm->array->data[(i - 1) * 2 * columns + j]; cm->array->data[matedit_i * 2 * columns + j] = tempd; } if (interactive) free_vartype(newx); return err; } } } else { /* We're sharing this array. I don't use disentangle() because it * does not deal with resizing. */ int4 newsize = (rows - 1) * columns; if (m->type == TYPE_REALMATRIX) { realmatrix_data *array = (realmatrix_data *) malloc(sizeof(realmatrix_data)); if (array == NULL) { if (interactive) free_vartype(newx); return ERR_INSUFFICIENT_MEMORY; } array->data = (phloat *) malloc(newsize * sizeof(phloat)); if (array->data == NULL) { if (interactive) free_vartype(newx); free(array); return ERR_INSUFFICIENT_MEMORY; } array->is_string = (char *) malloc(newsize); if (array->is_string == NULL) { if (interactive) free_vartype(newx); free(array->data); free(array); return ERR_INSUFFICIENT_MEMORY; } for (i = 0; i < matedit_i * columns; i++) { array->is_string[i] = rm->array->is_string[i]; array->data[i] = rm->array->data[i]; } for (i = matedit_i * columns; i < newsize; i++) { array->is_string[i] = rm->array->is_string[i + columns]; array->data[i] = rm->array->data[i + columns]; } array->refcount = 1; rm->array->refcount--; rm->array = array; rm->rows--; } else { complexmatrix_data *array = (complexmatrix_data *) malloc(sizeof(complexmatrix_data)); if (array == NULL) { if (interactive) free_vartype(newx); return ERR_INSUFFICIENT_MEMORY; } array->data = (phloat *) malloc(2 * newsize * sizeof(phloat)); if (array->data == NULL) { if (interactive) free_vartype(newx); free(array); return ERR_INSUFFICIENT_MEMORY; } for (i = 0; i < 2 * matedit_i * columns; i++) array->data[i] = cm->array->data[i]; for (i = 2 * matedit_i * columns; i < 2 * newsize; i++) array->data[i] = cm->array->data[i + 2 * columns]; array->refcount = 1; cm->array->refcount--; cm->array = array; cm->rows--; } } if (interactive) { free_vartype(reg_x); reg_x = newx; } matedit_i = newi; return ERR_NONE; } static void det_completion(int error, vartype *det) { if (error == ERR_NONE) unary_result(det); } int docmd_det(arg_struct *arg) { if (reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX) return linalg_det(reg_x, det_completion); else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_dim(arg_struct *arg) { phloat x, y; int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; x = ((vartype_real *) reg_x)->x; if (x == 0) return ERR_DIMENSION_ERROR; if (x < 0) x = -x; if (x >= 2147483648.0) return ERR_INSUFFICIENT_MEMORY; y = ((vartype_real *) reg_y)->x; if (y == 0) return ERR_DIMENSION_ERROR; if (y < 0) y = -y; if (y >= 2147483648.0) return ERR_INSUFFICIENT_MEMORY; return dimension_array(arg->val.text, arg->length, to_int(y), to_int(x)); } int docmd_dot(arg_struct *arg) { /* TODO: look for range errors in intermediate results. * Right now, 1e300+1e300i DOT 1e300-1e300i returns NaN * on the Palm, because two infinities of opposite signs * are added. (Why no NaN on the PC? Weird stuff: doing * "d = xre * yre + xim * yim" yields a different result * than "d = xre * yre ; d += xim * yim". Go figure.) */ vartype *v; if (reg_x->type == TYPE_STRING || reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REALMATRIX && reg_y->type == TYPE_REALMATRIX) { vartype_realmatrix *rm1 = (vartype_realmatrix *) reg_x; vartype_realmatrix *rm2 = (vartype_realmatrix *) reg_y; int4 size = rm1->rows * rm1->columns; int4 i; phloat dot = 0; int inf; if (size != rm2->rows * rm2->columns) return ERR_DIMENSION_ERROR; for (i = 0; i < size; i++) if (rm1->array->is_string[i] || rm2->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < size; i++) dot += rm1->array->data[i] * rm2->array->data[i]; if ((inf = p_isinf(dot)) != 0) { if (flags.f.range_error_ignore) dot = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_real(dot); } else if ((reg_x->type == TYPE_REALMATRIX && reg_y->type == TYPE_COMPLEXMATRIX) || (reg_x->type == TYPE_COMPLEXMATRIX && reg_y->type == TYPE_REALMATRIX)) { vartype_realmatrix *rm; vartype_complexmatrix *cm; int4 size, i; phloat dot_re = 0, dot_im = 0; int inf; if (reg_x->type == TYPE_REALMATRIX) { rm = (vartype_realmatrix *) reg_x; cm = (vartype_complexmatrix *) reg_y; } else { rm = (vartype_realmatrix *) reg_y; cm = (vartype_complexmatrix *) reg_x; } size = rm->rows * rm->columns; if (size != cm->rows * cm->columns) return ERR_DIMENSION_ERROR; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < size; i++) { dot_re += rm->array->data[i] * cm->array->data[2 * i]; dot_im += rm->array->data[i] * cm->array->data[2 * i + 1]; } if ((inf = p_isinf(dot_re)) != 0) { if (flags.f.range_error_ignore) dot_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } if ((inf = p_isinf(dot_im)) != 0) { if (flags.f.range_error_ignore) dot_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_complex(dot_re, dot_im); } else if (reg_x->type == TYPE_COMPLEXMATRIX && reg_y->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm1 = (vartype_complexmatrix *) reg_x; vartype_complexmatrix *cm2 = (vartype_complexmatrix *) reg_y; int4 size, i; phloat dot_re = 0, dot_im = 0; int inf; size = cm1->rows * cm1->columns; if (size != cm2->rows * cm2->columns) return ERR_DIMENSION_ERROR; size *= 2; for (i = 0; i < size; i += 2) { phloat re1 = cm1->array->data[i]; phloat im1 = cm1->array->data[i + 1]; phloat re2 = cm2->array->data[i]; phloat im2 = cm2->array->data[i + 1]; dot_re += re1 * re2 - im1 * im2; dot_im += re1 * im2 + re2 * im1; } if ((inf = p_isinf(dot_re)) != 0) { if (flags.f.range_error_ignore) dot_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } if ((inf = p_isinf(dot_im)) != 0) { if (flags.f.range_error_ignore) dot_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_complex(dot_re, dot_im); } else if (reg_x->type == TYPE_COMPLEX && reg_y->type == TYPE_COMPLEX) { vartype_complex *x = (vartype_complex *) reg_x; vartype_complex *y = (vartype_complex *) reg_y; phloat d = x->re * y->re + x->im * y->im; int inf; if ((inf = p_isinf(d)) != 0) { if (flags.f.range_error_ignore) d = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_real(d); } else return ERR_INVALID_TYPE; if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int matedit_get_dim(int4 *rows, int4 *columns) { vartype *m; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; *rows = rm->rows; *columns = rm->columns; return ERR_NONE; } else if (m->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; *rows = cm->rows; *columns = cm->columns; return ERR_NONE; } else return ERR_INVALID_TYPE; } /* NOTE: this is a callback for set_menu(); it is declared in * core_display.h but defined here. */ int appmenu_exitcallback_1(int menuid) { if (menuid == MENU_MATRIX_EDIT1 || menuid == MENU_MATRIX_EDIT2) { /* Just switching menus within the editor */ mode_appmenu = menuid; set_appmenu_exitcallback(1); return ERR_NONE; } else { /* The user is trying to leave the editor; we only allow that * if storing X is successful. */ int err = docmd_stoel(NULL); if (err == ERR_INSUFFICIENT_MEMORY) /* There's no graceful way to handle this, at least not without * some code restructuring (TODO); this is the one error that * should *not* prevent the user from getting out of the matrix * editor! Unfortunately, setting the error to ERR_NONE means we * aren't reporting the fact that changing the matrix element * was unsuccessful. */ err = ERR_NONE; if (err == ERR_NONEXISTENT) /* This can happen when the user overwrites the indexed * matrix with a scalar. The fact that that is even possible * is a bug; attempting to do that, or to CLV it, should * cause a Restricted Operation message. * This code is a workaround to allow users to escape from * the Matrix Editor once its state has become hosed in the * above manner. We could remove this hack eventually. (TODO) */ err = ERR_NONE; if (err != ERR_NONE) { /* Reinstate the callback so we'll get called again when * the user tries to leave again. */ set_appmenu_exitcallback(1); return err; } if (matedit_mode == 2) { free_vartype(reg_x); reg_x = matedit_x; matedit_x = NULL; } matedit_mode = 0; flags.f.grow = 0; flags.f.stack_lift_disable = 0; /* Note: no need to check the value returned by set_menu() here: * since we're in a set_menu() callback, we know that there is no * callback registered at the moment (set_menu() unregisters * callbacks just before invoking them), so these set_menu() calls * won't fail. */ if (menuid == MENU_NONE) set_menu(MENULEVEL_APP, matedit_prev_appmenu); else set_menu(MENULEVEL_APP, menuid); return ERR_NONE; } } static int finish_edit() { if (matedit_mode == 2 || matedit_mode == 3) /* Try to finish current interactive editing session using * set_menu(). The callback (appmenu_exitcallback) will * return its error status to set_menu(), which passes it * on to us. */ return set_menu_return_err(MENULEVEL_APP, MENU_NONE); else return ERR_NONE; } int docmd_edit(arg_struct *arg) { int err = finish_edit(); if (err != ERR_NONE) return err; if (reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX) { vartype *v; if (reg_x->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; if (rm->array->is_string[0]) v = new_string(phloat_text(rm->array->data[0]), phloat_length(rm->array->data[0])); else v = new_real(rm->array->data[0]); } else { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; v = new_complex(cm->array->data[0], cm->array->data[1]); } if (v == NULL) return ERR_INSUFFICIENT_MEMORY; matedit_mode = 2; flags.f.grow = 0; matedit_x = reg_x; reg_x = v; matedit_i = 0; matedit_j = 0; if (mode_appmenu >= MENU_MATRIX1 && mode_appmenu <= MENU_MATRIX_SIMQ) matedit_prev_appmenu = mode_appmenu; else matedit_prev_appmenu = MENU_NONE; set_menu(MENULEVEL_APP, MENU_MATRIX_EDIT1); set_appmenu_exitcallback(1); return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_editn(arg_struct *arg) { vartype *m; int err; err = finish_edit(); if (err != ERR_NONE) return err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; m = recall_var(arg->val.text, arg->length); if (m == NULL) return ERR_NONEXISTENT; else if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; else { vartype *v; int i; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; if (rm->array->is_string[0]) v = new_string(phloat_text(rm->array->data[0]), phloat_length(rm->array->data[0])); else v = new_real(rm->array->data[0]); } else { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; v = new_complex(cm->array->data[0], cm->array->data[1]); } if (v == NULL) return ERR_INSUFFICIENT_MEMORY; /* TODO: implement a mechanism that locks a matrix while it is * under edit. While locked, operations such as CLV, DIM, or * assignment should fail with ERR_RESTRICTED_OPERATION. */ matedit_mode = 3; flags.f.grow = 0; matedit_length = arg->length; for (i = 0; i < matedit_length; i++) matedit_name[i] = arg->val.text[i]; free_vartype(reg_x); reg_x = v; matedit_i = 0; matedit_j = 0; if (mode_appmenu >= MENU_MATRIX1 && mode_appmenu <= MENU_MATRIX_SIMQ) matedit_prev_appmenu = mode_appmenu; else matedit_prev_appmenu = MENU_NONE; set_menu(MENULEVEL_APP, MENU_MATRIX_EDIT1); set_appmenu_exitcallback(1); return ERR_NONE; } } int docmd_exitall(arg_struct *arg) { return set_menu_return_err(MENULEVEL_APP, MENU_NONE); } static int mappable_e_pow_x_1(phloat x, phloat *y) { *y = expm1(x); if (p_isinf(*y) != 0) { if (flags.f.range_error_ignore) *y = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_e_pow_x_1(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_e_pow_x_1, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int fnrm(vartype *m, phloat *norm) { if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; int4 size = rm->rows * rm->columns; int4 i; phloat nrm = 0; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < size; i++) { /* TODO -- overflows in intermediaries */ phloat x = rm->array->data[i]; nrm += x * x; } if (p_isinf(nrm)) { if (flags.f.range_error_ignore) nrm = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } else nrm = sqrt(nrm); *norm = nrm; return ERR_NONE; } else if (m->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; int4 size = 2 * cm->rows * cm->columns; int4 i; phloat nrm = 0; for (i = 0; i < size; i++) { /* TODO -- overflows in intermediaries */ phloat x = cm->array->data[i]; nrm += x * x; } if (p_isinf(nrm)) { if (flags.f.range_error_ignore) nrm = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } else nrm = sqrt(nrm); *norm = nrm; return ERR_NONE; } else if (m->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_fnrm(arg_struct *arg) { phloat norm; vartype *v; int err = fnrm(reg_x, &norm); if (err != ERR_NONE) return err; v = new_real(norm); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } int docmd_getm(arg_struct *arg) { vartype *m; phloat xx, yy; int4 x, y; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) /* Should not happen, but could, as long as I don't implement * matrix locking. */ return ERR_INVALID_TYPE; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; xx = ((vartype_real *) reg_x)->x; if (xx <= -2147483648.0 || xx >= 2147483648.0) return ERR_DIMENSION_ERROR; x = to_int4(xx); if (x == 0) return ERR_DIMENSION_ERROR; if (x < 0) x = -x; yy = ((vartype_real *) reg_y)->x; if (yy <= -2147483648.0 || yy >= 2147483648.0) return ERR_DIMENSION_ERROR; y = to_int4(yy); if (y == 0) return ERR_DIMENSION_ERROR; if (y < 0) y = -y; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *src, *dst; int4 i, j; src = (vartype_realmatrix *) m; if (src->rows < matedit_i + y || src->columns < matedit_j + x) return ERR_DIMENSION_ERROR; dst = (vartype_realmatrix *) new_realmatrix(y, x); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < y; i++) for (j = 0; j < x; j++) { int4 n1 = (i + matedit_i) * src->columns + j + matedit_j; int4 n2 = i * dst->columns + j; dst->array->is_string[n2] = src->array->is_string[n1]; dst->array->data[n2] = src->array->data[n1]; } binary_result((vartype *) dst); return ERR_NONE; } else /* m->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *src, *dst; int4 i, j; src = (vartype_complexmatrix *) m; if (src->rows < matedit_i + y || src->columns < matedit_j + x) return ERR_DIMENSION_ERROR; dst = (vartype_complexmatrix *) new_complexmatrix(y, x); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < y; i++) for (j = 0; j < x; j++) { int4 n1 = (i + matedit_i) * src->columns + j + matedit_j; int4 n2 = i * dst->columns + j; dst->array->data[n2 * 2] = src->array->data[n1 * 2]; dst->array->data[n2 * 2 + 1] = src->array->data[n1 * 2 + 1]; } binary_result((vartype *) dst); return ERR_NONE; } } int docmd_grow(arg_struct *arg) { flags.f.grow = 1; return ERR_NONE; } static int hms_add_or_sub(bool add) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) reg_x)->x; phloat y = ((vartype_real *) reg_y)->x; phloat r; bool neg; if (x < 0) { x = -x; add = !add; } if (y < 0) { y = -y; add = !add; neg = 1; } else neg = 0; #ifdef BCD_MATH phloat xh, xm, xs, yh, ym, ys, t; xh = floor(x); t = (x - xh) * 100; xm = floor(t); xs = (t - xm) * 100; yh = floor(y); t = (y - yh) * 100; ym = floor(t); ys = (t - ym) * 100; if (add) { ys += xs; if (ys >= 60) { ys -= 60; ym++; } ym += xm; if (ym >= 60) { ym -= 60; yh++; } yh += xh; } else { ys -= xs; if (ys < 0) { ys += 60; ym--; } else if (ys >= 60) { ys -= 60; ym++; } ym -= xm; if (ym < 0) { ym += 60; yh--; } else if (ym >= 60) { ym -= 60; yh++; } yh -= xh; if (yh < 0) { if (ys != 0) { ys -= 60; ym++; } if (ym != 0) { ym -= 60; yh++; } } } r = yh + ym / 100 + ys / 10000; #else /* I'm doing the computation in integer math. The nastiness is necessary, * because this is one of those cases (along with ->HR, ISG, and DSE) * where binary representation errors can mess things up pretty badly... * For example, you enter 0.45, thinking 45 minutes; the closest binary * representation is 0.449999999..., which could easily be mistaken for 44 * minutes 99.99999... seconds -- 40 seconds off! */ double rx, ry, res; int8 ix, iy, ixhr, iyhr, ires, ireshr; rx = floor(x); ry = floor(y); res = add ? ry + rx : ry - rx; ix = (int8) (((x - rx) * 1000000000000.0) + 0.5); iy = (int8) (((y - ry) * 1000000000000.0) + 0.5); ixhr = ix % LL(10000000000); iyhr = iy % LL(10000000000); ix /= LL(10000000000); iy /= LL(10000000000); ixhr += (ix % 100) * LL(6000000000); iyhr += (iy % 100) * LL(6000000000); ixhr += (ix / 100) * LL(360000000000); iyhr += (iy / 100) * LL(360000000000); ireshr = add ? iyhr + ixhr : iyhr - ixhr; while (ireshr < 0 && res > 0) { ireshr += LL(360000000000); res -= 1; } while (ireshr > 0 && res < 0) { ireshr -= LL(360000000000); res += 1; } ires = ireshr % LL(6000000000); ireshr /= LL(6000000000); ires += (ireshr % 60) * LL(10000000000); ires += (ireshr / 60) * LL(1000000000000); res += ires / 1000000000000.0; r = res; #endif /* Round-off may have caused the minutes or seconds to reach 60; * detect this and fix... */ r = fix_hms(r); if (neg) r = -r; int inf; if ((inf = p_isinf(r)) != 0) { if (flags.f.range_error_ignore) r = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } vartype *v = new_real(r); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_hmsadd(arg_struct *arg) { return hms_add_or_sub(true); } int docmd_hmssub(arg_struct *arg) { return hms_add_or_sub(false); } int docmd_i_add(arg_struct *arg) { int4 rows, columns; int err = matedit_get_dim(&rows, &columns); if (err != ERR_NONE) return err; if (++matedit_i >= rows) { flags.f.matrix_edge_wrap = 1; matedit_i = 0; if (++matedit_j >= columns) { flags.f.matrix_end_wrap = 1; matedit_j = 0; } else flags.f.matrix_end_wrap = 0; } else { flags.f.matrix_edge_wrap = 0; flags.f.matrix_end_wrap = 0; } return ERR_NONE; } int docmd_i_sub(arg_struct *arg) { int4 rows, columns; int err = matedit_get_dim(&rows, &columns); if (err != ERR_NONE) return err; if (--matedit_i < 0) { flags.f.matrix_edge_wrap = 1; matedit_i = rows - 1; if (--matedit_j < 0) { flags.f.matrix_end_wrap = 1; matedit_j = columns - 1; } else flags.f.matrix_end_wrap = 0; } else { flags.f.matrix_edge_wrap = 0; flags.f.matrix_end_wrap = 0; } return ERR_NONE; } void matedit_goto(int4 row, int4 column) { int4 rows, columns; int err = matedit_get_dim(&rows, &columns); if (err == ERR_NONE) { if (row == 0 || row > rows || column == 0 || column > columns) err = ERR_DIMENSION_ERROR; else { int prev_stack_lift_disable; matedit_i = row - 1; matedit_j = column - 1; prev_stack_lift_disable = flags.f.stack_lift_disable; flags.f.stack_lift_disable = 1; err = docmd_rclel(NULL); flags.f.stack_lift_disable = prev_stack_lift_disable; } } if (err != ERR_NONE) { display_error(err, 0); flush_display(); } } int docmd_index(arg_struct *arg) { int err; vartype *m; int i; if (matedit_mode == 2 || matedit_mode == 3) /* TODO: on the real HP-42S, you get this error message the * moment you say INDEX, that is, before even having to * complete the command. Doing that in free42 would require * a check in start_incomplete_command() or do_interactive(). * I'm putting that off until I have a better idea of whether * this is an isolated special case (in which case I'll go * for the quick-and-dirty hack) or something more general * (in which case I'll redesign stuff if necessary). */ return ERR_RESTRICTED_OPERATION; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; m = recall_var(arg->val.text, arg->length); if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; /* TODO: keep a 'weak' lock on the matrix while it is indexed. * If it is deleted or redimensioned, I and J should be reset to 1. * Note that the current code uses a lazy, keep-it-safe approach * (it does not assume I and J are necessarily in bounds when I+, J-, * LEFT, RIGHT, etc., are called, but simply forces I and J within * bounds just before using them), but this does not always yield the * same result (try deleting the indexed matrix and then recreating it * immediately; on the real HP-42S, I and J are now both 1; on Free42, * I and J are unchanged). */ matedit_mode = 1; matedit_i = 0; matedit_j = 0; matedit_length = arg->length; for (i = 0; i < matedit_length; i++) matedit_name[i] = arg->val.text[i]; return ERR_NONE; } int docmd_uvec(arg_struct *arg) { phloat norm; int err; vartype *v; if (reg_x->type == TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; if (reg_x->type == TYPE_COMPLEX) { vartype_complex *z = (vartype_complex *) reg_x; if (z->re == 0 && z->im == 0) return ERR_INVALID_DATA; else return docmd_sign(arg); } err = fnrm(reg_x, &norm); if (err != ERR_NONE) return err; if (norm == 0) { return ERR_INVALID_DATA; } else { vartype_realmatrix *src = (vartype_realmatrix *) reg_x; vartype_realmatrix *dst; int4 rows = src->rows; int4 columns = src->columns; int4 size = rows * columns; int4 i; dst = (vartype_realmatrix *) new_realmatrix(rows, columns); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < size; i++) dst->array->data[i] = src->array->data[i] / norm; v = (vartype *) dst; } unary_result(v); return ERR_NONE; } free42-nologo-1.4.77/common/core_commands3.h000644 000765 000024 00000003723 12110237247 021173 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS3_H #define CORE_COMMANDS3_H 1 #include "free42.h" #include "core_globals.h" int matedit_get_dim(int4 *rows, int4 *columns); int docmd_acosh(arg_struct *arg); int docmd_aleng(arg_struct *arg); int docmd_aoff(arg_struct *arg); int docmd_aon(arg_struct *arg); int docmd_arot(arg_struct *arg); int docmd_ashf(arg_struct *arg); int docmd_asinh(arg_struct *arg); int docmd_atanh(arg_struct *arg); int docmd_atox(arg_struct *arg); int docmd_cosh(arg_struct *arg); int docmd_cross(arg_struct *arg); int docmd_custom(arg_struct *arg); int docmd_delr(arg_struct *arg); int docmd_det(arg_struct *arg); int docmd_dim(arg_struct *arg); int docmd_dot(arg_struct *arg); int docmd_edit(arg_struct *arg); int docmd_editn(arg_struct *arg); int docmd_exitall(arg_struct *arg); int docmd_e_pow_x_1(arg_struct *arg); int docmd_fnrm(arg_struct *arg); int docmd_getm(arg_struct *arg); int docmd_grow(arg_struct *arg); int docmd_hmsadd(arg_struct *arg); int docmd_hmssub(arg_struct *arg); int docmd_i_add(arg_struct *arg); int docmd_i_sub(arg_struct *arg); void matedit_goto(int4 row, int4 column); int docmd_index(arg_struct *arg); int docmd_uvec(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_commands4.cc000644 000765 000024 00000116107 12110237247 021333 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands2.h" #include "core_commands3.h" #include "core_commands4.h" #include "core_display.h" #include "core_helpers.h" #include "core_linalg1.h" #include "core_sto_rcl.h" #include "core_variables.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 4 */ /********************************************************/ int docmd_insr(arg_struct *arg) { vartype *m, *newx; vartype_realmatrix *rm; vartype_complexmatrix *cm; int4 rows, columns, i; int err, refcount; int interactive; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; interactive = matedit_mode == 2 || matedit_mode == 3; if (interactive) { err = docmd_stoel(NULL); if (err != ERR_NONE) return err; } if (m->type == TYPE_REALMATRIX) { rm = (vartype_realmatrix *) m; rows = rm->rows; columns = rm->columns; refcount = rm->array->refcount; if (interactive) { newx = new_real(0); if (newx == NULL) return ERR_INSUFFICIENT_MEMORY; } } else { cm = (vartype_complexmatrix *) m; rows = cm->rows; columns = cm->columns; refcount = cm->array->refcount; if (interactive) { newx = new_complex(0, 0); if (newx == NULL) return ERR_INSUFFICIENT_MEMORY; } } if (matedit_i >= rows) matedit_i = rows - 1; if (matedit_j >= columns) matedit_j = columns - 1; if (refcount == 1) { /* We have this array to ourselves so we can modify it in place */ err = dimension_array_ref(m, rows + 1, columns); if (err != ERR_NONE) { if (interactive) free_vartype(newx); return err; } rows++; if (m->type == TYPE_REALMATRIX) { for (i = rows * columns - 1; i >= (matedit_i + 1) * columns; i--) { rm->array->is_string[i] = rm->array->is_string[i - columns]; rm->array->data[i] = rm->array->data[i - columns]; } for (i = matedit_i * columns; i < (matedit_i + 1) * columns; i++) { rm->array->is_string[i] = 0; rm->array->data[i] = 0; } } else { for (i = 2 * rows * columns - 1; i >= 2 * (matedit_i + 1) * columns; i--) cm->array->data[i] = cm->array->data[i - 2 * columns]; for (i = 2 * matedit_i * columns; i < 2 * (matedit_i + 1) * columns; i++) cm->array->data[i] = 0; } } else { /* We're sharing this array. I don't use disentangle() because it * does not deal with resizing. */ int4 newsize = (rows + 1) * columns; if (m->type == TYPE_REALMATRIX) { realmatrix_data *array = (realmatrix_data *) malloc(sizeof(realmatrix_data)); if (array == NULL) { if (interactive) free_vartype(newx); return ERR_INSUFFICIENT_MEMORY; } array->data = (phloat *) malloc(newsize * sizeof(phloat)); if (array->data == NULL) { if (interactive) free_vartype(newx); free(array); return ERR_INSUFFICIENT_MEMORY; } array->is_string = (char *) malloc(newsize); if (array->is_string == NULL) { if (interactive) free_vartype(newx); free(array->data); free(array); return ERR_INSUFFICIENT_MEMORY; } for (i = 0; i < matedit_i * columns; i++) { array->is_string[i] = rm->array->is_string[i]; array->data[i] = rm->array->data[i]; } for (i = matedit_i * columns; i < (matedit_i + 1) * columns; i++) { array->is_string[i] = 0; array->data[i] = 0; } for (i = (matedit_i + 1) * columns; i < newsize; i++) { array->is_string[i] = rm->array->is_string[i - columns]; array->data[i] = rm->array->data[i - columns]; } array->refcount = 1; rm->array->refcount--; rm->array = array; rm->rows++; } else { complexmatrix_data *array = (complexmatrix_data *) malloc(sizeof(complexmatrix_data)); if (array == NULL) { if (interactive) free_vartype(newx); return ERR_INSUFFICIENT_MEMORY; } array->data = (phloat *) malloc(2 * newsize * sizeof(phloat)); if (array->data == NULL) { if (interactive) free_vartype(newx); free(array); return ERR_INSUFFICIENT_MEMORY; } for (i = 0; i < 2 * matedit_i * columns; i++) array->data[i] = cm->array->data[i]; for (i = 2 * matedit_i * columns; i < 2 * (matedit_i + 1) * columns; i++) array->data[i] = 0; for (i = 2 * (matedit_i + 1) * columns; i < 2 * newsize; i++) array->data[i] = cm->array->data[i - 2 * columns]; array->refcount = 1; cm->array->refcount--; cm->array = array; cm->rows++; } } if (interactive) { free_vartype(reg_x); reg_x = newx; } return ERR_NONE; } static void invrt_completion(int error, vartype *res) { if (error == ERR_NONE) unary_result(res); } int docmd_invrt(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_COMPLEX) return ERR_INVALID_TYPE; else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return linalg_inv(reg_x, invrt_completion); } int docmd_j_add(arg_struct *arg) { int4 rows, columns; int4 oldi = matedit_i; int4 oldj = matedit_j; int err = matedit_get_dim(&rows, &columns); if (err != ERR_NONE) return err; if (++matedit_j >= columns) { flags.f.matrix_edge_wrap = 1; matedit_j = 0; if (++matedit_i >= rows) { flags.f.matrix_end_wrap = 1; if (flags.f.grow) { if (matedit_mode == 2) err = dimension_array_ref(matedit_x, rows + 1, columns); else err = dimension_array(matedit_name, matedit_length, rows + 1, columns); if (err != ERR_NONE) { matedit_i = oldi; matedit_j = oldj; return err; } matedit_i = rows; } else matedit_i = 0; } else flags.f.matrix_end_wrap = 0; } else { flags.f.matrix_edge_wrap = 0; flags.f.matrix_end_wrap = 0; } return ERR_NONE; } int docmd_j_sub(arg_struct *arg) { int4 rows, columns; int err = matedit_get_dim(&rows, &columns); if (err != ERR_NONE) return err; if (--matedit_j < 0) { flags.f.matrix_edge_wrap = 1; matedit_j = columns - 1; if (--matedit_i < 0) { flags.f.matrix_end_wrap = 1; matedit_i = rows - 1; } else flags.f.matrix_end_wrap = 0; } else { flags.f.matrix_edge_wrap = 0; flags.f.matrix_end_wrap = 0; } return ERR_NONE; } static int mappable_ln_1_x(phloat x, phloat *y) { if (x <= -1) return ERR_INVALID_DATA; *y = log1p(x); return ERR_NONE; } int docmd_ln_1_x(arg_struct *arg) { if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { vartype *v; int err = map_unary(reg_x, &v, mappable_ln_1_x, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_old(arg_struct *arg) { return docmd_rclel(NULL); } int docmd_posa(arg_struct *arg) { int pos = -1; vartype *v; if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; char c; int i; if (x < 0) x = -x; if (x >= 256) return ERR_INVALID_DATA; c = to_char(x); for (i = 0; i < reg_alpha_length; i++) if (reg_alpha[i] == c) { pos = i; break; } } else if (reg_x->type == TYPE_STRING) { vartype_string *s = (vartype_string *) reg_x; if (s->length != 0) { int i, j; for (i = 0; i < reg_alpha_length - s->length; i++) { for (j = 0; j < s->length; j++) if (reg_alpha[i + j] != s->text[j]) goto notfound; pos = i; break; notfound:; } } } else return ERR_INVALID_TYPE; v = new_real(pos); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } int docmd_putm(arg_struct *arg) { vartype *m; int4 i, j; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) /* Shouldn't happen, but could, as long as I don't * implement matrix locking */ return ERR_INVALID_TYPE; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_COMPLEX) return ERR_INVALID_TYPE; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *src, *dst; if (reg_x->type == TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; src = (vartype_realmatrix *) reg_x; dst = (vartype_realmatrix *) m; if (src->rows + matedit_i > dst->rows || src->columns + matedit_j > dst->columns) return ERR_DIMENSION_ERROR; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < src->rows; i++) for (j = 0; j < src->columns; j++) { int4 n1 = i * src->columns + j; int4 n2 = (i + matedit_i) * dst->columns + j + matedit_j; dst->array->is_string[n2] = src->array->is_string[n1]; dst->array->data[n2] = src->array->data[n1]; } return ERR_NONE; } else if (reg_x->type == TYPE_REALMATRIX) { vartype_realmatrix *src = (vartype_realmatrix *) reg_x; vartype_complexmatrix *dst = (vartype_complexmatrix *) m; if (src->rows + matedit_i > dst->rows || src->columns + matedit_j > dst->columns) return ERR_DIMENSION_ERROR; for (i = 0; i < src->rows * src->columns; i++) if (src->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < src->rows; i++) for (j = 0; j < src->columns; j++) { int4 n1 = i * src->columns + j; int4 n2 = (i + matedit_i) * dst->columns + j + matedit_j; dst->array->data[n2 * 2] = src->array->data[n1]; dst->array->data[n2 * 2 + 1] = 0; } return ERR_NONE; } else { vartype_complexmatrix *src = (vartype_complexmatrix *) reg_x; vartype_complexmatrix *dst = (vartype_complexmatrix *) m; if (src->rows + matedit_i > dst->rows || src->columns + matedit_j > dst->columns) return ERR_DIMENSION_ERROR; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < src->rows; i++) for (j = 0; j < src->columns; j++) { int4 n1 = i * src->columns + j; int4 n2 = (i + matedit_i) * dst->columns + j + matedit_j; dst->array->data[n2 * 2] = src->array->data[n1 * 2]; dst->array->data[n2 * 2 + 1] = src->array->data[n1 * 2 + 1]; } return ERR_NONE; } } int docmd_rclel(arg_struct *arg) { vartype *m, *v; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; int4 n = matedit_i * rm->columns + matedit_j; if (rm->array->is_string[n]) v = new_string(phloat_text(rm->array->data[n]), phloat_length(rm->array->data[n])); else v = new_real(rm->array->data[n]); } else if (m->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; int4 n = matedit_i * cm->columns + matedit_j; v = new_complex(cm->array->data[2 * n], cm->array->data[2 * n + 1]); } else return ERR_INVALID_TYPE; if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_rclij(arg_struct *arg) { vartype *i, *j; if (matedit_mode == 0) return ERR_NONEXISTENT; i = new_real(matedit_i + 1); j = new_real(matedit_j + 1); if (i == NULL || j == NULL) { free_vartype(i); free_vartype(j); return ERR_INSUFFICIENT_MEMORY; } recall_two_results(j, i); return ERR_NONE; } int docmd_rnrm(arg_struct *arg) { if (reg_x->type == TYPE_REALMATRIX) { vartype *v; vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; int4 size = rm->rows * rm->columns; int4 i, j; phloat max = 0; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; for (i = 0; i < rm->rows; i++) { phloat nrm = 0; for (j = 0; j < rm->columns; j++) { phloat x = rm->array->data[i * rm->columns + j]; if (x >= 0) nrm += x; else nrm -= x; } if (p_isinf(nrm)) { if (flags.f.range_error_ignore) max = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; break; } if (nrm > max) max = nrm; } v = new_real(max); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_COMPLEXMATRIX) { vartype *v; vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; int4 i, j; phloat max = 0; for (i = 0; i < cm->rows; i++) { phloat nrm = 0; for (j = 0; j < cm->columns; j++) { phloat re = cm->array->data[2 * (i * cm->columns + j)]; phloat im = cm->array->data[2 * (i * cm->columns + j) + 1]; nrm += hypot(re, im); } if (p_isinf(nrm)) { if (flags.f.range_error_ignore) max = POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; break; } if (nrm > max) max = nrm; } v = new_real(max); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_rsum(arg_struct *arg) { if (reg_x->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; vartype_realmatrix *res; int4 size = rm->rows * rm->columns; int4 i, j; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; res = (vartype_realmatrix *) new_realmatrix(rm->rows, 1); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < rm->rows; i++) { phloat sum = 0; int inf; for (j = 0; j < rm->columns; j++) sum += rm->array->data[i * rm->columns + j]; if ((inf = p_isinf(sum)) != 0) { if (flags.f.range_error_ignore) sum = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else { free_vartype((vartype *) res); return ERR_OUT_OF_RANGE; } } res->array->data[i] = sum; } unary_result((vartype *) res); return ERR_NONE; } else if (reg_x->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; vartype_complexmatrix *res; int4 i, j; res = (vartype_complexmatrix *) new_complexmatrix(cm->rows, 1); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < cm->rows; i++) { phloat sum_re = 0, sum_im = 0; int inf; for (j = 0; j < cm->columns; j++) { sum_re += cm->array->data[2 * (i * cm->columns + j)]; sum_im += cm->array->data[2 * (i * cm->columns + j) + 1]; } if ((inf = p_isinf(sum_re)) != 0) { if (flags.f.range_error_ignore) sum_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else { free_vartype((vartype *) res); return ERR_OUT_OF_RANGE; } } if ((inf = p_isinf(sum_im)) != 0) { if (flags.f.range_error_ignore) sum_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else { free_vartype((vartype *) res); return ERR_OUT_OF_RANGE; } } res->array->data[2 * i] = sum_re; res->array->data[2 * i + 1] = sum_im; } unary_result((vartype *) res); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_swap_r(arg_struct *arg) { vartype *m; phloat xx, yy; int4 x, y, i; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) /* Should not happen, but could, as long as I don't implement * matrix locking. */ return ERR_INVALID_TYPE; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; xx = ((vartype_real *) reg_x)->x; if (xx <= -2147483648.0 || xx >= 2147483648.0) return ERR_DIMENSION_ERROR; x = to_int4(xx); if (x == 0) return ERR_DIMENSION_ERROR; if (x < 0) x = -x; x--; yy = ((vartype_real *) reg_y)->x; if (yy <= -2147483648.0 || yy >= 2147483648.0) return ERR_DIMENSION_ERROR; y = to_int4(yy); if (y == 0) return ERR_DIMENSION_ERROR; if (y < 0) y = -y; y--; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; if (x > rm->rows || y > rm->rows) return ERR_DIMENSION_ERROR; else if (x == y) return ERR_NONE; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < rm->columns; i++) { int4 n1 = x * rm->columns + i; int4 n2 = y * rm->columns + i; char tempc = rm->array->is_string[n1]; phloat tempds = rm->array->data[n1]; rm->array->is_string[n1] = rm->array->is_string[n2]; rm->array->data[n1] = rm->array->data[n2]; rm->array->is_string[n2] = tempc; rm->array->data[n2] = tempds; } return ERR_NONE; } else /* m->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; if (x > cm->rows || y > cm->rows) return ERR_DIMENSION_ERROR; else if (x == y) return ERR_NONE; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < 2 * cm->columns; i++) { int4 n1 = x * 2 * cm->columns + i; int4 n2 = y * 2 * cm->columns + i; phloat tempd = cm->array->data[n1]; cm->array->data[n1] = cm->array->data[n2]; cm->array->data[n2] = tempd; } return ERR_NONE; } } static int mappable_sinh_r(phloat x, phloat *y) { int inf; *y = sinh(x); if ((inf = p_isinf(*y)) != 0) { if (flags.f.range_error_ignore) *y = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } static int mappable_sinh_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat sinhxre, coshxre; phloat sinxim, cosxim; int inf; sinhxre = sinh(xre); coshxre = cosh(xre); sincos(xim, &sinxim, &cosxim); *yre = sinhxre * cosxim; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = coshxre * sinxim; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_sinh(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_sinh_r, mappable_sinh_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } int docmd_stoel(arg_struct *arg) { vartype *m; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX && m->type != TYPE_COMPLEXMATRIX) /* Should not happen, but could, as long as I don't implement * matrix locking. */ return ERR_INVALID_TYPE; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; int4 n = matedit_i * rm->columns + matedit_j; if (reg_x->type == TYPE_REAL) { rm->array->is_string[n] = 0; rm->array->data[n] = ((vartype_real *) reg_x)->x; return ERR_NONE; } else if (reg_x->type == TYPE_STRING) { vartype_string *s = (vartype_string *) reg_x; int i; rm->array->is_string[n] = 1; phloat_length(rm->array->data[n]) = s->length; for (i = 0; i < s->length; i++) phloat_text(rm->array->data[n])[i] = s->text[i]; return ERR_NONE; } else return ERR_INVALID_TYPE; } else /* m->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; int4 n = matedit_i * cm->columns + matedit_j; if (reg_x->type == TYPE_REAL) { cm->array->data[2 * n] = ((vartype_real *) reg_x)->x; cm->array->data[2 * n + 1] = 0; return ERR_NONE; } else if (reg_x->type == TYPE_COMPLEX) { vartype_complex *c = (vartype_complex *) reg_x; cm->array->data[2 * n] = c->re; cm->array->data[2 * n + 1] = c->im; return ERR_NONE; } else return ERR_INVALID_TYPE; } } int docmd_stoij(arg_struct *arg) { vartype *m; phloat x, y; int4 i, j; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; x = ((vartype_real *) reg_x)->x; if (x <= -2147483648.0 || x >= 2147483648.0) return ERR_DIMENSION_ERROR; j = to_int4(x); if (j < 0) j = -j; y = ((vartype_real *) reg_y)->x; if (y <= -2147483648.0 || y >= 2147483648.0) return ERR_DIMENSION_ERROR; i = to_int4(y); if (i < 0) i = -i; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) m; if (i == 0 || i > rm->rows || j == 0 || j > rm->columns) return ERR_DIMENSION_ERROR; } else if (m->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) m; if (i == 0 || i > cm->rows || j == 0 || j > cm->columns) return ERR_DIMENSION_ERROR; } else /* Should not happen, but could, as long as I don't implement * matrix locking. */ return ERR_INVALID_TYPE; matedit_i = i - 1; matedit_j = j - 1; return ERR_NONE; } static int mappable_tanh_r(phloat x, phloat *y) { *y = tanh(x); return ERR_NONE; } static int mappable_tanh_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat sinhxre, coshxre; phloat sinxim, cosxim; phloat re_sinh, re_cosh, im_sinh, im_cosh, abs_cosh; int inf; sinhxre = sinh(xre); coshxre = cosh(xre); sincos(xim, &sinxim, &cosxim); re_sinh = sinhxre * cosxim; im_sinh = coshxre * sinxim; re_cosh = coshxre * cosxim; im_cosh = sinhxre * sinxim; abs_cosh = hypot(re_cosh, im_cosh); if (abs_cosh == 0) { if (flags.f.range_error_ignore) { *yre = re_sinh * im_sinh + re_cosh * im_cosh > 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; *yim = im_sinh * re_cosh - re_sinh * im_cosh > 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; } else return ERR_OUT_OF_RANGE; } *yre = (re_sinh * re_cosh + im_sinh * im_cosh) / abs_cosh / abs_cosh; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = (im_sinh * re_cosh - re_sinh * im_cosh) / abs_cosh / abs_cosh; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_tanh(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_tanh_r, mappable_tanh_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } int docmd_trans(arg_struct *arg) { if (reg_x->type == TYPE_REALMATRIX) { vartype_realmatrix *src = (vartype_realmatrix *) reg_x; vartype_realmatrix *dst; int4 rows = src->rows; int4 columns = src->columns; int4 i, j; dst = (vartype_realmatrix *) new_realmatrix(columns, rows); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < rows; i++) for (j = 0; j < columns; j++) { int4 n1 = i * columns + j; int4 n2 = j * rows + i; dst->array->is_string[n2] = src->array->is_string[n1]; dst->array->data[n2] = src->array->data[n1]; } unary_result((vartype *) dst); return ERR_NONE; } else if (reg_x->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *src = (vartype_complexmatrix *) reg_x; vartype_complexmatrix *dst; int4 rows = src->rows; int4 columns = src->columns; int4 i, j; dst = (vartype_complexmatrix *) new_complexmatrix(columns, rows); if (dst == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < rows; i++) for (j = 0; j < columns; j++) { int4 n1 = 2 * (i * columns + j); int4 n2 = 2 * (j * rows + i); dst->array->data[n2] = src->array->data[n1]; dst->array->data[n2 + 1] = src->array->data[n1 + 1]; } unary_result((vartype *) dst); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_wrap(arg_struct *arg) { flags.f.grow = 0; return ERR_NONE; } int docmd_x_swap(arg_struct *arg) { vartype *v; int err = generic_rcl(arg, &v); if (err != ERR_NONE) return err; err = generic_sto(arg, 0); if (err != ERR_NONE) free_vartype(v); else { free_vartype(reg_x); reg_x = v; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } return err; } #define DIR_LEFT 0 #define DIR_RIGHT 1 #define DIR_UP 2 #define DIR_DOWN 3 static int matedit_move(int direction) { vartype *m, *v; vartype_realmatrix *rm; vartype_complexmatrix *cm; int4 rows, columns, new_i, new_j, old_n, new_n; int edge_flag = 0; int end_flag = 0; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type == TYPE_REALMATRIX) { rm = (vartype_realmatrix *) m; rows = rm->rows; columns = rm->columns; } else if (m->type == TYPE_COMPLEXMATRIX) { cm = (vartype_complexmatrix *) m; rows = cm->rows; columns = cm->columns; } else return ERR_INVALID_TYPE; if (!disentangle(m)) return ERR_INSUFFICIENT_MEMORY; new_i = matedit_i; new_j = matedit_j; switch (direction) { case DIR_LEFT: if (--new_j < 0) { edge_flag = 1; new_j = columns - 1; if (--new_i < 0) { end_flag = 1; new_i = rows - 1; } } break; case DIR_RIGHT: if (++new_j >= columns) { edge_flag = 1; new_j = 0; if (++new_i >= rows) { end_flag = 1; if (flags.f.grow) { int err; if (matedit_mode == 2) err = dimension_array_ref(matedit_x, rows + 1, columns); else err = dimension_array(matedit_name, matedit_length, rows + 1, columns); if (err != ERR_NONE) return err; new_i = rows++; } else new_i = 0; } } break; case DIR_UP: if (--new_i < 0) { edge_flag = 1; new_i = rows - 1; if (--new_j < 0) { end_flag = 1; new_j = columns - 1; } } break; case DIR_DOWN: if (++new_i >= rows) { edge_flag = 1; new_i = 0; if (++new_j >= columns) { end_flag = 1; new_j = 0; } } break; } old_n = matedit_i * columns + matedit_j; new_n = new_i * columns + new_j; if (m->type == TYPE_REALMATRIX) { if (old_n != new_n) { if (rm->array->is_string[new_n]) v = new_string(phloat_text(rm->array->data[new_n]), phloat_length(rm->array->data[new_n])); else v = new_real(rm->array->data[new_n]); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } if (reg_x->type == TYPE_REAL) { rm->array->is_string[old_n] = 0; rm->array->data[old_n] = ((vartype_real *) reg_x)->x; } else if (reg_x->type == TYPE_STRING) { vartype_string *s = (vartype_string *) reg_x; int i; rm->array->is_string[old_n] = 1; phloat_length(rm->array->data[old_n]) = s->length; for (i = 0; i < s->length; i++) phloat_text(rm->array->data[old_n])[i] = s->text[i]; } else { free_vartype(v); return ERR_INVALID_TYPE; } } else /* m->type == TYPE_COMPLEXMATRIX */ { if (old_n != new_n) { v = new_complex(cm->array->data[2 * new_n], cm->array->data[2 * new_n + 1]); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } if (reg_x->type == TYPE_REAL) { cm->array->data[2 * old_n] = ((vartype_real *) reg_x)->x; cm->array->data[2 * old_n + 1] = 0; } else if (reg_x->type == TYPE_COMPLEX) { vartype_complex *c = (vartype_complex *) reg_x; cm->array->data[2 * old_n] = c->re; cm->array->data[2 * old_n + 1] = c->im; } else { free_vartype(v); return ERR_INVALID_TYPE; } } matedit_i = new_i; matedit_j = new_j; flags.f.matrix_edge_wrap = edge_flag; flags.f.matrix_end_wrap = end_flag; if (old_n != new_n) { free_vartype(reg_x); reg_x = v; } mode_disable_stack_lift = true; if (flags.f.trace_print && flags.f.printer_enable) docmd_prx(NULL); return ERR_NONE; } int docmd_left(arg_struct *arg) { return matedit_move(DIR_LEFT); } int docmd_up(arg_struct *arg) { return matedit_move(DIR_UP); } int docmd_down(arg_struct *arg) { return matedit_move(DIR_DOWN); } int docmd_right(arg_struct *arg) { return matedit_move(DIR_RIGHT); } int docmd_percent_ch(arg_struct *arg) { phloat x, y, r; int inf; vartype *v; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; x = ((vartype_real *) reg_x)->x; y = ((vartype_real *) reg_y)->x; if (y == 0) return ERR_DIVIDE_BY_0; r = (x - y) / y * 100; if ((inf = p_isinf(r)) != 0) { if (flags.f.range_error_ignore) r = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } v = new_real(r); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; /* Binary function, but unary result, like % */ unary_result(v); return ERR_NONE; } static vartype *matx_v; static void matx_completion(int error, vartype *res) { if (error != ERR_NONE) { free_vartype(matx_v); return; } store_var("MATX", 4, res); matedit_prev_appmenu = MENU_MATRIX_SIMQ; set_menu(MENULEVEL_APP, MENU_MATRIX_EDIT1); /* NOTE: no need to use set_menu_return_err() here, since the MAT[ABX] * commands can only be invoked from the SIMQ menu; the SIMQ menu * has no exit callback, so leaving it never fails. */ set_appmenu_exitcallback(1); if (res->type == TYPE_REALMATRIX) { vartype_realmatrix *m = (vartype_realmatrix *) res; vartype_real *v = (vartype_real *) matx_v; v->x = m->array->data[0]; } else { vartype_complexmatrix *m = (vartype_complexmatrix *) res; vartype_complex *v = (vartype_complex *) matx_v; v->re = m->array->data[0]; v->im = m->array->data[1]; } free_vartype(reg_x); reg_x = matx_v; matedit_mode = 3; matedit_length = 4; matedit_name[0] = 'M'; matedit_name[1] = 'A'; matedit_name[2] = 'T'; matedit_name[3] = 'X'; matedit_i = 0; matedit_j = 0; } static int matabx(int which) { vartype *mat, *v; switch (which) { case 0: mat = recall_var("MATA", 4); break; case 1: mat = recall_var("MATB", 4); break; case 2: { vartype *mata, *matb; mata = recall_var("MATA", 4); if (mata == NULL) return ERR_NONEXISTENT; if (mata->type != TYPE_REALMATRIX && mata->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; matb = recall_var("MATB", 4); if (matb == NULL) return ERR_NONEXISTENT; if (matb->type != TYPE_REALMATRIX && matb->type != TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; if (mata->type == TYPE_REALMATRIX && matb->type == TYPE_REALMATRIX) matx_v = new_real(0); else matx_v = new_complex(0, 0); if (matx_v == NULL) return ERR_INSUFFICIENT_MEMORY; return linalg_div(matb, mata, matx_completion); } } if (mat->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) mat; if (rm->array->is_string[0]) v = new_string(phloat_text(rm->array->data[0]), phloat_length(rm->array->data[0])); else v = new_real(rm->array->data[0]); } else { vartype_complexmatrix *cm = (vartype_complexmatrix *) mat; v = new_complex(cm->array->data[0], cm->array->data[1]); } if (v == NULL) return ERR_INSUFFICIENT_MEMORY; matedit_prev_appmenu = MENU_MATRIX_SIMQ; set_menu(MENULEVEL_APP, MENU_MATRIX_EDIT1); /* NOTE: no need to use set_menu_return_err() here, since the MAT[ABX] * commands can only be invoked from the SIMQ menu; the SIMQ menu * has no exit callback, so leaving it never fails. */ set_appmenu_exitcallback(1); free_vartype(reg_x); reg_x = v; matedit_mode = 3; matedit_length = 4; matedit_name[0] = 'M'; matedit_name[1] = 'A'; matedit_name[2] = 'T'; matedit_name[3] = which == 0 ? 'A' : 'B'; matedit_i = 0; matedit_j = 0; return ERR_NONE; } int docmd_mata(arg_struct *arg) { return matabx(0); } int docmd_matb(arg_struct *arg) { return matabx(1); } int docmd_matx(arg_struct *arg) { return matabx(2); } int docmd_simq(arg_struct *arg) { vartype *m, *mata, *matb, *matx; int4 dim; int err; if (arg->type != ARGTYPE_NUM) return ERR_INVALID_TYPE; dim = arg->val.num; if (dim <= 0) return ERR_DIMENSION_ERROR; m = recall_var("MATA", 4); if (m == NULL) { mata = new_realmatrix(dim, dim); if (mata == NULL) return ERR_INSUFFICIENT_MEMORY; } else { mata = dup_vartype(m); if (mata == NULL) return ERR_INSUFFICIENT_MEMORY; err = dimension_array_ref(mata, dim, dim); if (err != ERR_NONE) return err; } m = recall_var("MATB", 4); if (m == NULL) { matb = new_realmatrix(dim, 1); if (matb == NULL) return ERR_INSUFFICIENT_MEMORY; } else { matb = dup_vartype(m); if (matb == NULL) return ERR_INSUFFICIENT_MEMORY; err = dimension_array_ref(matb, dim, 1); if (err != ERR_NONE) return err; } m = recall_var("MATX", 4); if (m == NULL) { matx = new_realmatrix(dim, 1); if (matx == NULL) return ERR_INSUFFICIENT_MEMORY; } else { matx = dup_vartype(m); if (matx == NULL) return ERR_INSUFFICIENT_MEMORY; err = dimension_array_ref(matx, dim, 1); if (err != ERR_NONE) return err; } err = set_menu_return_err(MENULEVEL_APP, MENU_MATRIX_SIMQ); if (err != ERR_NONE) { /* Didn't work; we're stuck in the matrix editor * waiting for the user to put something valid into X. * (Then again, how can anyone issue the SIMQ command if * they're in the matrix editor? SIMQ has the 'hidden' * command property. Oh, well, better safe than sorry...) */ free_vartype(mata); free_vartype(matb); free_vartype(matx); return err; } store_var("MATX", 4, matx); store_var("MATB", 4, matb); store_var("MATA", 4, mata); return ERR_NONE; } static int max_min_helper(int do_max) { vartype *m; vartype_realmatrix *rm; phloat max_or_min_value = do_max ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; int4 i, max_or_min_index = 0; vartype *new_x, *new_y; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; rm = (vartype_realmatrix *) m; for (i = matedit_i; i < rm->rows; i++) { int4 index = i * rm->columns + matedit_j; phloat e; if (rm->array->is_string[index]) return ERR_ALPHA_DATA_IS_INVALID; e = rm->array->data[index]; if (do_max ? e >= max_or_min_value : e <= max_or_min_value) { max_or_min_value = e; max_or_min_index = i; } } new_x = new_real(max_or_min_value); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; new_y = new_real(max_or_min_index + 1); if (new_y == NULL) { free_vartype(new_x); return ERR_INSUFFICIENT_MEMORY; } recall_two_results(new_x, new_y); return ERR_NONE; } int docmd_max(arg_struct *arg) { return max_min_helper(1); } int docmd_min(arg_struct *arg) { return max_min_helper(0); } int docmd_find(arg_struct *arg) { vartype *m; if (reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; switch (matedit_mode) { case 0: return ERR_NONEXISTENT; case 1: case 3: m = recall_var(matedit_name, matedit_length); break; case 2: m = matedit_x; break; default: return ERR_INTERNAL_ERROR; } if (m == NULL) return ERR_NONEXISTENT; if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm; int4 i, j, p = 0; if (reg_x->type == TYPE_COMPLEX) return ERR_NO; rm = (vartype_realmatrix *) m; if (reg_x->type == TYPE_REAL) { phloat d = ((vartype_real *) reg_x)->x; for (i = 0; i < rm->rows; i++) for (j = 0; j < rm->columns; j++) if (!rm->array->is_string[p] && rm->array->data[p] == d) { matedit_i = i; matedit_j = j; return ERR_YES; } else p++; } else /* reg_x->type == TYPE_STRING */ { vartype_string *s = (vartype_string *) reg_x; for (i = 0; i < rm->rows; i++) for (j = 0; j < rm->columns; j++) if (rm->array->is_string[p] && string_equals(s->text, s->length, phloat_text(rm->array->data[p]), phloat_length(rm->array->data[p]))) { matedit_i = i; matedit_j = j; return ERR_YES; } else p++; } } else /* m->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *cm; int4 i, j, p = 0; phloat re, im; if (reg_x->type != TYPE_COMPLEX) return ERR_NO; cm = (vartype_complexmatrix *) m; re = ((vartype_complex *) reg_x)->re; im = ((vartype_complex *) reg_x)->im; for (i = 0; i < cm->rows; i++) for (j = 0; j < cm->columns; j++) if (cm->array->data[p] == re && cm->array->data[p + 1] == im) { matedit_i = i; matedit_j = j; return ERR_YES; } else p += 2; } return ERR_NO; } int docmd_xrom(arg_struct *arg) { return ERR_NONEXISTENT; } free42-nologo-1.4.77/common/core_commands4.h000644 000765 000024 00000003771 12110237247 021177 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS4_H #define CORE_COMMANDS4_H 1 #include "free42.h" #include "core_globals.h" int docmd_insr(arg_struct *arg); int docmd_invrt(arg_struct *arg); int docmd_j_add(arg_struct *arg); int docmd_j_sub(arg_struct *arg); int docmd_ln_1_x(arg_struct *arg); int docmd_old(arg_struct *arg); int docmd_posa(arg_struct *arg); int docmd_putm(arg_struct *arg); int docmd_rclel(arg_struct *arg); int docmd_rclij(arg_struct *arg); int docmd_rnrm(arg_struct *arg); int docmd_rsum(arg_struct *arg); int docmd_swap_r(arg_struct *arg); int docmd_sinh(arg_struct *arg); int docmd_stoel(arg_struct *arg); int docmd_stoij(arg_struct *arg); int docmd_tanh(arg_struct *arg); int docmd_trans(arg_struct *arg); int docmd_wrap(arg_struct *arg); int docmd_x_swap(arg_struct *arg); int docmd_left(arg_struct *arg); int docmd_up(arg_struct *arg); int docmd_down(arg_struct *arg); int docmd_right(arg_struct *arg); int docmd_percent_ch(arg_struct *arg); int docmd_simq(arg_struct *arg); int docmd_mata(arg_struct *arg); int docmd_matb(arg_struct *arg); int docmd_matx(arg_struct *arg); int docmd_max(arg_struct *arg); int docmd_min(arg_struct *arg); int docmd_find(arg_struct *arg); int docmd_xrom(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_commands5.cc000644 000765 000024 00000071056 12110237247 021337 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands2.h" #include "core_commands5.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_math1.h" #include "core_sto_rcl.h" #include "core_variables.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 5 */ /********************************************************/ int appmenu_exitcallback_2(int menuid) { if (menuid == MENU_BASE || menuid == MENU_BASE_A_THRU_F || menuid == MENU_BASE_LOGIC) { mode_appmenu = menuid; set_appmenu_exitcallback(2); } else { set_base(10); mode_appmenu = menuid; baseapp = 0; } return ERR_NONE; } static int base_helper(int base) { if (program_running()) { int err = set_menu_return_err(MENULEVEL_APP, MENU_BASE); if (err != ERR_NONE) return err; set_appmenu_exitcallback(2); baseapp = 1; } set_base(base); return ERR_NONE; } int docmd_binm(arg_struct *arg) { return base_helper(2); } int docmd_octm(arg_struct *arg) { return base_helper(8); } int docmd_decm(arg_struct *arg) { return base_helper(10); } int docmd_hexm(arg_struct *arg) { return base_helper(16); } int docmd_linf(arg_struct *arg) { flags.f.lin_fit = 1; flags.f.log_fit = 0; flags.f.exp_fit = 0; flags.f.pwr_fit = 0; return ERR_NONE; } int docmd_logf(arg_struct *arg) { flags.f.lin_fit = 0; flags.f.log_fit = 1; flags.f.exp_fit = 0; flags.f.pwr_fit = 0; return ERR_NONE; } int docmd_expf(arg_struct *arg) { flags.f.lin_fit = 0; flags.f.log_fit = 0; flags.f.exp_fit = 1; flags.f.pwr_fit = 0; return ERR_NONE; } int docmd_pwrf(arg_struct *arg) { flags.f.lin_fit = 0; flags.f.log_fit = 0; flags.f.exp_fit = 0; flags.f.pwr_fit = 1; return ERR_NONE; } int docmd_allsigma(arg_struct *arg) { flags.f.all_sigma = 1; return ERR_NONE; } int docmd_and(arg_struct *arg) { int8 x, y; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; v = new_real((phloat) (x & y)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_baseadd(arg_struct *arg) { int8 x, y, res; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; res = x + y; if ((err = base_range_check(&res)) != ERR_NONE) return err; v = new_real((phloat) res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_basesub(arg_struct *arg) { int8 x, y, res; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; res = y - x; if ((err = base_range_check(&res)) != ERR_NONE) return err; v = new_real((phloat) res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_basemul(arg_struct *arg) { int8 x, y; double res; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; /* I compute the result in 'double' arithmetic, because doing it * in int8 arithmetic could cause me to overlook an out-of-range * condition (e.g. 2^32 * 2^32). */ res = ((double) x) * ((double) y); if (res < -34359738368.0) { if (flags.f.range_error_ignore) res = -34359738368.0; else return ERR_OUT_OF_RANGE; } else if (res > 34359738367.0) { if (flags.f.range_error_ignore) res = 34359738367.0; else return ERR_OUT_OF_RANGE; } v = new_real(res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_basediv(arg_struct *arg) { int8 x, y, res; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; if (x == 0) return ERR_DIVIDE_BY_0; res = y / x; if ((err = base_range_check(&res)) != ERR_NONE) return err; v = new_real((phloat) res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_basechs(arg_struct *arg) { int8 x; int err; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if (x == LL(-34359738368)) { if (flags.f.range_error_ignore) x = LL(34359738367); else return ERR_OUT_OF_RANGE; } else x = -x; ((vartype_real *) reg_x)->x = (phloat) x; return ERR_NONE; } static struct sum_struct { phloat x; phloat x2; phloat y; phloat y2; phloat xy; phloat n; phloat lnx; phloat lnx2; phloat lny; phloat lny2; phloat lnxlny; phloat xlny; phloat ylnx; } sum; static int get_summation() { /* Check if summation registers are OK */ int4 first = mode_sigma_reg; int4 last = first + (flags.f.all_sigma ? 13 : 6); int4 size, i; vartype *regs = recall_var("REGS", 4); vartype_realmatrix *r; phloat *sigmaregs; if (regs == NULL) return ERR_SIZE_ERROR; if (regs->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; r = (vartype_realmatrix *) regs; size = r->rows * r->columns; if (last > size) return ERR_SIZE_ERROR; for (i = first; i < last; i++) if (r->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; sigmaregs = r->array->data + first; sum.x = sigmaregs[0]; sum.x2 = sigmaregs[1]; sum.y = sigmaregs[2]; sum.y2 = sigmaregs[3]; sum.xy = sigmaregs[4]; sum.n = sigmaregs[5]; if (flags.f.all_sigma) { sum.lnx = sigmaregs[6]; sum.lnx2 = sigmaregs[7]; sum.lny = sigmaregs[8]; sum.lny2 = sigmaregs[9]; sum.lnxlny = sigmaregs[10]; sum.xlny = sigmaregs[11]; sum.ylnx = sigmaregs[12]; } return ERR_NONE; } static struct model_struct { phloat x; phloat x2; phloat y; phloat y2; phloat xy; phloat n; int ln_before; int exp_after; int valid; phloat slope; phloat yint; } model; #define MODEL_NONE -1 #define MODEL_LIN 0 #define MODEL_LOG 1 #define MODEL_EXP 2 #define MODEL_PWR 3 static int get_model_summation(int modl) { int err = get_summation(); if (err != ERR_NONE) return err; switch (modl) { case MODEL_LIN: model.xy = sum.xy; model.ln_before = 0; model.exp_after = 0; break; case MODEL_LOG: if (flags.f.log_fit_invalid) return ERR_INVALID_FORECAST_MODEL; model.xy = sum.ylnx; model.ln_before = 1; model.exp_after = 0; break; case MODEL_EXP: if (flags.f.exp_fit_invalid) return ERR_INVALID_FORECAST_MODEL; model.xy = sum.xlny; model.ln_before = 0; model.exp_after = 1; break; case MODEL_PWR: if (flags.f.pwr_fit_invalid) return ERR_INVALID_FORECAST_MODEL; model.xy = sum.lnxlny; model.ln_before = 1; model.exp_after = 1; break; default: return ERR_INVALID_FORECAST_MODEL; } if (model.ln_before) { model.x = sum.lnx; model.x2 = sum.lnx2; } else { model.x = sum.x; model.x2 = sum.x2; } if (model.exp_after) { model.y = sum.lny; model.y2 = sum.lny2; } else { model.y = sum.y; model.y2 = sum.y2; } model.n = sum.n; return ERR_NONE; } static int corr_helper(int modl, phloat *r) { phloat cov, varx, vary, v, tr; int err = get_model_summation(modl); if (err != ERR_NONE) return err; if (model.n == 0 || model.n == 1) return ERR_STAT_MATH_ERROR; cov = model.xy - model.x * model.y / model.n; varx = model.x2 - model.x * model.x / model.n; vary = model.y2 - model.y * model.y / model.n; if (varx <= 0 || vary <= 0) return ERR_STAT_MATH_ERROR; v = varx * vary; if (v == 0) return ERR_STAT_MATH_ERROR; tr = cov / sqrt(v); if (tr < -1) tr = -1; else if (tr > 1) tr = 1; *r = tr; return ERR_NONE; } static int slope_yint_helper() { /* The caller should have made sure that 'model' is up to date * by calling get_model_summation() first. */ phloat cov, varx, meanx, meany; int inf; if (model.n == 0 || model.n == 1) return ERR_STAT_MATH_ERROR; cov = model.xy - model.x * model.y / model.n; varx = model.x2 - model.x * model.x / model.n; if (varx == 0) return ERR_STAT_MATH_ERROR; model.slope = cov / varx; if ((inf = p_isinf(model.slope)) != 0) model.slope = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; meanx = model.x / model.n; meany = model.y / model.n; model.yint = meany - model.slope * meanx; if ((inf = p_isinf(model.yint)) != 0) model.yint = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; return ERR_NONE; } static int get_model() { if (flags.f.lin_fit) return MODEL_LIN; else if (flags.f.log_fit) return MODEL_LOG; else if (flags.f.exp_fit) return MODEL_EXP; else if (flags.f.pwr_fit) return MODEL_PWR; else return MODEL_NONE; } int docmd_best(arg_struct *arg) { int best = MODEL_NONE; phloat bestr = 0; int firsterr = ERR_NONE; int i; for (i = MODEL_LIN; i <= MODEL_PWR; i++) { phloat r; int err = corr_helper(i, &r); if (err == ERR_NONE) { if (r < 0) r = -r; if (r > bestr) { best = i; bestr = r; } } else { if (firsterr == ERR_NONE) firsterr = err; } } if (best == MODEL_NONE) best = MODEL_LIN; else firsterr = ERR_NONE; flags.f.lin_fit = best == MODEL_LIN; flags.f.log_fit = best == MODEL_LOG; flags.f.exp_fit = best == MODEL_EXP; flags.f.pwr_fit = best == MODEL_PWR; return firsterr; } int docmd_bit_t(arg_struct *arg) { int8 x, y; int err; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; if (x < 0 || x > 35) return ERR_INVALID_DATA; return (y & (1LL << x)) != 0 ? ERR_YES : ERR_NO; } int docmd_corr(arg_struct *arg) { phloat r; int err; vartype *rv; err = corr_helper(get_model(), &r); if (err != ERR_NONE) return err; rv = new_real(r); if (rv == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(rv); return ERR_NONE; } static int mappable_fcstx(phloat x, phloat *y) { int inf; if (model.exp_after) { if (x <= 0) return ERR_INVALID_FORECAST_MODEL; x = log(x); } if (model.slope == 0) return ERR_STAT_MATH_ERROR; x = (x - model.yint) / model.slope; if (model.ln_before) x = exp(x); if ((inf = p_isinf(x)) != 0) x = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; *y = x; return ERR_NONE; } int docmd_fcstx(arg_struct *arg) { int err = get_model_summation(get_model()); vartype *v; if (err != ERR_NONE) return err; err = slope_yint_helper(); if (err != ERR_NONE) return err; if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { err = map_unary(reg_x, &v, mappable_fcstx, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static int mappable_fcsty(phloat x, phloat *y) { int inf; if (model.ln_before) { if (x <= 0) return ERR_INVALID_FORECAST_MODEL; x = log(x); } x = x * model.slope + model.yint; if (model.exp_after) x = exp(x); if ((inf = p_isinf(x)) != 0) x = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; *y = x; return ERR_NONE; } int docmd_fcsty(arg_struct *arg) { int err = get_model_summation(get_model()); vartype *v; if (err != ERR_NONE) return err; err = slope_yint_helper(); if (err != ERR_NONE) return err; if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_REALMATRIX) { err = map_unary(reg_x, &v, mappable_fcsty, NULL); if (err == ERR_NONE) unary_result(v); return err; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_mean(arg_struct *arg) { phloat m; int inf; vartype *mx, *my; int err = get_summation(); if (err != ERR_NONE) return err; if (sum.n == 0) return ERR_STAT_MATH_ERROR; m = sum.x / sum.n; if ((inf = p_isinf(m)) != 0) m = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; mx = new_real(m); if (mx == NULL) return ERR_INSUFFICIENT_MEMORY; m = sum.y / sum.n; if ((inf = p_isinf(m)) != 0) m = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; my = new_real(m); if (my == NULL) { free_vartype(mx); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_y); reg_y = my; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = mx; if (flags.f.trace_print && flags.f.printer_enable) docmd_prx(NULL); return ERR_NONE; } int docmd_sdev(arg_struct *arg) { int err = get_summation(); phloat var; vartype *sx, *sy; if (err != ERR_NONE) return err; if (sum.n == 0 || sum.n == 1) return ERR_STAT_MATH_ERROR; var = (sum.x2 - (sum.x * sum.x / sum.n)) / (sum.n - 1); if (var < 0) return ERR_STAT_MATH_ERROR; if (p_isinf(var)) sx = new_real(POS_HUGE_PHLOAT); else sx = new_real(sqrt(var)); if (sx == NULL) return ERR_INSUFFICIENT_MEMORY; var = (sum.y2 - (sum.y * sum.y / sum.n)) / (sum.n - 1); if (var < 0) return ERR_STAT_MATH_ERROR; if (p_isinf(var)) sy = new_real(POS_HUGE_PHLOAT); else sy = new_real(sqrt(var)); if (sy == NULL) { free_vartype(sx); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_y); reg_y = sy; free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = sx; if (flags.f.trace_print && flags.f.printer_enable) docmd_prx(NULL); return ERR_NONE; } int docmd_slope(arg_struct *arg) { int err = get_model_summation(get_model()); vartype *v; if (err != ERR_NONE) return err; err = slope_yint_helper(); if (err != ERR_NONE) return err; v = new_real(model.slope); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_sum(arg_struct *arg) { int err = get_summation(); vartype *sx, *sy; if (err != ERR_NONE) return err; sx = new_real(sum.x); if (sx == NULL) return ERR_INSUFFICIENT_MEMORY; sy = new_real(sum.y); if (sy == NULL) { free_vartype(sx); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_lastx); free_vartype(reg_y); reg_y = sy; reg_lastx = reg_x; reg_x = sx; if (flags.f.trace_print && flags.f.printer_enable) docmd_prx(NULL); return ERR_NONE; } int docmd_wmean(arg_struct *arg) { phloat wm; int inf; vartype *v; int err = get_summation(); if (err != ERR_NONE) return err; if (sum.y == 0) return ERR_STAT_MATH_ERROR; wm = sum.xy / sum.y; if ((inf = p_isinf(wm)) != 0) wm = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; v = new_real(wm); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_yint(arg_struct *arg) { int err = get_model_summation(get_model()); phloat yint; vartype *v; if (err != ERR_NONE) return err; err = slope_yint_helper(); if (err != ERR_NONE) return err; if (model.exp_after) { yint = exp(model.yint); if (p_isinf(yint) != 0) yint = POS_HUGE_PHLOAT; } else yint = model.yint; v = new_real(yint); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; recall_result(v); return ERR_NONE; } int docmd_integ(arg_struct *arg) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; if (!program_running()) clear_all_rtns(); string_copy(reg_alpha, ®_alpha_length, arg->val.text, arg->length); return start_integ(arg->val.text, arg->length); } int docmd_linsigma(arg_struct *arg) { flags.f.all_sigma = 0; return ERR_NONE; } int docmd_not(arg_struct *arg) { int8 x; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; v = new_real((phloat) ~x); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } int docmd_or(arg_struct *arg) { int8 x, y; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; v = new_real((phloat) (x | y)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_pgmslv(arg_struct *arg) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type == ARGTYPE_STR) { int prgm; int4 pc; if (!find_global_label(arg, &prgm, &pc)) return ERR_LABEL_NOT_FOUND; set_solve_prgm(arg->val.text, arg->length); return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_pgmint(arg_struct *arg) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type == ARGTYPE_STR) { int prgm; int4 pc; if (!find_global_label(arg, &prgm, &pc)) return ERR_LABEL_NOT_FOUND; set_integ_prgm(arg->val.text, arg->length); return ERR_NONE; } else return ERR_INVALID_TYPE; } int appmenu_exitcallback_3(int menuid) { if (menuid == MENU_NONE) { set_menu(MENULEVEL_APP, MENU_CATALOG); set_cat_section(CATSECT_PGM_SOLVE); } else mode_appmenu = menuid; return ERR_NONE; } int docmd_pgmslvi(arg_struct *arg) { /* This command can only be invoked from a menu; we assume that * the menu handler only gives us valid arguments. We do check * the argument type, but the existence of the named label, and * whether it actually has MVAR instructions, we just assume. */ if (arg->type == ARGTYPE_STR) { set_solve_prgm(arg->val.text, arg->length); string_copy(varmenu, &varmenu_length, arg->val.text, arg->length); varmenu_row = 0; varmenu_role = 1; set_menu(MENULEVEL_APP, MENU_VARMENU); set_appmenu_exitcallback(3); return ERR_NONE; } else return ERR_INVALID_TYPE; } int appmenu_exitcallback_4(int menuid) { if (menuid == MENU_NONE) { set_menu(MENULEVEL_APP, MENU_CATALOG); set_cat_section(CATSECT_PGM_INTEG); } else mode_appmenu = menuid; return ERR_NONE; } int appmenu_exitcallback_5(int menuid) { if (menuid == MENU_NONE) { get_integ_prgm(varmenu, &varmenu_length); varmenu_row = 0; varmenu_role = 2; set_menu(MENULEVEL_APP, MENU_VARMENU); set_appmenu_exitcallback(4); } else mode_appmenu = menuid; return ERR_NONE; } int docmd_pgminti(arg_struct *arg) { /* This command can only be invoked from a menu; we assume that * the menu handler only gives us valid arguments. We do check * the argument type, but the existence of the named label, and * whether it actually has MVAR instructions, we just assume. */ if (arg->type == ARGTYPE_STR) { set_integ_prgm(arg->val.text, arg->length); string_copy(varmenu, &varmenu_length, arg->val.text, arg->length); varmenu_row = 0; varmenu_role = 2; set_menu(MENULEVEL_APP, MENU_VARMENU); set_appmenu_exitcallback(4); clear_row(0); draw_string(0, 0, "Set Vars; Select \003var", 21); flags.f.message = 1; flags.f.two_line_message = 0; mode_varmenu = true; return ERR_NONE; } else return ERR_INVALID_TYPE; } int docmd_rotxy(arg_struct *arg) { int8 x, y, res; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; if (x < -35 || x > 35) return ERR_INVALID_DATA; if (x == 0) res = y; else { y &= LL(0xfffffffff); if (x > 0) res = (y >> x) | (y << (36 - x)); else { x = -x; res = (y << x) | (y >> (36 - x)); } if ((res & LL(0x800000000)) == 0) res &= LL(0x7ffffffff); else res |= LL(0xfffffff000000000); } v = new_real((phloat) res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_solve(arg_struct *arg) { int err; vartype *v; phloat x1, x2; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; v = recall_var(arg->val.text, arg->length); if (v == 0) x1 = 0; else if (v->type == TYPE_REAL) x1 = ((vartype_real *) v)->x; else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; if (reg_x->type == TYPE_REAL) x2 = ((vartype_real *) reg_x)->x; else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; if (!program_running()) clear_all_rtns(); string_copy(reg_alpha, ®_alpha_length, arg->val.text, arg->length); return start_solve(arg->val.text, arg->length, x1, x2); } int docmd_vmsolve(arg_struct *arg) { vartype *v; phloat x1, x2; if (arg->type != ARGTYPE_STR) return ERR_INVALID_TYPE; v = recall_var(arg->val.text, arg->length); if (v == NULL) { x1 = 0; x2 = 1; } else if (v->type == TYPE_REAL) { x1 = ((vartype_real *) v)->x; if (!get_shadow(arg->val.text, arg->length, &x2)) x2 = x1; } else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; clear_all_rtns(); string_copy(reg_alpha, ®_alpha_length, arg->val.text, arg->length); return start_solve(arg->val.text, arg->length, x1, x2); } int docmd_xor(arg_struct *arg) { int8 x, y; int err; vartype *v; if ((err = get_base_param(reg_x, &x)) != ERR_NONE) return err; if ((err = get_base_param(reg_y, &y)) != ERR_NONE) return err; v = new_real((phloat) (x ^ y)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(v); return ERR_NONE; } int docmd_to_dec(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat oct = ((vartype_real *) reg_x)->x; phloat res; int neg = oct < 0; if (neg) oct = -oct; if (oct > 777777777777.0 || oct != floor(oct)) return ERR_INVALID_DATA; vartype *v; #ifdef BCD_MATH phloat dec = 0, mul = 1; while (oct != 0) { int digit = to_digit(oct); if (digit > 7) return ERR_INVALID_DATA; oct = floor(oct / 10); dec += digit * mul; mul *= 8; } res = neg ? -dec : dec; #else int8 ioct = to_int8(oct); int8 dec = 0, mul = 1; while (ioct != 0) { int digit = (int) (ioct % 10); if (digit > 7) return ERR_INVALID_DATA; ioct /= 10; dec += digit * mul; mul <<= 3; } res = (double) (neg ? -dec : dec); #endif v = new_real(res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } int docmd_to_oct(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat dec = ((vartype_real *) reg_x)->x; phloat res; int neg = dec < 0; if (neg) dec = -dec; if (dec > 68719476735.0 || dec != floor(dec)) return ERR_INVALID_DATA; vartype *v; #ifdef BCD_MATH phloat oct = 0, mul = 1; while (dec != 0) { int digit = to_int(fmod(dec, 8)); dec = floor(dec / 8); oct += digit * mul; mul *= 10; } res = neg ? -oct : oct; #else int8 idec = to_int8(dec); int8 oct = 0, mul = 1; while (idec != 0) { int digit = (int) (idec & 7); idec >>= 3; oct += digit * mul; mul *= 10; } res = (double) (neg ? -oct : oct); #endif v = new_real(res); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } static void accum(phloat *sum, phloat term, int weight) { int inf; phloat s; if (weight == 1) s = *sum + term; else s = *sum - term; if ((inf = p_isinf(s)) != 0) s = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; *sum = s; } static phloat sigma_helper_2(phloat *sigmaregs, phloat x, phloat y, int weight) { accum(&sigmaregs[0], x, weight); accum(&sigmaregs[1], x * x, weight); accum(&sigmaregs[2], y, weight); accum(&sigmaregs[3], y * y, weight); accum(&sigmaregs[4], x * y, weight); accum(&sigmaregs[5], 1, weight); if (flags.f.all_sigma) { if (x > 0) { phloat lnx = log(x); if (y > 0) { phloat lny = log(y); accum(&sigmaregs[8], lny, weight); accum(&sigmaregs[9], lny * lny, weight); accum(&sigmaregs[10], lnx * lny, weight); accum(&sigmaregs[11], x * lny, weight); } else { flags.f.exp_fit_invalid = 1; flags.f.pwr_fit_invalid = 1; } accum(&sigmaregs[6], lnx, weight); accum(&sigmaregs[7], lnx * lnx, weight); accum(&sigmaregs[12], lnx * y, weight); } else { if (y > 0) { phloat lny = log(y); accum(&sigmaregs[8], lny, weight); accum(&sigmaregs[9], lny * lny, weight); accum(&sigmaregs[11], x * lny, weight); } else flags.f.exp_fit_invalid = 1; flags.f.log_fit_invalid = 1; flags.f.pwr_fit_invalid = 1; } } else { flags.f.log_fit_invalid = 1; flags.f.exp_fit_invalid = 1; flags.f.pwr_fit_invalid = 1; } return sigmaregs[5]; } static int sigma_helper_1(int weight) { /* Check if summation registers are OK */ int4 first = mode_sigma_reg; int4 last = first + (flags.f.all_sigma ? 13 : 6); int4 size, i; vartype *regs = recall_var("REGS", 4); vartype_realmatrix *r; phloat *sigmaregs; if (regs == NULL) return ERR_SIZE_ERROR; if (regs->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; r = (vartype_realmatrix *) regs; size = r->rows * r->columns; if (last > size) return ERR_SIZE_ERROR; for (i = first; i < last; i++) if (r->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; sigmaregs = r->array->data + first; /* All summation registers present, real-valued, non-string. */ switch (reg_x->type) { case TYPE_REAL: { if (reg_y->type == TYPE_REAL) { vartype_real *x = (vartype_real *) new_real(0); if (x == NULL) return ERR_INSUFFICIENT_MEMORY; x->x = sigma_helper_2(sigmaregs, ((vartype_real *) reg_x)->x, ((vartype_real *) reg_y)->x, weight); free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = (vartype *) x; mode_disable_stack_lift = true; return ERR_NONE; } else if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return ERR_INVALID_TYPE; } case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; vartype_real *x; int4 i; if (rm->columns != 2) return ERR_DIMENSION_ERROR; for (i = 0; i < rm->rows * 2; i++) if (rm->array->is_string[i]) return ERR_ALPHA_DATA_IS_INVALID; x = (vartype_real *) new_real(0); if (x == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < rm->rows; i++) x->x = sigma_helper_2(sigmaregs, rm->array->data[i * 2], rm->array->data[i * 2 + 1], weight); free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = (vartype *) x; mode_disable_stack_lift = true; return ERR_NONE; } case TYPE_STRING: return ERR_ALPHA_DATA_IS_INVALID; default: return ERR_INVALID_TYPE; } } int docmd_sigmaadd(arg_struct *arg) { int err = sigma_helper_1(1); if (err == ERR_NONE && flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return err; } int docmd_sigmasub(arg_struct *arg) { int err = sigma_helper_1(-1); if (err == ERR_NONE && flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return err; } free42-nologo-1.4.77/common/core_commands5.h000644 000765 000024 00000004505 12110237247 021174 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS5_H #define CORE_COMMANDS5_H 1 #include "free42.h" #include "core_globals.h" int docmd_binm(arg_struct *arg); int docmd_octm(arg_struct *arg); int docmd_decm(arg_struct *arg); int docmd_hexm(arg_struct *arg); int docmd_linf(arg_struct *arg); int docmd_logf(arg_struct *arg); int docmd_expf(arg_struct *arg); int docmd_pwrf(arg_struct *arg); int docmd_allsigma(arg_struct *arg); int docmd_and(arg_struct *arg); int docmd_baseadd(arg_struct *arg); int docmd_basesub(arg_struct *arg); int docmd_basemul(arg_struct *arg); int docmd_basediv(arg_struct *arg); int docmd_basechs(arg_struct *arg); int docmd_best(arg_struct *arg); int docmd_bit_t(arg_struct *arg); int docmd_corr(arg_struct *arg); int docmd_fcstx(arg_struct *arg); int docmd_fcsty(arg_struct *arg); int docmd_mean(arg_struct *arg); int docmd_sdev(arg_struct *arg); int docmd_slope(arg_struct *arg); int docmd_sum(arg_struct *arg); int docmd_wmean(arg_struct *arg); int docmd_yint(arg_struct *arg); int docmd_integ(arg_struct *arg); int docmd_linsigma(arg_struct *arg); int docmd_not(arg_struct *arg); int docmd_or(arg_struct *arg); int docmd_pgmint(arg_struct *arg); int docmd_pgmslv(arg_struct *arg); int docmd_pgminti(arg_struct *arg); int docmd_pgmslvi(arg_struct *arg); int docmd_rotxy(arg_struct *arg); int docmd_solve(arg_struct *arg); int docmd_vmsolve(arg_struct *arg); int docmd_xor(arg_struct *arg); int docmd_to_dec(arg_struct *arg); int docmd_to_oct(arg_struct *arg); int docmd_sigmaadd(arg_struct *arg); int docmd_sigmasub(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_commands6.cc000644 000765 000024 00000055414 12110237247 021340 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands6.h" #include "core_helpers.h" #include "core_math2.h" #include "core_sto_rcl.h" #include "core_variables.h" /********************************************************/ /* Implementations of HP-42S built-in functions, part 6 */ /********************************************************/ static int mappable_sin_r(phloat x, phloat *y) { if (flags.f.rad) { *y = sin(x); } else if (flags.f.grad) { x = fmod(x, 400); if (x < 0) x += 400; if (x == 0 || x == 200) *y = 0; else if (x == 100) *y = 1; else if (x == 300) *y = -1; else *y = sin(x / (200 / PI)); } else { x = fmod(x, 360); if (x < 0) x += 360; if (x == 0 || x == 180) *y = 0; else if (x == 90) *y = 1; else if (x == 270) *y = -1; else *y = sin(x / (180 / PI)); } return ERR_NONE; } static int mappable_sin_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* NOTE: DEG/RAD/GRAD mode does not apply here. */ phloat sinxre, cosxre; phloat sinhxim, coshxim; int inf; sincos(xre, &sinxre, &cosxre); sinhxim = sinh(xim); coshxim = cosh(xim); *yre = sinxre * coshxim; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = cosxre * sinhxim; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_sin(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_sin_r, mappable_sin_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } static int mappable_cos_r(phloat x, phloat *y) { if (flags.f.rad) { *y = cos(x); } else if (flags.f.grad) { x = fmod(x, 400); if (x < 0) x += 400; if (x == 0) *y = 1; else if (x == 100 || x == 300) *y = 0; else if (x == 200) *y = -1; else *y = cos(x / (200 / PI)); } else { x = fmod(x, 360); if (x < 0) x += 360; if (x == 0) *y = 1; else if (x == 90 || x == 270) *y = 0; else if (x == 180) *y = -1; else *y = cos(x / (180 / PI)); } return ERR_NONE; } static int mappable_cos_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* NOTE: DEG/RAD/GRAD mode does not apply here. */ phloat sinxre, cosxre; phloat sinhxim, coshxim; int inf; sincos(xre, &sinxre, &cosxre); sinhxim = sinh(xim); coshxim = cosh(xim); *yre = cosxre * coshxim; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = -sinxre * sinhxim; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_cos(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_cos_r, mappable_cos_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } static int mappable_tan_r(phloat x, phloat *y) { int inf = 1; if (flags.f.rad) { *y = tan(x); } else if (flags.f.grad) { x = fmod(x, 200); if (x < 0) x += 200; if (x == 0) *y = 0; else if (x == 50) *y = 1; else if (x == 100) goto infinite; else if (x == 150) *y = -1; else *y = tan(x / (200 / PI)); } else { x = fmod(x, 180); if (x < 0) x += 180; if (x == 0) *y = 0; else if (x == 45) *y = 1; else if (x == 90) goto infinite; else if (x == 135) *y = -1; else *y = tan(x / (180 / PI)); } if (p_isnan(*y)) goto infinite; if ((inf = p_isinf(*y)) != 0) { infinite: if (flags.f.range_error_ignore) *y = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } static int mappable_tan_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* NOTE: DEG/RAD/GRAD mode does not apply here. */ phloat sinxre, cosxre; phloat sinhxim, coshxim; phloat re_sin, im_sin, re_cos, im_cos, abs_cos; int inf; sincos(xre, &sinxre, &cosxre); sinhxim = sinh(xim); coshxim = cosh(xim); re_sin = sinxre * coshxim; im_sin = cosxre * sinhxim; re_cos = cosxre * coshxim; im_cos = -sinxre * sinhxim; abs_cos = hypot(re_cos, im_cos); if (abs_cos == 0) { if (flags.f.range_error_ignore) { *yre = re_sin * re_cos + im_sin * im_cos > 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; *yim = im_sin * re_cos - re_sin * im_cos > 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; return ERR_NONE; } else return ERR_OUT_OF_RANGE; } *yre = (re_sin * re_cos + im_sin * im_cos) / abs_cos / abs_cos; if ((inf = p_isinf(*yre)) != 0) { if (flags.f.range_error_ignore) *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yim = (im_sin * re_cos - re_sin * im_cos) / abs_cos / abs_cos; if ((inf = p_isinf(*yim)) != 0) { if (flags.f.range_error_ignore) *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } int docmd_tan(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_tan_r, mappable_tan_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } static int mappable_asin_r(phloat x, phloat *y) { if (x < -1 || x > 1) return ERR_INVALID_DATA; *y = rad_to_angle(asin(x)); return ERR_NONE; } static int mappable_asin_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat tre, tim; int err = math_asinh(-xim, xre, &tre, &tim); *yre = tim; *yim = -tre; return err; } int docmd_asin(arg_struct *arg) { vartype *v; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x < -1 || x > 1) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { if (x > 0) v = new_complex(PI / 2, -acosh(x)); else v = new_complex(-PI / 2, acosh(-x)); } } else v = new_real(rad_to_angle(asin(x))); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } else { int err = map_unary(reg_x, &v, mappable_asin_r, mappable_asin_c); if (err != ERR_NONE) return err; } unary_result(v); return ERR_NONE; } static int mappable_acos_r(phloat x, phloat *y) { if (x < -1 || x > 1) return ERR_INVALID_DATA; *y = rad_to_angle(acos(x)); return ERR_NONE; } static int mappable_acos_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat tre, tim; int err = math_acosh(xre, xim, &tre, &tim); *yre = tim; *yim = -tre; return err; } int docmd_acos(arg_struct *arg) { vartype *v; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x < -1 || x > 1) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { /* TODO: review */ if (x > 0) v = new_complex(0, acosh(x)); else v = new_complex(PI, -acosh(-x)); } } else v = new_real(rad_to_angle(acos(x))); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; } else { int err = map_unary(reg_x, &v, mappable_acos_r, mappable_acos_c); if (err != ERR_NONE) return err; } unary_result(v); return ERR_NONE; } static int mappable_atan_r(phloat x, phloat *y) { *y = rad_to_angle(atan(x)); return ERR_NONE; } static int mappable_atan_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat tre, tim; int err = math_atanh(xim, -xre, &tre, &tim); *yre = -tim; *yim = tre; return err; } int docmd_atan(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else { vartype *v; int err = map_unary(reg_x, &v, mappable_atan_r, mappable_atan_c); if (err == ERR_NONE) unary_result(v); return err; } } static int mappable_log_r(phloat x, phloat *y) { if (x <= 0) return ERR_INVALID_DATA; else { *y = log10(x); return ERR_NONE; } } static int mappable_log_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat h = hypot(xre, xim); if (h == 0) return ERR_INVALID_DATA; else { if (p_isinf(h)) { const phloat s = 10000; h = hypot(xre / s, xim / s); *yre = log10(h) + 4; } else *yre = log10(h); *yim = atan2(xim, xre) / log(10.0); return ERR_NONE; } } int docmd_log(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REAL) { vartype_real *x = (vartype_real *) reg_x; if (x->x == 0) return ERR_INVALID_DATA; else if (x->x < 0) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { vartype *r = new_complex(log10(-x->x), PI / log(10.0)); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; else { unary_result(r); return ERR_NONE; } } } else { vartype *r = new_real(log10(x->x)); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; else { unary_result(r); return ERR_NONE; } } } else { vartype *v; int err = map_unary(reg_x, &v, mappable_log_r, mappable_log_c); if (err == ERR_NONE) unary_result(v); return err; } } static int mappable_10_pow_x_r(phloat x, phloat *y) { *y = pow(10, x); if (p_isinf(*y) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; *y = POS_HUGE_PHLOAT; } return ERR_NONE; } static int mappable_10_pow_x_c(phloat xre, phloat xim, phloat *yre, phloat *yim){ int inf; phloat h; xim *= log(10.0); if ((inf = p_isinf(xim)) != 0) xim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; h = pow(10, xre); if ((inf = p_isinf(h)) == 0) { *yre = cos(xim) * h; *yim = sin(xim) * h; return ERR_NONE; } else if (flags.f.range_error_ignore) { phloat t = cos(xim); if (t == 0) *yre = 0; else if (t < 0) *yre = inf < 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; t = sin(xim); if (t == 0) *yim = 0; else if (t < 0) *yim = inf < 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; return ERR_NONE; } else return ERR_OUT_OF_RANGE; } int docmd_10_pow_x(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_10_pow_x_r, mappable_10_pow_x_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } static int mappable_ln_r(phloat x, phloat *y) { if (x <= 0) return ERR_INVALID_DATA; else { *y = log(x); return ERR_NONE; } } static int mappable_ln_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat h = hypot(xre, xim); if (h == 0) return ERR_INVALID_DATA; else { if (p_isinf(h)) { const phloat s = 10000; h = hypot(xre / s, xim / s); *yre = log(h) + log(s); } else *yre = log(h); *yim = atan2(xim, xre); return ERR_NONE; } } int docmd_ln(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REAL) { vartype_real *x = (vartype_real *) reg_x; if (x->x == 0) return ERR_INVALID_DATA; else if (x->x < 0) { if (flags.f.real_result_only) return ERR_INVALID_DATA; else { vartype *r = new_complex(log(-x->x), PI); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; else { unary_result(r); return ERR_NONE; } } } else { vartype *r = new_real(log(x->x)); if (r == NULL) return ERR_INSUFFICIENT_MEMORY; else { unary_result(r); return ERR_NONE; } } } else { vartype *v; int err = map_unary(reg_x, &v, mappable_ln_r, mappable_ln_c); if (err == ERR_NONE) unary_result(v); return err; } } static int mappable_e_pow_x_r(phloat x, phloat *y) { *y = exp(x); if (p_isinf(*y) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; *y = POS_HUGE_PHLOAT; } return ERR_NONE; } static int mappable_e_pow_x_c(phloat xre, phloat xim, phloat *yre, phloat *yim){ phloat h = exp(xre); int inf = p_isinf(h); if (inf == 0) { *yre = cos(xim) * h; *yim = sin(xim) * h; return ERR_NONE; } else if (flags.f.range_error_ignore) { phloat t = cos(xim); if (t == 0) *yre = 0; else if (t < 0) *yre = inf < 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; t = sin(xim); if (t == 0) *yim = 0; else if (t < 0) *yim = inf < 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; return ERR_NONE; } else return ERR_OUT_OF_RANGE; } int docmd_e_pow_x(arg_struct *arg) { if (reg_x->type != TYPE_STRING) { vartype *v; int err = map_unary(reg_x, &v, mappable_e_pow_x_r, mappable_e_pow_x_c); if (err == ERR_NONE) unary_result(v); return err; } else return ERR_ALPHA_DATA_IS_INVALID; } static int mappable_sqrt_r(phloat x, phloat *y) { if (x < 0) return ERR_INVALID_DATA; else { *y = sqrt(x); return ERR_NONE; } } static int mappable_sqrt_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* TODO: review -- is there a better way, without all the trig? */ phloat r = sqrt(hypot(xre, xim)); phloat phi = atan2(xim, xre) / 2; phloat s, c; sincos(phi, &s, &c); *yre = r * c; *yim = r * s; return ERR_NONE; } int docmd_sqrt(arg_struct *arg) { if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; vartype *v; if (x < 0) { if (flags.f.real_result_only) return ERR_INVALID_DATA; v = new_complex(0, sqrt(-x)); } else v = new_real(sqrt(x)); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(v); return ERR_NONE; } else if (reg_x->type == TYPE_STRING) { return ERR_ALPHA_DATA_IS_INVALID; } else { vartype *v; int err = map_unary(reg_x, &v, mappable_sqrt_r, mappable_sqrt_c); if (err != ERR_NONE) return err; unary_result(v); return ERR_NONE; } } static int mappable_square_r(phloat x, phloat *y) { phloat r = x * x; int inf; if ((inf = p_isinf(r)) != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *y = r; return ERR_NONE; } static int mappable_square_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat rre = xre * xre - xim * xim; phloat rim = 2 * xre * xim; int inf; if ((inf = p_isinf(rre)) != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } if ((inf = p_isinf(rim)) != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *yre = rre; *yim = rim; return ERR_NONE; } int docmd_square(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else { vartype *v; int err = map_unary(reg_x, &v, mappable_square_r, mappable_square_c); if (err == ERR_NONE) unary_result(v); return err; } } static int mappable_inv_r(phloat x, phloat *y) { int inf; if (x == 0) return ERR_DIVIDE_BY_0; *y = 1 / x; if ((inf = p_isinf(*y)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; *y = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } return ERR_NONE; } static int mappable_inv_c(phloat xre, phloat xim, phloat *yre, phloat *yim) { int inf; phloat h = hypot(xre, xim); if (h == 0) return ERR_DIVIDE_BY_0; *yre = xre / h / h; if ((inf = p_isinf(*yre)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; *yre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } *yim = (-xim) / h / h; if ((inf = p_isinf(*yim)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; *yim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } return ERR_NONE; } int docmd_inv(arg_struct *arg) { if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else { vartype *v; int err = map_unary(reg_x, &v, mappable_inv_r, mappable_inv_c); if (err == ERR_NONE) unary_result(v); return err; } } int docmd_y_pow_x(arg_struct *arg) { phloat yr, yphi; int inf; vartype *res; if (reg_x->type == TYPE_STRING || reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type == TYPE_REALMATRIX || reg_x->type == TYPE_COMPLEXMATRIX || reg_y->type == TYPE_REALMATRIX || reg_y->type == TYPE_COMPLEXMATRIX) return ERR_INVALID_TYPE; else if (reg_x->type == TYPE_REAL) { phloat x = ((vartype_real *) reg_x)->x; if (x == floor(x)) { /* Integer exponent */ if (reg_y->type == TYPE_REAL) { /* Real number to integer power */ phloat y = ((vartype_real *) reg_y)->x; phloat r = pow(y, x); if (p_isnan(r)) /* Should not happen; pow() is supposed to be able * to raise negative numbers to integer exponents */ return ERR_INVALID_DATA; if ((inf = p_isinf(r)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; r = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } res = new_real(r); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } else { /* Complex number to integer power */ phloat rre, rim, yre, yim; int4 ex; if (x < -2147483647.0 || x > 2147483647.0) /* For really huge exponents, the repeated-squaring * algorithm for integer exponents loses its accuracy * and speed advantage, and we switch to the general * complex-to-real-power code instead. */ goto complex_pow_real_1; rre = 1; rim = 0; yre = ((vartype_complex *) reg_y)->re; yim = ((vartype_complex *) reg_y)->im; ex = to_int4(x); if (yre == 0 && yim == 0) { if (ex < 0) return ERR_INVALID_DATA; else if (ex == 0) { res = new_complex(1, 0); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } } if (ex < 0) { phloat h = hypot(yre, yim); yre = yre / h / h; yim = (-yim) / h / h; ex = -ex; } while (1) { phloat tmp; if ((ex & 1) != 0) { tmp = rre * yre - rim * yim; rim = rre * yim + rim * yre; rre = tmp; /* TODO: can one component be infinite while * the other is zero? If yes, how do we handle * that? */ if (p_isinf(rre) && p_isinf(rim)) break; if (rre == 0 && rim == 0) break; } ex >>= 1; if (ex == 0) break; tmp = yre * yre - yim * yim; yim = 2 * yre * yim; yre = tmp; } if ((inf = p_isinf(rre)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; rre = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if ((inf = p_isinf(rim)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; rim = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } res = new_complex(rre, rim); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } } else if (reg_y->type == TYPE_REAL) { /* Real number to noninteger real power */ phloat y = ((vartype_real *) reg_y)->x; phloat r; if (y < 0) { if (flags.f.real_result_only) return ERR_INVALID_DATA; yr = -y; yphi = PI; goto complex_pow_real_2; } r = pow(y, x); inf = p_isinf(r); if (inf != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; r = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } res = new_real(r); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } else { /* Complex (or negative real) number to noninteger real power */ complex_pow_real_1: { phloat yre = ((vartype_complex *) reg_y)->re; phloat yim = ((vartype_complex *) reg_y)->im; yr = hypot(yre, yim); yphi = atan2(yim, yre); } complex_pow_real_2: yr = pow(yr, x); yphi *= x; phloat rre, rim; if ((inf = p_isinf(yr)) != 0) { if (!flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else { phloat re, im; sincos(yphi, &im, &re); rre = re == 0 ? 0 : re < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; rim = im == 0 ? 0 : im < 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; } } else { phloat re, im; sincos(yphi, &im, &re); rre = yr * re; rim = yr * im; } res = new_complex(rre, rim); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } } else { /* Real or complex number to complex power */ phloat xre = ((vartype_complex *) reg_x)->re; phloat xim = ((vartype_complex *) reg_x)->im; phloat yre, yim; phloat lre, lim; phloat tmp; int err; if (reg_y->type == TYPE_REAL) { yre = ((vartype_real *) reg_y)->x; yim = 0; } else { yre = ((vartype_complex *) reg_y)->re; yim = ((vartype_complex *) reg_y)->im; } if (yre == 0 && yim == 0) { if (xre < 0 || (xre == 0 && xim != 0)) return ERR_INVALID_DATA; else if (xre == 0) res = new_complex(1, 0); else res = new_complex(0, 0); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } err = mappable_ln_c(yre, yim, &lre, &lim); if (err != ERR_NONE) return err; tmp = lre * xre - lim * xim; lim = lre * xim + lim * xre; lre = tmp; err = mappable_e_pow_x_c(lre, lim, &xre, &xim); if (err != ERR_NONE) return err; res = new_complex(xre, xim); if (res == NULL) return ERR_INSUFFICIENT_MEMORY; else { binary_result(res); return ERR_NONE; } } } free42-nologo-1.4.77/common/core_commands6.h000644 000765 000024 00000002573 12110237247 021200 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS6_H #define CORE_COMMANDS6_H 1 #include "free42.h" #include "core_globals.h" int docmd_sin(arg_struct *arg); int docmd_cos(arg_struct *arg); int docmd_tan(arg_struct *arg); int docmd_asin(arg_struct *arg); int docmd_acos(arg_struct *arg); int docmd_atan(arg_struct *arg); int docmd_log(arg_struct *arg); int docmd_10_pow_x(arg_struct *arg); int docmd_ln(arg_struct *arg); int docmd_e_pow_x(arg_struct *arg); int docmd_sqrt(arg_struct *arg); int docmd_square(arg_struct *arg); int docmd_inv(arg_struct *arg); int docmd_y_pow_x(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_commands7.cc000644 000765 000024 00000041742 12110237247 021340 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_commands2.h" #include "core_commands7.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_variables.h" #include "shell.h" ////////////////////////////////////////////////////////////////////////// ///// Accelerometer, Location Services, and Compass support ///// ///// iPhone only, for now. In order to compile this, the shell must ///// ///// provide shell_get_acceleration() etc., and those are only ///// ///// implemented in the iPhone shell so far. ///// ////////////////////////////////////////////////////////////////////////// #if defined(ANDROID) || defined(IPHONE) int docmd_accel(arg_struct *arg) { if (!core_settings.enable_ext_accel) return ERR_NONEXISTENT; double x, y, z; int err = shell_get_acceleration(&x, &y, &z); if (err == 0) return ERR_NONEXISTENT; vartype *new_x = new_real(x); vartype *new_y = new_real(y); vartype *new_z = new_real(z); if (new_x == NULL || new_y == NULL || new_z == NULL) { free_vartype(new_x); free_vartype(new_y); free_vartype(new_z); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_t); free_vartype(reg_z); if (flags.f.stack_lift_disable) { free_vartype(reg_x); reg_t = reg_y; } else { free_vartype(reg_y); reg_t = reg_x; } reg_z = new_z; reg_y = new_y; reg_x = new_x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_locat(arg_struct *arg) { if (!core_settings.enable_ext_locat) return ERR_NONEXISTENT; double lat, lon, lat_lon_acc, elev, elev_acc; int err = shell_get_location(&lat, &lon, &lat_lon_acc, &elev, &elev_acc); if (err == 0) return ERR_NONEXISTENT; vartype *new_x = new_real(lat); vartype *new_y = new_real(lon); vartype *new_z = new_real(elev); vartype *new_t = new_realmatrix(1, 2); if (new_x == NULL || new_y == NULL || new_z == NULL || new_t == NULL) { free_vartype(new_x); free_vartype(new_y); free_vartype(new_z); free_vartype(new_t); return ERR_INSUFFICIENT_MEMORY; } vartype_realmatrix *rm = (vartype_realmatrix *) new_t; rm->array->data[0] = lat_lon_acc; rm->array->data[1] = elev_acc; free_vartype(reg_t); free_vartype(reg_z); free_vartype(reg_y); free_vartype(reg_x); reg_t = new_t; reg_z = new_z; reg_y = new_y; reg_x = new_x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } int docmd_heading(arg_struct *arg) { if (!core_settings.enable_ext_heading) return ERR_NONEXISTENT; double mag_heading, true_heading, acc, x, y, z; int err = shell_get_heading(&mag_heading, &true_heading, &acc, &x, &y, &z); if (err == 0) return ERR_NONEXISTENT; vartype *new_x = new_real(mag_heading); vartype *new_y = new_real(true_heading); vartype *new_z = new_real(acc); vartype *new_t = new_realmatrix(1, 3); if (new_x == NULL || new_y == NULL || new_z == NULL || new_t == NULL) { free_vartype(new_x); free_vartype(new_y); free_vartype(new_z); free_vartype(new_t); return ERR_INSUFFICIENT_MEMORY; } vartype_realmatrix *rm = (vartype_realmatrix *) new_t; rm->array->data[0] = x; rm->array->data[1] = y; rm->array->data[2] = z; free_vartype(reg_t); free_vartype(reg_z); free_vartype(reg_y); free_vartype(reg_x); reg_t = new_t; reg_z = new_z; reg_y = new_y; reg_x = new_x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); return ERR_NONE; } #endif ///////////////////////////////////////////////// ///// HP-41 Time Module & CX Time emulation ///// ///////////////////////////////////////////////// static int date2comps(phloat x, int4 *yy, int4 *mm, int4 *dd) { int4 m = to_int4(floor(x)); #ifdef BCD_MATH int4 d = to_int4(floor((x - m) * 100)); int4 y = to_int4(x * 1000000) % 10000; #else int4 r = (int4) floor((x - m) * 100000000 + 0.5); r /= 100; int4 d = r / 10000; int4 y = r % 10000; #endif if (mode_time_dmy) { int4 t = m; m = d; d = t; } if (y < 1582 || y > 4320 || m < 1 || m > 12 || d < 1 || d > 31) return ERR_INVALID_DATA; if ((m == 4 || m == 6 || m == 9 || m == 11) && d == 31) return ERR_INVALID_DATA; if (m == 2 && d > ((y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? 29 : 28)) return ERR_INVALID_DATA; if (y == 1582 && (m < 10 || m == 10 && d < 15) || y == 4320 && (m > 9 || m == 9 && d > 10)) return ERR_INVALID_DATA; *yy = y; *mm = m; *dd = d; return ERR_NONE; } static phloat comps2date(int4 y, int4 m, int4 d) { if (mode_time_dmy) { int4 t = m; m = d; d = t; } return phloat(m * 1000000 + d * 10000 + y) / 1000000; } /* Gregorian Date <-> Day Number conversion functions * Algorithm due to Henry F. Fliegel and Thomas C. Van Flandern, * Communications of the ACM, Vol. 11, No. 10 (October, 1968). */ static int greg2jd(int4 y, int4 m, int4 d, int4 *jd) { *jd = ( 1461 * ( y + 4800 + ( m - 14 ) / 12 ) ) / 4 + ( 367 * ( m - 2 - 12 * ( ( m - 14 ) / 12 ) ) ) / 12 - ( 3 * ( ( y + 4900 + ( m - 14 ) / 12 ) / 100 ) ) / 4 + d - 32075; return ERR_NONE; } static int jd2greg(int4 jd, int4 *y, int4 *m, int4 *d) { if (jd < 2299161 || jd > 3299160) return ERR_OUT_OF_RANGE; int4 l = jd + 68569; int4 n = ( 4 * l ) / 146097; l = l - ( 146097 * n + 3 ) / 4; int4 i = ( 4000 * ( l + 1 ) ) / 1461001; l = l - ( 1461 * i ) / 4 + 31; int4 j = ( 80 * l ) / 2447; *d = l - ( 2447 * j ) / 80; l = j / 11; *m = j + 2 - ( 12 * l ); *y = 100 * ( n - 49 ) + i + l; return ERR_NONE; } int docmd_adate(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) reg_x)->x; if (x < 0) x = -x; if (x >= 100) return ERR_INVALID_DATA; int m = to_int(floor(x)); int4 dy = to_int4(floor((x - floor(x)) * 1000000)); int d = (int) (dy / 10000); int c = (int) (dy / 100 % 100); int y = (int) (dy % 100); int digits; if (flags.f.fix_or_all && flags.f.eng_or_all) digits = 11; else { digits = 0; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; } char buf[10]; int bufptr = 0; if (m < 10) char2buf(buf, 10, &bufptr, '0'); bufptr += int2string(m, buf + bufptr, 10 - bufptr); if (digits > 0) { char2buf(buf, 10, &bufptr, mode_time_dmy ? '.' : '/'); if (d < 10) char2buf(buf, 10, &bufptr, '0'); bufptr += int2string(d, buf + bufptr, 10 - bufptr); if (digits > 2) { char2buf(buf, 10, &bufptr, mode_time_dmy ? '.' : '/'); if (digits > 4) { if (c < 10) char2buf(buf, 10, &bufptr, '0'); bufptr += int2string(c, buf + bufptr, 10 - bufptr); } if (y < 10) char2buf(buf, 10, &bufptr, '0'); bufptr += int2string(y, buf + bufptr, 10 - bufptr); } } append_alpha_string(buf, bufptr, 0); return ERR_NONE; } int docmd_atime(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) reg_x)->x; bool neg = x < 0; if (neg) x = -x; if (x >= 100) return ERR_INVALID_DATA; int h = to_int(floor(x)); if (h == 0) neg = false; int4 ms = to_int4(floor((x - floor(x)) * 1000000)); int m = (int) (ms / 10000); int s = (int) (ms / 100 % 100); int cs = (int) (ms % 100); bool am = false; bool pm = false; if (mode_time_clk24) { if (neg && h >= 1 && h <= 11) h += 12; } else if (h < 24) { if (!neg && h < 12) am = true; else pm = true; if (h == 0) h = 12; else if (h > 12) h -= 12; } int digits; if (flags.f.fix_or_all && flags.f.eng_or_all) digits = 11; else { digits = 0; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; } char buf[14]; int bufptr = 0; if (h < 10) char2buf(buf, 14, &bufptr, mode_time_clk24 ? '0' : ' '); bufptr += int2string(h, buf + bufptr, 14 - bufptr); if (digits > 0) { char2buf(buf, 14, &bufptr, ':'); if (m < 10) char2buf(buf, 14, &bufptr, '0'); bufptr += int2string(m, buf + bufptr, 14 - bufptr); if (digits > 2) { char2buf(buf, 14, &bufptr, ':'); if (s < 10) char2buf(buf, 14, &bufptr, '0'); bufptr += int2string(s, buf + bufptr, 14 - bufptr); if (digits > 4) { char2buf(buf, 14, &bufptr, '.'); if (cs < 10) char2buf(buf, 14, &bufptr, '0'); bufptr += int2string(cs, buf + bufptr, 14 - bufptr); } } } if (am) string2buf(buf, 14, &bufptr, " AM", 3); else if (pm) string2buf(buf, 14, &bufptr, " PM", 3); append_alpha_string(buf, bufptr, 0); return ERR_NONE; } int docmd_atime24(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; bool saved_clk24 = mode_time_clk24; mode_time_clk24 = true; int res = docmd_atime(arg); mode_time_clk24 = saved_clk24; return res; } int docmd_clk12(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; mode_time_clk24 = false; return ERR_NONE; } int docmd_clk24(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; mode_time_clk24 = true; return ERR_NONE; } static char weekdaynames[] = "SUNMONTUEWEDTHUFRISAT"; int docmd_date(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; uint4 date; int weekday; shell_get_time_date(NULL, &date, &weekday); int y = date / 10000; int m = date / 100 % 100; int d = date % 100; if (mode_time_dmy) date = y + m * 10000L + d * 1000000; else date = y + m * 1000000 + d * 10000L; vartype *new_x = new_real((int4) date); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; ((vartype_real *) new_x)->x /= 1000000; recall_result(new_x); if (!program_running()) { /* Note: I'm not completely faithful to the HP-41 here. It formats the * date as "14.03.2010 SUN" in DMY mode, and as "03/14/2010:SU" in MDY * mode. I mimic the former, but the latter I changed to * "03/14/2010 SUN"; the MDY display format used on the HP-41 is the * way it is because that was all they could fit in its 12-character * display. (Note that the periods in the DMY format and the colon in * the MDY format don't take up a character position on the HP-41.) */ char buf[22]; int bufptr = 0; int n = mode_time_dmy ? d : m; if (n < 10) char2buf(buf, 22, &bufptr, '0'); bufptr += int2string(n, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, mode_time_dmy ? '.' : '/'); n = mode_time_dmy ? m : d; if (n < 10) char2buf(buf, 22, &bufptr, '0'); bufptr += int2string(n, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, mode_time_dmy ? '.' : '/'); bufptr += int2string(y, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, ' '); string2buf(buf, 22, &bufptr, weekdaynames + weekday * 3, 3); clear_row(0); draw_string(0, 0, buf, bufptr); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; } /* TODO: Trace-mode printing. What should I print, the contents of X, * or, when not in a running program, the nicely formatted date? */ return ERR_NONE; } int docmd_date_plus(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; // TODO: Accept real matrices as well? if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat date = ((vartype_real *) reg_y)->x; if (date < 0 || date > 100) return ERR_INVALID_DATA; phloat days = ((vartype_real *) reg_x)->x; if (days < -1000000 || days > 1000000) return ERR_OUT_OF_RANGE; int4 y, m, d, jd; int err = date2comps(date, &y, &m, &d); if (err != ERR_NONE) return err; err = greg2jd(y, m, d, &jd); if (err != ERR_NONE) return err; jd += to_int4(floor(days)); err = jd2greg(jd, &y, &m, &d); if (err != ERR_NONE) return err; date = comps2date(y, m, d); vartype *new_x = new_real(date); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(new_x); return ERR_NONE; } int docmd_ddays(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; // TODO: Accept real matrices as well? if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; if (reg_y->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_y->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat date1 = ((vartype_real *) reg_y)->x; if (date1 < 0 || date1 > 100) return ERR_INVALID_DATA; phloat date2 = ((vartype_real *) reg_x)->x; if (date2 < 0 || date2 > 100) return ERR_INVALID_DATA; int4 y, m, d, jd1, jd2; int err = date2comps(date1, &y, &m, &d); if (err != ERR_NONE) return err; err = greg2jd(y, m, d, &jd1); if (err != ERR_NONE) return err; err = date2comps(date2, &y, &m, &d); if (err != ERR_NONE) return err; err = greg2jd(y, m, d, &jd2); if (err != ERR_NONE) return err; vartype *new_x = new_real(jd2 - jd1); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; binary_result(new_x); return ERR_NONE; } int docmd_dmy(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; mode_time_dmy = true; return ERR_NONE; } int docmd_dow(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; // TODO: Accept real matrices as well? if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; if (reg_x->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) reg_x)->x; if (x < 0 || x > 100) return ERR_INVALID_DATA; int4 y, m, d, jd; int err = date2comps(x, &y, &m, &d); if (err != ERR_NONE) return err; err = greg2jd(y, m, d, &jd); if (err != ERR_NONE) return err; jd = (jd + 1) % 7; vartype *new_x = new_real(jd); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; unary_result(new_x); if (!program_running()) { clear_row(0); draw_string(0, 0, weekdaynames + jd * 3, 3); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; } return ERR_NONE; } int docmd_mdy(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; mode_time_dmy = false; return ERR_NONE; } int docmd_time(arg_struct *arg) { if (!core_settings.enable_ext_time) return ERR_NONEXISTENT; uint4 time; shell_get_time_date(&time, NULL, NULL); vartype *new_x = new_real((int4) time); if (new_x == NULL) return ERR_INSUFFICIENT_MEMORY; ((vartype_real *) new_x)->x /= 1000000; recall_result(new_x); if (!program_running()) { int h = time / 1000000; bool am; if (!mode_time_clk24) { am = h < 12; h = h % 12; if (h == 0) h = 12; } int m = time / 10000 % 100; int s = time / 100 % 100; char buf[22]; int bufptr = 0; if (h < 10) char2buf(buf, 22, &bufptr, ' '); bufptr += int2string(h, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, ':'); if (m < 10) char2buf(buf, 22, &bufptr, '0'); bufptr += int2string(m, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, ':'); if (s < 10) char2buf(buf, 22, &bufptr, '0'); bufptr += int2string(s, buf + bufptr, 22 - bufptr); if (!mode_time_clk24) { char2buf(buf, 22, &bufptr, ' '); char2buf(buf, 22, &bufptr, am ? 'A' : 'P'); char2buf(buf, 22, &bufptr, 'M'); } clear_row(0); draw_string(0, 0, buf, bufptr); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; } /* TODO: Trace-mode printing. What should I print, the contents of X, * or, when not in a running program, the nicely formatted time? */ return ERR_NONE; } free42-nologo-1.4.77/common/core_commands7.h000644 000765 000024 00000002726 12110237247 021201 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_COMMANDS7_H #define CORE_COMMANDS7_H 1 #include "free42.h" #include "core_globals.h" #if defined(ANDROID) || defined(IPHONE) int docmd_accel(arg_struct *arg); int docmd_locat(arg_struct *arg); int docmd_heading(arg_struct *arg); #endif int docmd_adate(arg_struct *arg); int docmd_atime(arg_struct *arg); int docmd_atime24(arg_struct *arg); int docmd_clk12(arg_struct *arg); int docmd_clk24(arg_struct *arg); int docmd_date(arg_struct *arg); int docmd_date_plus(arg_struct *arg); int docmd_ddays(arg_struct *arg); int docmd_dmy(arg_struct *arg); int docmd_dow(arg_struct *arg); int docmd_mdy(arg_struct *arg); int docmd_time(arg_struct *arg); #endif free42-nologo-1.4.77/common/core_display.cc000644 000765 000024 00000213510 12110237247 021107 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_display.h" #include "core_commands2.h" #include "core_helpers.h" #include "core_main.h" #include "core_tables.h" #include "core_variables.h" #include "shell.h" /********************/ /* HP-42S font data */ /********************/ #if defined(WINDOWS) && !defined(__GNUC__) /* Disable warnings: * 'initializing' : truncation from 'const int ' to 'const char ' * 'initializing' : truncation of constant value */ #pragma warning(push) #pragma warning(disable: 4305) #pragma warning(disable: 4309) #endif static char bigchars[130][5] = { { 0x08, 0x08, 0x2a, 0x08, 0x08 }, { 0x22, 0x14, 0x08, 0x14, 0x22 }, { 0x10, 0x20, 0x7f, 0x01, 0x01 }, { 0x20, 0x40, 0x3e, 0x01, 0x02 }, { 0x55, 0x2a, 0x55, 0x2a, 0x55 }, { 0x41, 0x63, 0x55, 0x49, 0x63 }, { 0x7f, 0x7f, 0x3e, 0x1c, 0x08 }, { 0x04, 0x7c, 0x04, 0x7c, 0x04 }, { 0x30, 0x48, 0x45, 0x40, 0x20 }, { 0x50, 0x58, 0x54, 0x52, 0x51 }, { 0x0f, 0x08, 0x00, 0x78, 0x28 }, { 0x51, 0x52, 0x54, 0x58, 0x50 }, { 0x14, 0x34, 0x1c, 0x16, 0x14 }, { 0x20, 0x70, 0xa8, 0x20, 0x3f }, { 0x10, 0x20, 0x7f, 0x20, 0x10 }, { 0x08, 0x08, 0x2a, 0x1c, 0x08 }, { 0x08, 0x1c, 0x2a, 0x08, 0x08 }, { 0x7e, 0x20, 0x20, 0x1e, 0x20 }, { 0x48, 0x7e, 0x49, 0x41, 0x02 }, { 0x00, 0x0e, 0x0a, 0x0e, 0x00 }, { 0x78, 0x16, 0x15, 0x16, 0x78 }, { 0x7c, 0x0a, 0x11, 0x22, 0x7d }, { 0x7c, 0x13, 0x12, 0x13, 0x7c }, { 0x60, 0x50, 0x58, 0x64, 0x42 }, { 0x3e, 0x2a, 0x2a, 0x22, 0x00 }, { 0x7e, 0x09, 0x7f, 0x49, 0x41 }, { 0x60, 0x00, 0x60, 0x00, 0x60 }, { 0x1f, 0x15, 0x71, 0x50, 0x50 }, { 0x3c, 0x43, 0x42, 0x43, 0x3c }, { 0x3c, 0x41, 0x40, 0x41, 0x3c }, { 0x55, 0x2a, 0x55, 0x2a, 0x55 }, { 0x3c, 0x3c, 0x3c, 0x3c, 0x3c }, { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x5f, 0x00, 0x00 }, { 0x00, 0x07, 0x00, 0x07, 0x00 }, { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, { 0x23, 0x13, 0x08, 0x64, 0x62 }, { 0x36, 0x49, 0x56, 0x20, 0x50 }, { 0x00, 0x00, 0x07, 0x00, 0x00 }, { 0x00, 0x1c, 0x22, 0x41, 0x00 }, { 0x00, 0x41, 0x22, 0x1c, 0x00 }, { 0x08, 0x2a, 0x1c, 0x2a, 0x08 }, { 0x08, 0x08, 0x3e, 0x08, 0x08 }, { 0x00, 0xb0, 0x70, 0x00, 0x00 }, { 0x08, 0x08, 0x08, 0x08, 0x00 }, { 0x00, 0x60, 0x60, 0x00, 0x00 }, { 0x20, 0x10, 0x08, 0x04, 0x02 }, { 0x3e, 0x51, 0x49, 0x45, 0x3e }, { 0x00, 0x42, 0x7f, 0x40, 0x00 }, { 0x62, 0x51, 0x49, 0x49, 0x46 }, { 0x22, 0x49, 0x49, 0x49, 0x36 }, { 0x18, 0x14, 0x12, 0x7f, 0x10 }, { 0x27, 0x45, 0x45, 0x45, 0x39 }, { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, { 0x01, 0x71, 0x09, 0x05, 0x03 }, { 0x36, 0x49, 0x49, 0x49, 0x36 }, { 0x06, 0x49, 0x49, 0x29, 0x1e }, { 0x00, 0x36, 0x36, 0x00, 0x00 }, { 0x00, 0xb6, 0x76, 0x00, 0x00 }, { 0x08, 0x14, 0x22, 0x41, 0x00 }, { 0x14, 0x14, 0x14, 0x14, 0x14 }, { 0x41, 0x22, 0x14, 0x08, 0x00 }, { 0x02, 0x01, 0x51, 0x09, 0x06 }, { 0x3e, 0x41, 0x5d, 0x55, 0x5e }, { 0x7e, 0x09, 0x09, 0x09, 0x7e }, { 0x7f, 0x49, 0x49, 0x49, 0x36 }, { 0x3e, 0x41, 0x41, 0x41, 0x22 }, { 0x7f, 0x41, 0x41, 0x22, 0x1c }, { 0x7f, 0x49, 0x49, 0x49, 0x41 }, { 0x7f, 0x09, 0x09, 0x09, 0x01 }, { 0x3e, 0x41, 0x41, 0x51, 0x72 }, { 0x7f, 0x08, 0x08, 0x08, 0x7f }, { 0x00, 0x41, 0x7f, 0x41, 0x00 }, { 0x30, 0x40, 0x40, 0x40, 0x3f }, { 0x7f, 0x08, 0x14, 0x22, 0x41 }, { 0x7f, 0x40, 0x40, 0x40, 0x40 }, { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, { 0x7f, 0x04, 0x08, 0x10, 0x7f }, { 0x3e, 0x41, 0x41, 0x41, 0x3e }, { 0x7f, 0x09, 0x09, 0x09, 0x06 }, { 0x3e, 0x41, 0x51, 0x21, 0x5e }, { 0x7f, 0x09, 0x19, 0x29, 0x46 }, { 0x26, 0x49, 0x49, 0x49, 0x32 }, { 0x01, 0x01, 0x7f, 0x01, 0x01 }, { 0x3f, 0x40, 0x40, 0x40, 0x3f }, { 0x07, 0x18, 0x60, 0x18, 0x07 }, { 0x7f, 0x20, 0x18, 0x20, 0x7f }, { 0x63, 0x14, 0x08, 0x14, 0x63 }, { 0x03, 0x04, 0x78, 0x04, 0x03 }, { 0x61, 0x51, 0x49, 0x45, 0x43 }, { 0x00, 0x7f, 0x41, 0x41, 0x00 }, { 0x02, 0x04, 0x08, 0x10, 0x20 }, { 0x00, 0x41, 0x41, 0x7f, 0x00 }, { 0x04, 0x02, 0x7f, 0x02, 0x04 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, { 0x00, 0x03, 0x04, 0x00, 0x00 }, { 0x20, 0x54, 0x54, 0x54, 0x78 }, { 0x7f, 0x44, 0x44, 0x44, 0x38 }, { 0x38, 0x44, 0x44, 0x44, 0x44 }, { 0x38, 0x44, 0x44, 0x44, 0x7f }, { 0x38, 0x54, 0x54, 0x54, 0x58 }, { 0x00, 0x08, 0x7e, 0x09, 0x02 }, { 0x18, 0xa4, 0xa4, 0xa4, 0x78 }, { 0x7f, 0x04, 0x04, 0x04, 0x78 }, { 0x00, 0x44, 0x7d, 0x40, 0x00 }, { 0x00, 0x40, 0x80, 0x84, 0x7d }, { 0x7f, 0x10, 0x28, 0x44, 0x00 }, { 0x00, 0x41, 0x7f, 0x40, 0x00 }, { 0x7c, 0x04, 0x38, 0x04, 0x7c }, { 0x7c, 0x04, 0x04, 0x04, 0x78 }, { 0x38, 0x44, 0x44, 0x44, 0x38 }, { 0xfc, 0x24, 0x24, 0x24, 0x18 }, { 0x18, 0x24, 0x24, 0x24, 0xfc }, { 0x7c, 0x08, 0x04, 0x04, 0x04 }, { 0x48, 0x54, 0x54, 0x54, 0x24 }, { 0x00, 0x04, 0x3f, 0x44, 0x20 }, { 0x3c, 0x40, 0x40, 0x40, 0x7c }, { 0x1c, 0x20, 0x40, 0x20, 0x1c }, { 0x3c, 0x40, 0x30, 0x40, 0x3c }, { 0x44, 0x28, 0x10, 0x28, 0x44 }, { 0x1c, 0xa0, 0xa0, 0xa0, 0x7c }, { 0x44, 0x64, 0x54, 0x4c, 0x44 }, { 0x08, 0x36, 0x41, 0x41, 0x00 }, { 0x00, 0x00, 0x7f, 0x00, 0x00 }, { 0x00, 0x41, 0x41, 0x36, 0x08 }, { 0x08, 0x04, 0x08, 0x10, 0x08 }, { 0x7f, 0x08, 0x08, 0x08, 0x08 }, { 0x28, 0x00, 0x00, 0x00, 0x00 }, { 0x04, 0x08, 0x70, 0x08, 0x04 } }; static char smallchars[329] = { 0x00, 0x00, 0x00, 0x5c, 0x06, 0x00, 0x06, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x08, 0x54, 0x7c, 0x54, 0x20, 0x24, 0x10, 0x48, 0x30, 0x4c, 0x50, 0x20, 0x50, 0x08, 0x04, 0x38, 0x44, 0x44, 0x38, 0x54, 0x38, 0x54, 0x10, 0x38, 0x10, 0x40, 0x20, 0x10, 0x10, 0x10, 0x40, 0x60, 0x10, 0x0c, 0x38, 0x44, 0x38, 0x48, 0x7c, 0x40, 0x74, 0x54, 0x5c, 0x44, 0x54, 0x7c, 0x1c, 0x10, 0x7c, 0x5c, 0x54, 0x74, 0x7c, 0x54, 0x74, 0x64, 0x14, 0x0c, 0x7c, 0x54, 0x7c, 0x5c, 0x54, 0x7c, 0x28, 0x40, 0x28, 0x10, 0x28, 0x44, 0x28, 0x28, 0x28, 0x44, 0x28, 0x10, 0x08, 0x04, 0x54, 0x08, 0x38, 0x44, 0x54, 0x58, 0x78, 0x14, 0x78, 0x7c, 0x54, 0x28, 0x38, 0x44, 0x44, 0x7c, 0x44, 0x38, 0x7c, 0x54, 0x44, 0x7c, 0x14, 0x04, 0x7c, 0x44, 0x54, 0x74, 0x7c, 0x10, 0x7c, 0x7c, 0x60, 0x40, 0x7c, 0x7c, 0x10, 0x28, 0x44, 0x7c, 0x40, 0x40, 0x7c, 0x08, 0x10, 0x08, 0x7c, 0x7c, 0x18, 0x30, 0x7c, 0x7c, 0x44, 0x7c, 0x7c, 0x14, 0x1c, 0x38, 0x44, 0x24, 0x58, 0x7c, 0x14, 0x6c, 0x48, 0x54, 0x24, 0x04, 0x7c, 0x04, 0x7c, 0x40, 0x7c, 0x1c, 0x60, 0x1c, 0x7c, 0x20, 0x10, 0x20, 0x7c, 0x6c, 0x10, 0x6c, 0x0c, 0x70, 0x0c, 0x64, 0x54, 0x4c, 0x7c, 0x44, 0x0c, 0x10, 0x60, 0x44, 0x7c, 0x10, 0x08, 0x7c, 0x08, 0x10, 0x40, 0x40, 0x40, 0x04, 0x08, 0x10, 0x6c, 0x44, 0x6c, 0x44, 0x6c, 0x10, 0x10, 0x08, 0x10, 0x20, 0x10, 0x54, 0x28, 0x54, 0x28, 0x54, 0x10, 0x54, 0x10, 0x28, 0x10, 0x28, 0x10, 0x20, 0x7c, 0x04, 0x04, 0x04, 0x20, 0x40, 0x38, 0x04, 0x08, 0x44, 0x6c, 0x54, 0x44, 0x08, 0x78, 0x08, 0x78, 0x08, 0x50, 0x58, 0x54, 0x3c, 0x20, 0x00, 0x78, 0x28, 0x54, 0x58, 0x50, 0x28, 0x68, 0x38, 0x2c, 0x28, 0x10, 0x20, 0x7c, 0x20, 0x10, 0x10, 0x10, 0x54, 0x38, 0x10, 0x10, 0x38, 0x54, 0x10, 0x10, 0x78, 0x20, 0x38, 0x20, 0x1c, 0x14, 0x1c, 0x1c, 0x08, 0x08, 0x60, 0x00, 0x60, 0x00, 0x60, 0x60, 0x50, 0x58, 0x64, 0x40, 0x74, 0x28, 0x28, 0x74, 0x34, 0x48, 0x48, 0x34, 0x34, 0x40, 0x40, 0x34, 0x7c, 0x12, 0x24, 0x7a, 0x50, 0x78, 0x54, 0x04, 0x20, 0x54, 0x40, 0x20, 0x78, 0x14, 0x7c, 0x54, 0x38, 0x38, 0x38, 0x70, 0x2c, 0x70, 0x58, 0x24, 0x54, 0x48 }; static short smallchars_offset[99] = { 0, 3, 4, 7, 12, 17, 20, 25, 27, 29, 31, 34, 37, 39, 42, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 77, 79, 82, 85, 88, 92, 96, 99, 102, 105, 108, 111, 114, 118, 121, 122, 125, 129, 132, 137, 141, 144, 147, 151, 154, 157, 160, 163, 166, 171, 174, 177, 180, 182, 185, 187, 192, 195, 197, 200, 201, 204, 209, 214, 217, 220, 226, 231, 235, 240, 243, 248, 251, 256, 261, 266, 271, 275, 278, 281, 286, 291, 295, 299, 303, 307, 311, 315, 319, 322, 325, 329 }; static char smallchars_map[128] = { /* 0 */ 70, /* 1 */ 71, /* 2 */ 72, /* 3 */ 73, /* 4 */ 69, /* 5 */ 74, /* 6 */ 97, /* 7 */ 75, /* 8 */ 93, /* 9 */ 76, /* 10 */ 77, /* 11 */ 78, /* 12 */ 79, /* 13 */ 0, /* 14 */ 80, /* 15 */ 81, /* 16 */ 82, /* 17 */ 83, /* 18 */ 92, /* 19 */ 84, /* 20 */ 96, /* 21 */ 91, /* 22 */ 88, /* 23 */ 87, /* 24 */ 37, /* 25 */ 94, /* 26 */ 86, /* 27 */ 69, /* 28 */ 89, /* 29 */ 90, /* 30 */ 69, /* 31 */ 95, /* 32 */ 0, /* 33 */ 1, /* 34 */ 2, /* 35 */ 3, /* 36 */ 4, /* 37 */ 5, /* 38 */ 6, /* 39 */ 7, /* 40 */ 8, /* 41 */ 9, /* 42 */ 10, /* 43 */ 11, /* 44 */ 12, /* 45 */ 13, /* 46 */ 14, /* 47 */ 15, /* 48 */ 16, /* 49 */ 17, /* 50 */ 18, /* 51 */ 19, /* 52 */ 20, /* 53 */ 21, /* 54 */ 22, /* 55 */ 23, /* 56 */ 24, /* 57 */ 25, /* 58 */ 26, /* 59 */ 27, /* 60 */ 28, /* 61 */ 29, /* 62 */ 30, /* 63 */ 31, /* 64 */ 32, /* 65 */ 33, /* 66 */ 34, /* 67 */ 35, /* 68 */ 36, /* 69 */ 37, /* 70 */ 38, /* 71 */ 39, /* 72 */ 40, /* 73 */ 41, /* 74 */ 42, /* 75 */ 43, /* 76 */ 44, /* 77 */ 45, /* 78 */ 46, /* 79 */ 47, /* 80 */ 48, /* 81 */ 49, /* 82 */ 50, /* 83 */ 51, /* 84 */ 52, /* 85 */ 53, /* 86 */ 54, /* 87 */ 55, /* 88 */ 56, /* 89 */ 57, /* 90 */ 58, /* 91 */ 59, /* 92 */ 60, /* 93 */ 61, /* 94 */ 62, /* 95 */ 63, /* 96 */ 64, /* 97 */ 33, /* 98 */ 34, /* 99 */ 35, /* 100 */ 36, /* 101 */ 37, /* 102 */ 38, /* 103 */ 39, /* 104 */ 40, /* 105 */ 41, /* 106 */ 42, /* 107 */ 43, /* 108 */ 44, /* 109 */ 45, /* 110 */ 46, /* 111 */ 47, /* 112 */ 48, /* 113 */ 49, /* 114 */ 50, /* 115 */ 51, /* 116 */ 52, /* 117 */ 53, /* 118 */ 54, /* 119 */ 55, /* 120 */ 56, /* 121 */ 57, /* 122 */ 58, /* 123 */ 65, /* 124 */ 66, /* 125 */ 67, /* 126 */ 68, /* 127 */ 85 }; #if defined(WINDOWS) && !defined(__GNUC__) #pragma warning(pop) #endif static char display[272]; static int is_dirty = 0; static int dirty_top, dirty_left, dirty_bottom, dirty_right; static int catalogmenu_section[5]; static int catalogmenu_rows[5]; static int catalogmenu_row[5]; static int catalogmenu_item[5][6]; static int custommenu_length[3][6]; static char custommenu_label[3][6][7]; static arg_struct progmenu_arg[9]; static int progmenu_is_gto[9]; static int progmenu_length[6]; static char progmenu_label[6][7]; static int appmenu_exitcallback; /*******************************/ /* Private function prototypes */ /*******************************/ static void mark_dirty(int top, int left, int bottom, int right); static void fill_rect(int x, int y, int width, int height, int color); static int get_cat_index(); bool persist_display() { if (!shell_write_saved_state(catalogmenu_section, 5 * sizeof(int))) return false; if (!shell_write_saved_state(catalogmenu_rows, 5 * sizeof(int))) return false; if (!shell_write_saved_state(catalogmenu_row, 5 * sizeof(int))) return false; if (!shell_write_saved_state(catalogmenu_item, 30 * sizeof(int))) return false; if (!shell_write_saved_state(custommenu_length, 18 * sizeof(int))) return false; if (!shell_write_saved_state(custommenu_label, 126)) return false; if (!shell_write_saved_state(progmenu_arg, 9 * sizeof(arg_struct))) return false; if (!shell_write_saved_state(progmenu_is_gto, 9 * sizeof(int))) return false; if (!shell_write_saved_state(progmenu_length, 6 * sizeof(int))) return false; if (!shell_write_saved_state(progmenu_label, 42)) return false; if (!shell_write_saved_state(display, 272)) return false; if (!shell_write_saved_state(&appmenu_exitcallback, sizeof(int))) return false; return true; } bool unpersist_display(int version) { int custommenu_cmd[3][6]; is_dirty = 0; if (shell_read_saved_state(catalogmenu_section, 5 * sizeof(int)) != 5 * sizeof(int)) return false; if (shell_read_saved_state(catalogmenu_rows, 5 * sizeof(int)) != 5 * sizeof(int)) return false; if (shell_read_saved_state(catalogmenu_row, 5 * sizeof(int)) != 5 * sizeof(int)) return false; if (shell_read_saved_state(catalogmenu_item, 30 * sizeof(int)) != 30 * sizeof(int)) return false; if (version < 7) { /* In version 7, I removed the special handling * of FCN catalog assignments (after discovering how * the real HP-42S does it and realizing that, for perfect * compatibility, I had to do it the same way). */ if (shell_read_saved_state(custommenu_cmd, 18 * sizeof(int)) != 18 * sizeof(int)) return false; } if (shell_read_saved_state(custommenu_length, 18 * sizeof(int)) != 18 * sizeof(int)) return false; if (shell_read_saved_state(custommenu_label, 126) != 126) return false; if (version < 7) { /* Starting with version 7, FCN catalog assignments are no longer * handled specially; instead, commands with shortened key labels * (e.g. ENTER/ENTR, ASSIGN/ASGN) are handled by using "meta" * characters to indicate the disappearing positions. This is how * the HP-42S does it, and since this can even affect programs * (e.g. create the line STO "ENTER" by selecting ENTER from the * function catalog, and the second E is encoded as meta-E in the * program!), we have to do it the same way, for compatibility. * I think my original approach was better, but such is life. :-) */ int row, pos; for (row = 0; row < 3; row++) for (pos = 0; pos < 6; pos++) { int cmd = custommenu_cmd[row][pos]; if (cmd != CMD_NONE) { const command_spec *cs = cmdlist(cmd); string_copy(custommenu_label[row][pos], &custommenu_length[row][pos], cs->name, cs->name_length); } } } for (int i = 0; i < 9; i++) if (!read_arg(progmenu_arg + i, version < 9)) return false; if (shell_read_saved_state(progmenu_is_gto, 9 * sizeof(int)) != 9 * sizeof(int)) return false; if (shell_read_saved_state(progmenu_length, 6 * sizeof(int)) != 6 * sizeof(int)) return false; if (shell_read_saved_state(progmenu_label, 42) != 42) return false; if (shell_read_saved_state(display, 272) != 272) return false; if (shell_read_saved_state(&appmenu_exitcallback, sizeof(int)) != sizeof(int)) return false; return true; } void clear_display() { int i; for (i = 0; i < 272; i++) display[i] = 0; mark_dirty(0, 0, 16, 131); } void flush_display() { if (!is_dirty) return; shell_blitter(display, 17, dirty_left, dirty_top, dirty_right - dirty_left, dirty_bottom - dirty_top); is_dirty = 0; } void repaint_display() { shell_blitter(display, 17, 0, 0, 131, 16); } void draw_pixel(int x, int y) { display[y * 17 + (x >> 3)] |= 1 << (x & 7); mark_dirty(y, x, y + 1, x + 1); } void draw_pattern(phloat dx, phloat dy, const char *pattern, int pattern_width){ int x, y, h, v, hmin, hmax, vmin, vmax; if (dx + pattern_width < 1 || dx > 131 || dy + 8 < 1 || dy > 16) return; x = dx < 0 ? to_int(-floor(-dx + 0.5)) : to_int(floor(dx + 0.5)); y = dy < 0 ? to_int(-floor(-dy + 0.5)) : to_int(floor(dy + 0.5)); hmin = x < 1 ? 1 - x : 0; hmax = x + pattern_width > 132 ? 132 - x : pattern_width; vmin = y < 1 ? 1 - y : 0; vmax = y + 8 > 17 ? 17 - y : 8; x--; y--; if (flags.f.agraph_control1) { if (flags.f.agraph_control0) /* dst = dst ^ src */ for (h = hmin; h < hmax; h++) { char c = pattern[h] >> vmin; for (v = vmin; v < vmax; v++) { if (c & 1) { int X = h + x; int Y = v + y; display[Y * 17 + (X >> 3)] ^= 1 << (X & 7); } c >>= 1; } } else /* dst = dst & ~src */ for (h = hmin; h < hmax; h++) { char c = pattern[h] >> vmin; for (v = vmin; v < vmax; v++) { if (c & 1) { int X = h + x; int Y = v + y; display[Y * 17 + (X >> 3)] &= ~(1 << (X & 7)); } c >>= 1; } } } else { if (flags.f.agraph_control0) /* dst = src */ for (h = hmin; h < hmax; h++) { char c = pattern[h] >> vmin; for (v = vmin; v < vmax; v++) { int X = h + x; int Y = v + y; if (c & 1) display[Y * 17 + (X >> 3)] |= 1 << (X & 7); else display[Y * 17 + (X >> 3)] &= ~(1 << (X & 7)); c >>= 1; } } else /* dst = dst | src */ for (h = hmin; h < hmax; h++) { char c = pattern[h] >> vmin; for (v = vmin; v < vmax; v++) { if (c & 1) { int X = h + x; int Y = v + y; display[Y * 17 + (X >> 3)] |= 1 << (X & 7); } c >>= 1; } } } mark_dirty(y + vmin, x + hmin, y + vmax, x + hmax); } void fly_goose() { static uint4 lastgoosetime = 0; uint4 goosetime = shell_milliseconds(); if (goosetime - 100 < lastgoosetime) /* No goose movements if the most recent one was less than 100 ms * ago; in other words, maximum goose speed is 10 positions/second */ return; lastgoosetime = goosetime; if (mode_goose < 0) { clear_row(0); mode_goose = (-mode_goose) % 22; draw_char(mode_goose, 0, 6); } else { draw_char(mode_goose, 0, ' '); mode_goose = (mode_goose + 1) % 22; draw_char(mode_goose, 0, 6); } flush_display(); } void squeak() { if (flags.f.audio_enable) shell_beeper(1835, 125); } void tone(int n) { if (flags.f.audio_enable) { int frequency; switch (n) { case 0: frequency = 164; break; case 1: frequency = 220; break; case 2: frequency = 243; break; case 3: frequency = 275; break; case 4: frequency = 293; break; case 5: frequency = 324; break; case 6: frequency = 366; break; case 7: frequency = 418; break; case 8: frequency = 438; break; case 9: frequency = 550; break; default: return; } shell_beeper(frequency, 250); } } static void mark_dirty(int top, int left, int bottom, int right) { if (is_dirty) { if (top < dirty_top) dirty_top = top; if (left < dirty_left) dirty_left = left; if (bottom > dirty_bottom) dirty_bottom = bottom; if (right > dirty_right) dirty_right = right; } else { dirty_top = top; dirty_left = left; dirty_bottom = bottom; dirty_right = right; is_dirty = 1; } } void draw_char(int x, int y, char c) { int X, Y, h, v; unsigned char uc = (unsigned char) c; if (x < 0 || x >= 22 || y < 0 || y >= 2) return; if (uc >= 130) uc -= 128; X = x * 6; Y = y * 8; for (v = 0; v < 8; v++) { int YY = Y + v; for (h = 0; h < 5; h++) { int XX = X + h; char mask = 1 << (XX & 7); if (bigchars[uc][h] & (1 << v)) display[YY * 17 + (XX >> 3)] |= mask; else display[YY * 17 + (XX >> 3)] &= ~mask; } } mark_dirty(Y, X, Y + 8, X + 5); } void get_char(char *bits, char c) { /* TODO - it should be possible to simply return bigchars[c], * but that crashes on the Palm. Looks like a compiler bug. */ int i; unsigned char uc = (unsigned char) c; if (uc >= 130) uc -= 128; for (i = 0; i < 5; i++) bits[i] = bigchars[uc][i]; } void draw_string(int x, int y, const char *s, int length) { while (length != 0 && x < 22) { draw_char(x++, y, *s++); length--; } } static void fill_rect(int x, int y, int width, int height, int color) { int h, v; for (v = y; v < y + height; v++) for (h = x; h < x + width; h++) if (color) display[v * 17 + (h >> 3)] |= 1 << (h & 7); else display[v * 17 + (h >> 3)] &= ~(1 << (h & 7)); mark_dirty(y, x, y + height, x + width); } static void draw_key(int n, int highlight, int hide_meta, const char *s, int length) { int swidth = 0; int len = 0; int len2; int x, i; int fatdot = 31; /* Note: the SST handling code uses a magic value of 2 in prgm_mode * so that we know *not* to suppress menu highlights while stepping. */ if (flags.f.prgm_mode == 1) highlight = 0; if (highlight) { int f = smallchars_map[fatdot]; swidth = smallchars_offset[f + 1] - smallchars_offset[f]; } while (len < length) { int c, m, cw; c = (unsigned char) s[len++]; if (hide_meta && c >= 128) continue; c &= 127; m = smallchars_map[c]; cw = smallchars_offset[m + 1] - smallchars_offset[m]; if (swidth != 0) cw++; if (swidth + cw > 19) { len--; break; } swidth += cw; } fill_rect(n * 22, 9, 21, 7, 1); x = n * 22 + 10 - swidth / 2; len2 = highlight ? len + 1 : len; for (i = 0; i < len2; i++) { int c, m, o, cw, j; if (i == len) c = fatdot; else c = (unsigned char) s[i]; if (hide_meta && c >= 128) continue; c &= 127; m = smallchars_map[c]; o = smallchars_offset[m]; cw = smallchars_offset[m + 1] - o; for (j = 0; j < cw; j++) { int b, k; b = smallchars[o + j]; for (k = 0; k < 8; k++) if ((b >> k) & 1) display[(k + 8) * 17 + (x >> 3)] &= ~(1 << (x & 7)); x++; } x++; } /* No need for mark_dirty(); fill_rect() took care of that already. */ } void clear_row(int row) { fill_rect(0, row * 8, 131, 8, 0); } static int prgmline2buf(char *buf, int len, int4 line, int highlight, int cmd, arg_struct *arg) { int bufptr = 0; if (line != -1) { if (line < 10) char2buf(buf, len, &bufptr, '0'); bufptr += int2string(line, buf + bufptr, len - bufptr); if (highlight) char2buf(buf, len, &bufptr, 6); else char2buf(buf, len, &bufptr, ' '); } if (line == 0) { int4 size = core_program_size(current_prgm); string2buf(buf, len, &bufptr, "{ ", 2); bufptr += int2string(size, buf + bufptr, len - bufptr); string2buf(buf, len, &bufptr, "-Byte Prgm }", 12); } else if (flags.f.alpha_mode && mode_alpha_entry && highlight) { int append = entered_string_length > 0 && entered_string[0] == 127; if (append) { string2buf(buf, len, &bufptr, "\177\"", 2); string2buf(buf, len, &bufptr, entered_string + 1, entered_string_length - 1); } else { char2buf(buf, len, &bufptr, '"'); string2buf(buf, len, &bufptr, entered_string, entered_string_length); } char2buf(buf, len, &bufptr, '_'); } else if (cmd == CMD_END && current_prgm == prgms_count - 1) { string2buf(buf, len, &bufptr, ".END.", 5); } else if (cmd == CMD_NUMBER) { char *num = phloat2program(arg->val_d); char c; while ((c = *num++) != 0 && bufptr < len) buf[bufptr++] = c; if (c != 0) buf[bufptr - 1] = 26; } else if (cmd == CMD_STRING) { int append = arg->length > 0 && arg->val.text[0] == 127; if (append) char2buf(buf, len, &bufptr, 127); char2buf(buf, len, &bufptr, '"'); string2buf(buf, len, &bufptr, arg->val.text + append, arg->length - append); char2buf(buf, len, &bufptr, '"'); } else bufptr += command2buf(buf + bufptr, len - bufptr, cmd, arg); return bufptr; } void display_prgm_line(int row, int line_offset) { int4 tmppc = pc; int4 tmpline = pc2line(pc); int cmd; arg_struct arg; char buf[44]; int bufptr; int len = 22; if (row == -1) /* This means use both lines; used by SHOW */ len = 44; if (tmpline != 0) { if (line_offset == 0) get_next_command(&tmppc, &cmd, &arg, 0); else if (line_offset == 1) { tmppc += get_command_length(current_prgm, tmppc); tmpline++; get_next_command(&tmppc, &cmd, &arg, 0); } else /* line_offset == -1 */ { tmpline--; if (tmpline != 0) { tmppc = line2pc(tmpline); get_next_command(&tmppc, &cmd, &arg, 0); } } } else { if (line_offset == 0) { /* Nothing to do */ } else if (line_offset == 1) { tmppc = 0; get_next_command(&tmppc, &cmd, &arg, 0); tmpline++; } /* Should not get offset == -1 when at line 0! */ } bufptr = prgmline2buf(buf, len, tmpline, line_offset == 0, cmd, &arg); if (row == -1) { clear_display(); if (bufptr <= 22) draw_string(0, 0, buf, bufptr); else { draw_string(0, 0, buf, 22); draw_string(0, 1, buf + 22, bufptr - 22); } } else { clear_row(row); draw_string(0, row, buf, bufptr); } } void display_x(int row) { char buf[22]; int bufptr = 0; clear_row(row); if (matedit_mode == 2 || matedit_mode == 3) { bufptr += int2string(matedit_i + 1, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, ':'); bufptr += int2string(matedit_j + 1, buf + bufptr, 22 - bufptr); char2buf(buf, 22, &bufptr, '='); } else if (input_length > 0) { string2buf(buf, 22, &bufptr, input_name, input_length); char2buf(buf, 22, &bufptr, '?'); } else { string2buf(buf, 22, &bufptr, "x\200", 2); } bufptr += vartype2string(reg_x, buf + bufptr, 22 - bufptr); draw_string(0, row, buf, bufptr); } void display_y(int row) { char buf[20]; int len; clear_row(row); len = vartype2string(reg_y, buf, 20); draw_string(0, row, "\201\200", 2); if (len > 20) { draw_string(2, row, buf, 19); draw_char(21, row, 26); } else draw_string(2, row, buf, len); } void display_incomplete_command(int row) { char buf[40]; int bufptr = 0; const command_spec *cmd = cmdlist(incomplete_command); if (flags.f.prgm_mode && (cmd->flags & FLAG_IMMED) == 0) { int line = pc2line(pc); if (line < 10) char2buf(buf, 40, &bufptr, '0'); bufptr += int2string(line, buf + bufptr, 40 - bufptr); char2buf(buf, 40, &bufptr, 6); } if (incomplete_command == CMD_ASSIGNb) { string2buf(buf, 40, &bufptr, "ASSIGN \"", 8); string2buf(buf, 40, &bufptr, pending_command_arg.val.text, pending_command_arg.length); string2buf(buf, 40, &bufptr, "\" TO _", 6); goto done; } if (incomplete_argtype == ARG_MKEY) { /* KEYG/KEYX */ string2buf(buf, 40, &bufptr, "KEY _", 5); goto done; } if (incomplete_command == CMD_SIMQ) string2buf(buf, 40, &bufptr, "Number of Unknowns ", 19); else { string2buf(buf, 40, &bufptr, cmd->name, cmd->name_length); char2buf(buf, 40, &bufptr, ' '); } if (incomplete_ind) string2buf(buf, 40, &bufptr, "IND ", 4); if (incomplete_alpha) { char2buf(buf, 40, &bufptr, '"'); string2buf(buf, 40, &bufptr, incomplete_str, incomplete_length); char2buf(buf, 40, &bufptr, '_'); } else { int d = 1; int i; for (i = 0; i < incomplete_length - 1; i++) d *= 10; for (i = 0; i < incomplete_maxdigits; i++) { if (i < incomplete_length) { char2buf(buf, 40, &bufptr, (char) ('0' + (incomplete_num / d) % 10)); d /= 10; } else char2buf(buf, 40, &bufptr, '_'); } } done: clear_row(row); if (bufptr <= 22) draw_string(0, row, buf, bufptr); else { draw_char(0, row, 26); draw_string(1, row, buf + (bufptr - 21), 21); } } void display_error(int error, int print) { clear_row(0); draw_string(0, 0, errors[error].text, errors[error].length); flags.f.message = 1; flags.f.two_line_message = 0; if (print && (flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) print_text(errors[error].text, errors[error].length, 1); } void display_command(int row) { char buf[22]; int bufptr = 0; const command_spec *cmd = cmdlist(pending_command); int *the_menu; int catsect; int hide = pending_command == CMD_VMEXEC || (pending_command == CMD_XEQ && xeq_invisible && (the_menu = get_front_menu()) != NULL && *the_menu == MENU_CATALOG && ((catsect = get_cat_section()) == CATSECT_PGM || catsect == CATSECT_PGM_ONLY)); if (pending_command >= CMD_ASGN01 && pending_command <= CMD_ASGN18) string2buf(buf, 22, &bufptr, "ASSIGN ", 7); else if (!hide) { if (pending_command == CMD_SIMQ) string2buf(buf, 22, &bufptr, "Number of Unknowns ", 19); else { string2buf(buf, 22, &bufptr, cmd->name, cmd->name_length); char2buf(buf, 22, &bufptr, ' '); } } if (cmd->argtype == ARG_NONE) goto done; if (pending_command_arg.type == ARGTYPE_IND_NUM || pending_command_arg.type == ARGTYPE_IND_STK || pending_command_arg.type == ARGTYPE_IND_STR) string2buf(buf, 22, &bufptr, "IND ", 4); if (pending_command_arg.type == ARGTYPE_NUM || pending_command_arg.type == ARGTYPE_IND_NUM) { int d = 1, i; int leadingzero = 1; for (i = 0; i < pending_command_arg.length - 1; i++) d *= 10; for (i = 0; i < pending_command_arg.length; i++) { int digit = (pending_command_arg.val.num / d) % 10; if (digit != 0 || i >= pending_command_arg.length - 2) leadingzero = 0; if (!leadingzero) char2buf(buf, 22, &bufptr, (char) ('0' + digit)); d /= 10; } } else if (pending_command_arg.type == ARGTYPE_STK || pending_command_arg.type == ARGTYPE_IND_STK) { string2buf(buf, 22, &bufptr, "ST ", 3); char2buf(buf, 22, &bufptr, pending_command_arg.val.stk); } else if (pending_command_arg.type == ARGTYPE_STR || pending_command_arg.type == ARGTYPE_IND_STR) { char2buf(buf, 22, &bufptr, '"'); string2buf(buf, 22, &bufptr, pending_command_arg.val.text, pending_command_arg.length); char2buf(buf, 22, &bufptr, '"'); } else if (pending_command_arg.type == ARGTYPE_LBLINDEX) { int labelindex = pending_command_arg.val.num; if (labels[labelindex].length == 0) if (labelindex == labels_count - 1) string2buf(buf, 22, &bufptr, ".END.", 5); else string2buf(buf, 22, &bufptr, "END", 3); else { char2buf(buf, 22, &bufptr, '"'); string2buf(buf, 22, &bufptr, labels[labelindex].name, labels[labelindex].length); char2buf(buf, 22, &bufptr, '"'); } } else /* ARGTYPE_LCLBL */ { char2buf(buf, 22, &bufptr, pending_command_arg.val.lclbl); } if (pending_command >= CMD_ASGN01 && pending_command <= CMD_ASGN18) { int keynum = pending_command - CMD_ASGN01 + 1; string2buf(buf, 22, &bufptr, " TO ", 4); char2buf(buf, 22, &bufptr, (char) ('0' + keynum / 10)); char2buf(buf, 22, &bufptr, (char) ('0' + keynum % 10)); } done: clear_row(row); draw_string(0, row, buf, bufptr); } static int set_appmenu(int menuid) { if (mode_appmenu != MENU_NONE && appmenu_exitcallback != 0) { /* We delegate the set_menu() call to the callback, * but only once. If the callback wants to stay active, * it will have to call set_appmenu_callback() itself * to reinstate itself. */ int cb = appmenu_exitcallback; appmenu_exitcallback = 0; /* NOTE: I don't use a 'traditional' callback pointer * because appmenu_exitcallback has to be persistable, * and pointers to code do not have that property. */ switch (cb) { case 1: return appmenu_exitcallback_1(menuid); case 2: return appmenu_exitcallback_2(menuid); case 3: return appmenu_exitcallback_3(menuid); case 4: return appmenu_exitcallback_4(menuid); case 5: return appmenu_exitcallback_5(menuid); default: return ERR_INTERNAL_ERROR; } } else { mode_appmenu = menuid; appmenu_exitcallback = 0; return ERR_NONE; } } void draw_varmenu() { arg_struct arg; int saved_prgm, prgm; int4 pc, pc2; int command, i, row, key; int num_mvars = 0; if (mode_appmenu != MENU_VARMENU) return; arg.type = ARGTYPE_STR; arg.length = varmenu_length; for (i = 0; i < arg.length; i++) arg.val.text[i] = varmenu[i]; if (!find_global_label(&arg, &prgm, &pc)) { set_appmenu(MENU_NONE); varmenu_length = 0; return; } saved_prgm = current_prgm; current_prgm = prgm; pc += get_command_length(prgm, pc); pc2 = pc; while (get_next_command(&pc, &command, &arg, 0), command == CMD_MVAR) num_mvars++; if (num_mvars == 0) { current_prgm = saved_prgm; set_appmenu(MENU_NONE); varmenu_length = 0; return; } varmenu_rows = (num_mvars + 5) / 6; if (varmenu_row >= varmenu_rows) varmenu_row = varmenu_rows - 1; row = 0; key = 0; while (get_next_command(&pc2, &command, &arg, 0), command == CMD_MVAR) { if (row == varmenu_row) { varmenu_labellength[key] = arg.length; for (i = 0; i < arg.length; i++) varmenu_labeltext[key][i] = arg.val.text[i]; draw_key(key, 0, 0, arg.val.text, arg.length); } if (key++ == 5) { if (row++ == varmenu_row) break; else key = 0; } } current_prgm = saved_prgm; while (key < 6) { varmenu_labellength[key] = 0; draw_key(key, 0, 0, "", 0); key++; } } static int fcn_cat[] = { CMD_ABS, CMD_ACOS, CMD_ACOSH, CMD_ADV, CMD_AGRAPH, CMD_AIP, CMD_ALENG, CMD_ALL, CMD_ALLSIGMA, CMD_AND, CMD_AOFF, CMD_AON, CMD_ARCL, CMD_AROT, CMD_ASHF, CMD_ASIN, CMD_ASINH, CMD_ASSIGNa, CMD_ASTO, CMD_ATAN, CMD_ATANH, CMD_ATOX, CMD_AVIEW, CMD_BASEADD, CMD_BASESUB, CMD_BASEMUL, CMD_BASEDIV, CMD_BASECHS, CMD_BEEP, CMD_BEST, CMD_BINM, CMD_BIT_T, CMD_BST, CMD_CF, CMD_CLA, CMD_CLALLa, CMD_CLD, CMD_CLKEYS, CMD_CLLCD, CMD_CLMENU, CMD_CLP, CMD_CLRG, CMD_CLST, CMD_CLV, CMD_CLX, CMD_CLSIGMA, CMD_COMB, CMD_COMPLEX, CMD_CORR, CMD_COS, CMD_COSH, CMD_CPXRES, CMD_CPX_T, CMD_CROSS, CMD_CUSTOM, CMD_DECM, CMD_DEG, CMD_DEL, CMD_DELAY, CMD_DELR, CMD_DET, CMD_DIM, CMD_DIM_T, CMD_DOT, CMD_DSE, CMD_EDIT, CMD_EDITN, CMD_END, CMD_ENG, CMD_ENTER, CMD_EXITALL, CMD_EXPF, CMD_E_POW_X, CMD_E_POW_X_1, CMD_FC_T, CMD_FCC_T, CMD_FCSTX, CMD_FCSTY, CMD_FIX, CMD_FNRM, CMD_FP, CMD_FS_T, CMD_FSC_T, CMD_GAMMA, CMD_GETKEY, CMD_GETM, CMD_GRAD, CMD_GROW, CMD_GTO, CMD_HEXM, CMD_HMSADD, CMD_HMSSUB, CMD_I_ADD, CMD_I_SUB, CMD_INDEX, CMD_INPUT, CMD_INSR, CMD_INTEG, CMD_INVRT, CMD_IP, CMD_ISG, CMD_J_ADD, CMD_J_SUB, CMD_KEYASN, CMD_KEYG, CMD_KEYX, CMD_LASTX, CMD_LBL, CMD_LCLBL, CMD_LINF, CMD_LINSIGMA, CMD_LIST, CMD_LN, CMD_LN_1_X, CMD_LOG, CMD_LOGF, CMD_MAN, CMD_MAT_T, CMD_MEAN, CMD_MENU, CMD_MOD, CMD_MVAR, CMD_FACT, CMD_NEWMAT, CMD_NORM, CMD_NOT, CMD_OCTM, CMD_OFF, CMD_OLD, CMD_ON, CMD_OR, CMD_PERM, CMD_PGMINT, CMD_PGMSLV, CMD_PI, CMD_PIXEL, CMD_POLAR, CMD_POSA, CMD_PRA, CMD_PRLCD, CMD_POFF, CMD_PROMPT, CMD_PON, CMD_PRP, CMD_PRSTK, CMD_PRUSR, CMD_PRV, CMD_PRX, CMD_PRSIGMA, CMD_PSE, CMD_PUTM, CMD_PWRF, CMD_QUIET, CMD_RAD, CMD_RAN, CMD_RCL, CMD_RCL_ADD, CMD_RCL_SUB, CMD_RCL_MUL, CMD_RCL_DIV, CMD_RCLEL, CMD_RCLIJ, CMD_RDXCOMMA, CMD_RDXDOT, CMD_REALRES, CMD_REAL_T, CMD_RECT, CMD_RND, CMD_RNRM, CMD_ROTXY, CMD_RSUM, CMD_RTN, CMD_SWAP_R, CMD_RUP, CMD_RDN, CMD_SCI, CMD_SDEV, CMD_SEED, CMD_SF, CMD_SIGN, CMD_SIN, CMD_SINH, CMD_SIZE, CMD_SLOPE, CMD_SOLVE, CMD_SQRT, CMD_SST, CMD_STO, CMD_STO_ADD, CMD_STO_SUB, CMD_STO_MUL, CMD_STO_DIV, CMD_STOEL, CMD_STOIJ, CMD_STOP, CMD_STR_T, CMD_SUM, CMD_TAN, CMD_TANH, CMD_TONE, CMD_TRACE, CMD_TRANS, CMD_UVEC, CMD_VARMENU, CMD_VIEW, CMD_WMEAN, CMD_WRAP, CMD_X_SWAP, CMD_SWAP, CMD_X_LT_0, CMD_X_LT_Y, CMD_X_LE_0, CMD_X_LE_Y, CMD_X_EQ_0, CMD_X_EQ_Y, CMD_X_NE_0, CMD_X_NE_Y, CMD_X_GT_0, CMD_X_GT_Y, CMD_X_GE_0, CMD_X_GE_Y, CMD_XEQ, CMD_XOR, CMD_XTOA, CMD_SQUARE, CMD_YINT, CMD_Y_POW_X, CMD_INV, CMD_10_POW_X, CMD_ADD, CMD_SUB, CMD_MUL, CMD_DIV, CMD_CHS, CMD_SIGMAADD, CMD_SIGMASUB, CMD_SIGMAREG, CMD_SIGMAREG_T, CMD_TO_DEC, CMD_TO_DEG, CMD_TO_HMS, CMD_TO_HR, CMD_TO_OCT, CMD_TO_POL, CMD_TO_RAD, CMD_TO_REC, CMD_LEFT, CMD_UP, CMD_DOWN, CMD_RIGHT, CMD_PERCENT, CMD_PERCENT_CH }; typedef struct { int first_cmd; int last_cmd; bool *enable_flag; } extension_struct; static extension_struct extensions[] = { { CMD_OPENF, CMD_DELP, &core_settings.enable_ext_copan }, { CMD_DROP, CMD_DROP, &core_settings.enable_ext_bigstack }, { CMD_ACCEL, CMD_ACCEL, &core_settings.enable_ext_accel }, { CMD_LOCAT, CMD_LOCAT, &core_settings.enable_ext_locat }, { CMD_HEADING, CMD_HEADING, &core_settings.enable_ext_heading }, { CMD_ADATE, CMD_SWPT, &core_settings.enable_ext_time }, { CMD_NULL, CMD_NULL, NULL } }; static void draw_catalog() { int catsect = get_cat_section(); int catindex = get_cat_index(); if (catsect == CATSECT_TOP) { draw_top: draw_key(0, 0, 0, "FCN", 3); draw_key(1, 0, 0, "PGM", 3); draw_key(2, 0, 0, "REAL", 4); draw_key(3, 0, 0, "CPX", 3); draw_key(4, 0, 0, "MAT", 3); draw_key(5, 0, 0, "MEM", 3); mode_updown = false; shell_annunciators(0, -1, -1, -1, -1, -1); } else if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY || catsect == CATSECT_PGM_SOLVE || catsect == CATSECT_PGM_INTEG) { /* Show menu of alpha labels */ int lcount = 0; int i, j, k = -1; if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY) { int lastprgm = -1; for (i = 0; i < labels_count; i++) { if (labels[i].length > 0 || labels[i].prgm != lastprgm) lcount++; lastprgm = labels[i].prgm; } } else /* CATSECT_PGM_SOLVE, CATSECT_PGM_INTEG */ { for (i = 0; i < labels_count; i++) if (label_has_mvar(i)) lcount++; } catalogmenu_rows[catindex] = (lcount + 5) / 6; if (catalogmenu_row[catindex] >= catalogmenu_rows[catindex]) catalogmenu_row[catindex] = catalogmenu_rows[catindex] - 1; j = -1; for (i = labels_count - 1; i >= 0; i--) { int show_this_label; if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY) { show_this_label = labels[i].length > 0 || i == 0 || labels[i - 1].prgm != labels[i].prgm; } else { show_this_label = label_has_mvar(i); } if (show_this_label) { j++; if (j / 6 == catalogmenu_row[catindex]) { int len = labels[i].length; k = j % 6; if (len == 0) { if (i == labels_count - 1) draw_key(k, 0, 0, ".END.", 5); else draw_key(k, 0, 0, "END", 3); } else draw_key(k, 0, 0, labels[i].name, labels[i].length); catalogmenu_item[catindex][k] = i; if (k == 5) break; } } } while (k < 5) { draw_key(++k, 0, 0, "", 0); catalogmenu_item[catindex][k] = -1; } mode_updown = catalogmenu_rows[catindex] > 1; shell_annunciators(mode_updown, -1, -1, -1, -1, -1); } else if (catsect == CATSECT_FCN) { int desired_row = catalogmenu_row[catindex]; if (desired_row < 42) { standard_fcn: for (int i = 0; i < 6; i++) { int cmd = fcn_cat[desired_row * 6 + i]; catalogmenu_item[catindex][i] = cmd; draw_key(i, 0, 1, cmdlist(cmd)->name, cmdlist(cmd)->name_length); } // Setting number of rows to a ridiculously large value; // this value only comes into play when the user presses "up" // while on the first row of the FCN catalog, and the extension-handling // code in the desired_row >= 42 case takes care of handling the // ever-changing number of rows in this menu, and will clip the row // number to the actual number of rows. catalogmenu_rows[catindex] = 1000; } else { int curr_row = 41; int curr_pos = 5; bool menu_full = false; int menu_length = 0; bool no_extensions = true; for (int extno = 0; extensions[extno].first_cmd != CMD_NULL; extno++) { if (extensions[extno].enable_flag == NULL || *extensions[extno].enable_flag) { for (int cmd = extensions[extno].first_cmd; cmd <= extensions[extno].last_cmd; cmd++) { if ((cmdlist(cmd)->flags & FLAG_HIDDEN) == 0) { no_extensions = false; if (curr_pos == 5) { curr_pos = 0; curr_row++; } else curr_pos++; if (menu_full) goto done; catalogmenu_item[catindex][curr_pos] = cmd; catalogmenu_row[catindex] = curr_row; menu_length = curr_pos + 1; if (curr_pos == 5 && curr_row == desired_row) menu_full = true; } } } } if (no_extensions) { desired_row = catalogmenu_row[catindex] = desired_row == 42 ? 0 : 41; goto standard_fcn; } done: catalogmenu_rows[catindex] = curr_row + 1; for (int i = 0; i < 6; i++) { if (i >= menu_length) catalogmenu_item[catindex][i] = -1; int cmd = catalogmenu_item[catindex][i]; if (cmd == -1) draw_key(i, 0, 0, "", 0); else draw_key(i, 0, 1, cmdlist(cmd)->name, cmdlist(cmd)->name_length); } } mode_updown = true; shell_annunciators(1, -1, -1, -1, -1, -1); } else { int vcount = 0; int i, j, k = -1; int show_real = 1; int show_cpx = 1; int show_mat = 1; switch (catsect) { case CATSECT_REAL: case CATSECT_REAL_ONLY: show_cpx = show_mat = 0; break; case CATSECT_CPX: show_real = show_mat = 0; break; case CATSECT_MAT: case CATSECT_MAT_ONLY: show_real = show_cpx = 0; break; } for (i = 0; i < vars_count; i++) { int type = vars[i].value->type; switch (type) { case TYPE_REAL: case TYPE_STRING: if (show_real) vcount++; break; case TYPE_COMPLEX: if (show_cpx) vcount++; break; case TYPE_REALMATRIX: case TYPE_COMPLEXMATRIX: if (show_mat) vcount++; break; } } if (vcount == 0) { /* We should only get here if the 'plainmenu' catalog is * in operation; the other catalogs only operate during * command entry mode, or are label catalogs -- so in those * cases, it is possible to prevent empty catalogs from * being displayed *in advance* (i.e., check if any real * variables exist before enabling MENU_CATALOG with * catalogmenu_section = CATSECT_REAL, etc.). * When a catalog becomes empty while displayed, we move * to the top level silently. The "No XXX Variables" message * is only displayed if the user actively tries to enter * an empty catalog section. */ set_cat_section(CATSECT_TOP); goto draw_top; } catalogmenu_rows[catindex] = (vcount + 5) / 6; if (catalogmenu_row[catindex] >= catalogmenu_rows[catindex]) catalogmenu_row[catindex] = catalogmenu_rows[catindex] - 1; j = -1; for (i = vars_count - 1; i >= 0; i--) { int type = vars[i].value->type; switch (type) { case TYPE_REAL: case TYPE_STRING: if (show_real) break; else continue; case TYPE_COMPLEX: if (show_cpx) break; else continue; case TYPE_REALMATRIX: case TYPE_COMPLEXMATRIX: if (show_mat) break; else continue; } j++; if (j / 6 == catalogmenu_row[catindex]) { k = j % 6; draw_key(k, 0, 0, vars[i].name, vars[i].length); catalogmenu_item[catindex][k] = i; if (k == 5) break; } } while (k < 5) { draw_key(++k, 0, 0, "", 0); catalogmenu_item[catindex][k] = -1; } mode_updown = catalogmenu_rows[catindex] > 1; shell_annunciators(mode_updown, -1, -1, -1, -1, -1); } } void display_mem() { uint4 bytes = shell_get_mem(); char buf[16]; int buflen; clear_display(); draw_string(0, 0, "Available Memory:", 17); buflen = uint2string(bytes, buf, 16); draw_string(0, 1, buf, buflen); draw_string(buflen + 1, 1, "Bytes", 5); flush_display(); } void show() { if (flags.f.prgm_mode) display_prgm_line(-1, 0); else if (flags.f.alpha_mode) { clear_display(); if (reg_alpha_length <= 22) draw_string(0, 0, reg_alpha, reg_alpha_length); else { draw_string(0, 0, reg_alpha, 22); draw_string(0, 1, reg_alpha + 22, reg_alpha_length - 22); } } else { char buf[44]; int bufptr; clear_display(); switch (reg_x->type) { case TYPE_REAL: { bufptr = phloat2string(((vartype_real *) reg_x)->x, buf, 44, 2, 0, 3, flags.f.thousands_separators); if (bufptr <= 22) draw_string(0, 0, buf, bufptr); else { draw_string(0, 0, buf, 22); draw_string(0, 1, buf + 22, bufptr - 22); } break; } case TYPE_STRING: { vartype_string *s = (vartype_string *) reg_x; draw_char(0, 0, '"'); draw_string(1, 0, s->text, s->length); draw_char(s->length + 1, 0, '"'); break; } case TYPE_COMPLEX: { vartype_complex *c = (vartype_complex *) reg_x; phloat x, y; if (flags.f.polar) { generic_r2p(c->re, c->im, &x, &y); if (p_isinf(x)) x = POS_HUGE_PHLOAT; } else { x = c->re; y = c->im; } bufptr = phloat2string(x, buf, 22, 0, 0, 3, flags.f.thousands_separators); draw_string(0, 0, buf, bufptr); bufptr = phloat2string(y, buf, 22, 0, 0, 3, flags.f.thousands_separators); if (flags.f.polar) { draw_char(0, 1, 23); draw_string(1, 1, buf, bufptr); } else { if (buf[0] == '-') { draw_string(0, 1, "-i", 2); draw_string(2, 1, buf + 1, bufptr - 1); } else { draw_char(0, 1, 'i'); draw_string(1, 1, buf, bufptr); } } break; } case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) reg_x; phloat *d = rm->array->data; bufptr = vartype2string(reg_x, buf, 22); draw_string(0, 0, buf, bufptr); draw_string(0, 1, "1:1=", 4); if (rm->array->is_string[0]) { draw_char(4, 1, '"'); draw_string(5, 1, phloat_text(*d), phloat_length(*d)); draw_char(5 + phloat_length(*d), 1, '"'); } else { bufptr = phloat2string(*d, buf, 18, 0, 0, 3, flags.f.thousands_separators); draw_string(4, 1, buf, bufptr); } break; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) reg_x; vartype_complex c; bufptr = vartype2string(reg_x, buf, 22); draw_string(0, 0, buf, bufptr); draw_string(0, 1, "1:1=", 4); c.type = TYPE_COMPLEX; c.re = cm->array->data[0]; c.im = cm->array->data[1]; bufptr = vartype2string((vartype *) &c, buf, 18); draw_string(4, 1, buf, bufptr); break; } } } flush_display(); } void redisplay() { int menu_id; int avail_rows = 2; int i; if (mode_clall) { clear_display(); draw_string(0, 0, "Clear All Memory?", 17); draw_key(0, 0, 0, "YES", 3); draw_key(1, 0, 0, "", 0); draw_key(2, 0, 0, "", 0); draw_key(3, 0, 0, "", 0); draw_key(4, 0, 0, "", 0); draw_key(5, 0, 0, "NO", 2); flush_display(); return; } if (flags.f.two_line_message) return; if (flags.f.message) clear_row(1); else clear_display(); if (mode_commandmenu != MENU_NONE) menu_id = mode_commandmenu; else if (mode_alphamenu != MENU_NONE) menu_id = mode_alphamenu; else if (mode_transientmenu != MENU_NONE) menu_id = mode_transientmenu; else if (mode_plainmenu != MENU_NONE) menu_id = mode_plainmenu; else if (mode_appmenu != MENU_NONE) menu_id = mode_appmenu; else menu_id = MENU_NONE; if (menu_id == MENU_CATALOG) { draw_catalog(); avail_rows = 1; } else if (menu_id == MENU_VARMENU) { draw_varmenu(); if (varmenu_length == 0) { redisplay(); return; } avail_rows = 1; } else if (menu_id == MENU_CUSTOM1 || menu_id == MENU_CUSTOM2 || menu_id == MENU_CUSTOM3) { int r = menu_id - MENU_CUSTOM1; if (flags.f.local_label && !(mode_command_entry && incomplete_argtype == ARG_CKEY)) { for (i = 0; i < 5; i++) { char c = (r == 0 ? 'A' : 'F') + i; draw_key(i, 0, 0, &c, 1); } draw_key(5, 0, 0, "XEQ", 3); } else { for (i = 0; i < 6; i++) { draw_key(i, 0, 1, custommenu_label[r][i], custommenu_length[r][i]); } } avail_rows = 1; } else if (menu_id == MENU_PROGRAMMABLE) { for (i = 0; i < 6; i++) draw_key(i, 0, 0, progmenu_label[i], progmenu_length[i]); avail_rows = 1; } else if (menu_id != MENU_NONE) { const menu_spec *m = menus + menu_id; for (i = 0; i < 6; i++) { const menu_item_spec *mi = m->child + i; if (mi->menuid == MENU_NONE || (mi->menuid & 0x3000) == 0) draw_key(i, 0, 0, mi->title, mi->title_length); else { int cmd_id = mi->menuid & 0xfff; const command_spec *cmd = cmdlist(cmd_id); int is_flag = (mi->menuid & 0x2000) != 0; if (is_flag) { /* Take a closer look at the command ID and highlight * the menu item if appropriate -- that is, clear 'is_flag' * if highlighting is *not* appropriate */ switch (cmd_id) { case CMD_FIX: is_flag = flags.f.fix_or_all && !flags.f.eng_or_all; break; case CMD_SCI: is_flag = !flags.f.fix_or_all && !flags.f.eng_or_all; break; case CMD_ENG: is_flag = !flags.f.fix_or_all && flags.f.eng_or_all; break; case CMD_ALL: is_flag = flags.f.fix_or_all && flags.f.eng_or_all; break; case CMD_RDXDOT: is_flag = flags.f.decimal_point; break; case CMD_RDXCOMMA: is_flag = !flags.f.decimal_point; break; case CMD_DEG: is_flag = !flags.f.rad && !flags.f.grad; break; case CMD_RAD: is_flag = flags.f.rad && !flags.f.grad; break; case CMD_GRAD: is_flag = flags.f.grad; break; case CMD_POLAR: is_flag = flags.f.polar; break; case CMD_RECT: is_flag = !flags.f.polar; break; case CMD_QUIET: is_flag = !flags.f.audio_enable; break; case CMD_CPXRES: is_flag = !flags.f.real_result_only; break; case CMD_REALRES: is_flag = flags.f.real_result_only; break; case CMD_KEYASN: is_flag = !flags.f.local_label; break; case CMD_LCLBL: is_flag = flags.f.local_label; break; case CMD_PON: is_flag = flags.f.printer_exists; break; case CMD_POFF: is_flag = !flags.f.printer_exists; break; case CMD_MAN: is_flag = !flags.f.trace_print && !flags.f.normal_print; break; case CMD_NORM: is_flag = !flags.f.trace_print && flags.f.normal_print; break; case CMD_TRACE: is_flag = flags.f.trace_print; break; case CMD_ALLSIGMA: is_flag = flags.f.all_sigma; break; case CMD_LINSIGMA: is_flag = !flags.f.all_sigma; break; case CMD_LINF: is_flag = flags.f.lin_fit; break; case CMD_LOGF: is_flag = flags.f.log_fit; break; case CMD_EXPF: is_flag = flags.f.exp_fit; break; case CMD_PWRF: is_flag = flags.f.pwr_fit; break; case CMD_WRAP: is_flag = !flags.f.grow; break; case CMD_GROW: is_flag = flags.f.grow; break; case CMD_BINM: is_flag = get_base() == 2; break; case CMD_OCTM: is_flag = get_base() == 8; break; case CMD_DECM: is_flag = get_base() == 10; break; case CMD_HEXM: is_flag = get_base() == 16; break; } } draw_key(i, is_flag, 1, cmd->name, cmd->name_length); } } avail_rows = 1; } if (!flags.f.prgm_mode && (mode_command_entry || pending_command != CMD_NONE)) { int cmd_row; if (menu_id == MENU_NONE) { cmd_row = 1; avail_rows = 1; } else { cmd_row = 0; avail_rows = 0; } if (mode_command_entry) display_incomplete_command(cmd_row); else display_command(cmd_row); } if (!flags.f.alpha_mode && !flags.f.prgm_mode) { if (avail_rows == 1) { if (!flags.f.message) display_x(0); } else if (avail_rows == 2) { if (!flags.f.message) display_y(0); display_x(1); } } else if (flags.f.prgm_mode && avail_rows != 0) { if (mode_command_entry) { if (avail_rows == 1) display_incomplete_command(0); else { display_prgm_line(0, -1); display_incomplete_command(1); } } else { if (avail_rows == 1) { if (!flags.f.message) display_prgm_line(0, 0); } else if (avail_rows == 2) { if (!flags.f.message) { if (pc == -1) prgm_highlight_row = 0; else { if (prgms[current_prgm].text[pc] == CMD_END) prgm_highlight_row = 1; } if (prgm_highlight_row == 0) { display_prgm_line(0, 0); display_prgm_line(1, 1); } else { display_prgm_line(0, -1); display_prgm_line(1, 0); } } else display_prgm_line(1, 0); } } } else if (flags.f.alpha_mode && avail_rows != 0 && !flags.f.message) { int avail = mode_alpha_entry ? 21 : 22; if (reg_alpha_length <= avail) { draw_string(0, 0, reg_alpha, reg_alpha_length); if (mode_alpha_entry) draw_char(reg_alpha_length, 0, '_'); } else { avail--; draw_char(0, 0, 26); draw_string(1, 0, reg_alpha + reg_alpha_length - avail, avail); if (mode_alpha_entry) draw_char(21, 0, '_'); } } flush_display(); } void print_display() { shell_print("", 5, display, 17, 0, 0, 131, 16); } typedef struct { char buf[100]; int len; int saved_prgm; int cmd; arg_struct arg; int4 line; int4 pc; int4 lines; int width; int first; int trace; int normal; } prp_data_struct; static prp_data_struct *prp_data; static int print_program_worker(int interrupted); int print_program(int prgm_index, int4 pc, int4 lines, int normal) { prp_data_struct *dat = (prp_data_struct *) malloc(sizeof(prp_data_struct)); if (dat == NULL) return ERR_INSUFFICIENT_MEMORY; shell_annunciators(-1, -1, 1, -1, -1, -1); dat->len = 0; dat->saved_prgm = current_prgm; dat->cmd = CMD_NONE; dat->line = pc2line(pc); dat->pc = pc; dat->lines = lines; dat->width = flags.f.double_wide_print ? 12 : 24; dat->first = 1; if (normal) { dat->trace = 0; dat->normal = 1; } else { dat->trace = flags.f.trace_print; dat->normal = flags.f.normal_print; } current_prgm = prgm_index; prp_data = dat; if (normal) { /* Printing just one line for NORM and TRACE mode; * we don't do the 'interruptible' thing in this case. */ int err; while ((err = print_program_worker(0)) == ERR_INTERRUPTIBLE); return err; } else { print_text(NULL, 0, 1); mode_interruptible = print_program_worker; mode_stoppable = true; return ERR_INTERRUPTIBLE; } } static int print_program_worker(int interrupted) { prp_data_struct *dat = prp_data; int printed = 0; if (interrupted) goto done; do { if (dat->line == 0) dat->pc = 0; else get_next_command(&dat->pc, &dat->cmd, &dat->arg, 0); if (dat->trace) { if (dat->cmd == CMD_LBL || dat->first) { if (dat->len > 0) { print_lines(dat->buf, dat->len, 1); printed = 1; } if (!dat->first) print_text(NULL, 0, 1); dat->first = 0; dat->buf[0] = ' '; dat->len = 1 + prgmline2buf(dat->buf + 1, 100 - 1, dat->line, dat->cmd == CMD_LBL, dat->cmd, &dat->arg); if (dat->cmd == CMD_LBL || dat->cmd == CMD_END || dat->lines == 1) { print_lines(dat->buf, dat->len, 1); printed = 1; dat->len = 0; } } else { int i, len2; if (dat->len > 0) { dat->buf[dat->len++] = ' '; dat->buf[dat->len++] = ' '; } len2 = prgmline2buf(dat->buf + dat->len, 100 - dat->len, -1, 0, dat->cmd, &dat->arg); if (dat->len > 0 && dat->len + len2 > dat->width) { /* Break line before current instruction */ print_lines(dat->buf, dat->len - 2, 1); printed = 1; for (i = 0; i < len2; i++) dat->buf[i] = dat->buf[dat->len + i]; dat->len = len2; } else dat->len += len2; if (dat->lines == 1 || dat->cmd == CMD_END) { print_lines(dat->buf, dat->len, 1); printed = 1; } else if (dat->len >= dat->width) { len2 = (dat->len / dat->width) * dat->width; print_lines(dat->buf, len2, 1); printed = 1; for (i = len2; i < dat->len; i++) dat->buf[i - len2] = dat->buf[i]; dat->len -= len2; } } } else { dat->len = prgmline2buf(dat->buf, 100, dat->line, dat->cmd == CMD_LBL, dat->cmd, &dat->arg); if (dat->normal) { /* In normal mode, programs are printed right-justified; * we pad the instuctions to a minimum of 8 characters so * the listing won't look too ragged. * First, find the beginning of the instruction -- it starts * right after the first space or 'goose' (6) character. */ int p = 0; while (dat->buf[p] != ' ' && dat->buf[p] != 6) p++; while (dat->len < p + 9) dat->buf[dat->len++] = ' '; /* Insert blank line above LBLs */ if (dat->cmd == CMD_LBL && !dat->first) print_text(NULL, 0, 1); dat->first = 0; } print_lines(dat->buf, dat->len, !dat->normal); printed = 1; } dat->line++; dat->lines--; } while (!printed); if (dat->lines != 0 && dat->cmd != CMD_END) return ERR_INTERRUPTIBLE; done: current_prgm = dat->saved_prgm; free(dat); shell_annunciators(-1, -1, 0, -1, -1, -1); return ERR_STOP; } void print_program_line(int prgm_index, int4 pc) { print_program(prgm_index, pc, 1, 1); } int command2buf(char *buf, int len, int cmd, const arg_struct *arg) { int bufptr = 0; int4 xrom_arg; if (!core_settings.enable_ext_copan && cmd >= CMD_OPENF && cmd <= CMD_DELP || !core_settings.enable_ext_bigstack && cmd == CMD_DROP || !core_settings.enable_ext_accel && cmd == CMD_ACCEL || !core_settings.enable_ext_locat && cmd == CMD_LOCAT || !core_settings.enable_ext_heading && cmd == CMD_HEADING || !core_settings.enable_ext_time && cmd >= CMD_ADATE && cmd <= CMD_SWPT || (cmdlist(cmd)->hp42s_code & 0xfffff800) == 0x0000a000 && (cmdlist(cmd)->flags & FLAG_HIDDEN) != 0) { xrom_arg = cmdlist(cmd)->hp42s_code; cmd = CMD_XROM; } else if (cmd == CMD_XROM) xrom_arg = arg->val.num; const command_spec *cmdspec = cmdlist(cmd); if (cmd >= CMD_ASGN01 && cmd <= CMD_ASGN18) string2buf(buf, len, &bufptr, "ASSIGN ", 7); else string2buf(buf, len, &bufptr, cmdspec->name, cmdspec->name_length); if (cmd == CMD_XROM) { int n = xrom_arg & 0x7FF; int rom = n >> 6; int instr = n & 63; char2buf(buf, len, &bufptr, ' '); char2buf(buf, len, &bufptr, '0' + rom / 10); char2buf(buf, len, &bufptr, '0' + rom % 10); char2buf(buf, len, &bufptr, ','); char2buf(buf, len, &bufptr, '0' + instr / 10); char2buf(buf, len, &bufptr, '0' + instr % 10); } else if (cmdspec->argtype != ARG_NONE) { if (cmdspec->name_length > 0) char2buf(buf, len, &bufptr, ' '); if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) string2buf(buf, len, &bufptr, "IND ", 4); if (arg->type == ARGTYPE_NUM || arg->type == ARGTYPE_IND_NUM) { int digits = arg->type == ARGTYPE_IND_NUM ? 2 : cmdspec->argtype == ARG_NUM9 ? 1 : 2; int d = 1, i; for (i = 0; i < digits - 1; i++) d *= 10; while (arg->val.num >= d * 10) { d *= 10; digits++; } for (i = 0; i < digits; i++) { char2buf(buf, len, &bufptr, (char) ('0' + (arg->val.num / d) % 10)); d /= 10; } } else if (arg->type == ARGTYPE_STK || arg->type == ARGTYPE_IND_STK) { string2buf(buf, len, &bufptr, "ST ", 3); char2buf(buf, len, &bufptr, arg->val.stk); } else if (arg->type == ARGTYPE_STR || arg->type == ARGTYPE_IND_STR) { char2buf(buf, len, &bufptr, '"'); string2buf(buf, len, &bufptr, arg->val.text, arg->length); char2buf(buf, len, &bufptr, '"'); } else if (arg->type == ARGTYPE_LCLBL) { char2buf(buf, len, &bufptr, arg->val.lclbl); } else if (arg->type == ARGTYPE_LBLINDEX) { label_struct *lbl = labels + arg->val.num; if (lbl->length == 0) { if (arg->val.num == labels_count - 1) string2buf(buf, len, &bufptr, ".END.", 5); else string2buf(buf, len, &bufptr, "END", 3); } else { char2buf(buf, len, &bufptr, '"'); string2buf(buf, len, &bufptr, lbl->name, lbl->length); char2buf(buf, len, &bufptr, '"'); } } else /* ARGTYPE_COMMAND; for backward compatibility only */ { const command_spec *cs = cmdlist(arg->val.cmd); char2buf(buf, len, &bufptr, '"'); string2buf(buf, len, &bufptr, cs->name, cs->name_length); char2buf(buf, len, &bufptr, '"'); } } if (cmd >= CMD_ASGN01 && cmd <= CMD_ASGN18) { int keynum = cmd - CMD_ASGN01 + 1; string2buf(buf, len, &bufptr, " TO ", 4); char2buf(buf, len, &bufptr, (char) ('0' + keynum / 10)); char2buf(buf, len, &bufptr, (char) ('0' + keynum % 10)); } return bufptr; } static int get_cat_index() { if (mode_commandmenu != MENU_NONE) return 0; else if (mode_alphamenu != MENU_NONE) return 1; else if (mode_transientmenu != MENU_NONE) return 2; else if (mode_plainmenu != MENU_NONE) return 3; else if (mode_appmenu != MENU_NONE) return 4; else return -1; } void set_menu(int level, int menuid) { int err = set_menu_return_err(level, menuid); if (err != ERR_NONE) { display_error(err, 1); flush_display(); } } int set_menu_return_err(int level, int menuid) { int *newmenu; int err; switch (level) { case MENULEVEL_COMMAND: mode_commandmenu = menuid; goto lbl_03; case MENULEVEL_ALPHA: mode_alphamenu = menuid; goto lbl_02; case MENULEVEL_TRANSIENT: mode_transientmenu = menuid; goto lbl_01; case MENULEVEL_PLAIN: mode_plainmenu = menuid; goto lbl_00; case MENULEVEL_APP: err = set_appmenu(menuid); if (err != ERR_NONE) return err; } mode_plainmenu = MENU_NONE; lbl_00: mode_transientmenu = MENU_NONE; lbl_01: mode_alphamenu = MENU_NONE; lbl_02: mode_commandmenu = MENU_NONE; lbl_03: newmenu = get_front_menu(); if (newmenu != NULL) { if (*newmenu == MENU_CATALOG) { int index = get_cat_index(); mode_updown = index != -1 && catalogmenu_rows[index] > 1; } else if (*newmenu == MENU_PROGRAMMABLE) { /* The programmable menu's up/down annunciator is on if the UP * and/or DOWN keys have been assigned to. * This is something the original HP-42S doesn't do, but I couldn't * resist this little improvement, perfect compatibility be damned. * In my defense, the Programming Examples and Techniques book, * bottom of page 34, does state that this should work. * Can't say whether the fact that it doesn't work on the real * HP-42S is a bug, or whether the coders and the documentation * writers just had a misunderstanding. */ mode_updown = progmenu_arg[6].type != ARGTYPE_NONE || progmenu_arg[7].type != ARGTYPE_NONE; } else { /* The up/down annunciator for catalogs depends on how many * items they contain; this is handled in draw_catalog(). */ mode_updown = *newmenu == MENU_VARMENU ? varmenu_rows > 1 : menus[*newmenu].next != MENU_NONE; } } else mode_updown = false; shell_annunciators(mode_updown, -1, -1, -1, -1, -1); return ERR_NONE; } void set_appmenu_exitcallback(int callback_id) { appmenu_exitcallback = callback_id; } void set_plainmenu(int menuid) { mode_commandmenu = MENU_NONE; mode_alphamenu = MENU_NONE; mode_transientmenu = MENU_NONE; if (menuid == mode_plainmenu) mode_plainmenu_sticky = 1; else if (menuid == MENU_CUSTOM1 || menuid == MENU_CUSTOM2 || menuid == MENU_CUSTOM3) { mode_plainmenu = menuid; mode_plainmenu_sticky = 1; redisplay(); mode_updown = 1; shell_annunciators(1, -1, -1, -1, -1, -1); } else { /* Even if it's a different menu than the current one, it should * still stick if it belongs to the same group. */ if (mode_plainmenu != MENU_NONE) { int menu1 = mode_plainmenu; int menu2 = menuid; int parent; while ((parent = menus[menu1].parent) != MENU_NONE) menu1 = parent; while ((parent = menus[menu2].parent) != MENU_NONE) menu2 = parent; if (menu1 == menu2) mode_plainmenu_sticky = 1; else if (menus[menu1].next == MENU_NONE) mode_plainmenu_sticky = 0; else { int m = menu1; mode_plainmenu_sticky = 0; do { m = menus[m].next; if (m == menu2) { mode_plainmenu_sticky = 1; break; } } while (m != menu1); } } else mode_plainmenu_sticky = 0; if (!mode_plainmenu_sticky) { mode_plainmenu = menuid; if (mode_plainmenu == MENU_CATALOG) set_cat_section(CATSECT_TOP); redisplay(); } mode_updown = mode_plainmenu != MENU_NONE && menus[mode_plainmenu].next != MENU_NONE; shell_annunciators(mode_updown, -1, -1, -1, -1, -1); } } void set_catalog_menu(int section) { mode_commandmenu = MENU_CATALOG; move_cat_row(0); set_cat_section(section); switch (section) { case CATSECT_TOP: case CATSECT_FCN: case CATSECT_PGM: case CATSECT_PGM_ONLY: return; case CATSECT_REAL: case CATSECT_REAL_ONLY: if (!vars_exist(1, 0, 0)) mode_commandmenu = MENU_NONE; return; case CATSECT_CPX: if (!vars_exist(0, 1, 0)) mode_commandmenu = MENU_NONE; return; case CATSECT_MAT: case CATSECT_MAT_ONLY: if (!vars_exist(0, 0, 1)) mode_commandmenu = MENU_NONE; return; case CATSECT_VARS_ONLY: if (!vars_exist(1, 1, 1)) mode_commandmenu = MENU_NONE; return; case CATSECT_PGM_SOLVE: case CATSECT_PGM_INTEG: default: mode_commandmenu = MENU_NONE; return; } } int *get_front_menu() { if (mode_commandmenu != MENU_NONE) return &mode_commandmenu; if (mode_alphamenu != MENU_NONE) return &mode_alphamenu; if (mode_transientmenu != MENU_NONE) return &mode_transientmenu; if (mode_plainmenu != MENU_NONE) return &mode_plainmenu; if (mode_appmenu != MENU_NONE) return &mode_appmenu; return NULL; } void set_cat_section(int section) { int index = get_cat_index(); if (index != -1) catalogmenu_section[index] = section; } int get_cat_section() { int index = get_cat_index(); if (index != -1) return catalogmenu_section[index]; else return CATSECT_TOP; } void move_cat_row(int direction) { int index = get_cat_index(); if (index == -1) return; if (direction == 0) catalogmenu_row[index] = 0; else if (direction == -1) { catalogmenu_row[index]--; if (catalogmenu_row[index] < 0) catalogmenu_row[index] = catalogmenu_rows[index] - 1; } else { catalogmenu_row[index]++; if (catalogmenu_row[index] >= catalogmenu_rows[index]) catalogmenu_row[index] = 0; } } void set_cat_row(int row) { int index = get_cat_index(); if (index == -1) return; catalogmenu_row[index] = row; } int get_cat_row() { int index = get_cat_index(); if (index == -1) return 0; else return catalogmenu_row[index]; } int get_cat_item(int menukey) { int index = get_cat_index(); if (index == -1) return -1; else return catalogmenu_item[index][menukey]; } void update_catalog() { int *the_menu; if (mode_commandmenu != MENU_NONE) the_menu = &mode_commandmenu; else if (mode_alphamenu != MENU_NONE) the_menu = &mode_alphamenu; else if (mode_transientmenu != MENU_NONE) the_menu = &mode_transientmenu; else if (mode_plainmenu != MENU_NONE) the_menu = &mode_plainmenu; else if (mode_appmenu != MENU_NONE) the_menu = &mode_appmenu; else return; if (*the_menu != MENU_CATALOG) return; switch (get_cat_section()) { case CATSECT_TOP: case CATSECT_FCN: return; case CATSECT_PGM: case CATSECT_PGM_ONLY: break; case CATSECT_REAL: if (!vars_exist(1, 0, 0)) set_cat_section(CATSECT_TOP); break; case CATSECT_CPX: if (!vars_exist(0, 1, 0)) set_cat_section(CATSECT_TOP); break; case CATSECT_MAT: if (!vars_exist(0, 0, 1)) set_cat_section(CATSECT_TOP); break; case CATSECT_REAL_ONLY: if (!vars_exist(1, 0, 0)) { *the_menu = MENU_NONE; redisplay(); return; } break; case CATSECT_MAT_ONLY: if (!vars_exist(0, 0, 1)) { *the_menu = MENU_NONE; redisplay(); return; } break; case CATSECT_VARS_ONLY: if (!vars_exist(1, 1, 1)) { *the_menu = MENU_NONE; redisplay(); return; } break; case CATSECT_PGM_SOLVE: case CATSECT_PGM_INTEG: if (!mvar_prgms_exist()) { *the_menu = MENU_NONE; display_error(ERR_NO_MENU_VARIABLES, 0); redisplay(); return; } break; } draw_catalog(); } void clear_custom_menu() { int row, key; for (row = 0; row < 3; row++) for (key = 0; key < 6; key++) custommenu_length[row][key] = 0; } void assign_custom_key(int keynum, const char *name, int length) { int row = (keynum - 1) / 6; int key = (keynum - 1) % 6; int i; custommenu_length[row][key] = length; for (i = 0; i < length; i++) custommenu_label[row][key][i] = name[i]; } void get_custom_key(int keynum, char *name, int *length) { int row = (keynum - 1) / 6; int key = (keynum - 1) % 6; string_copy(name, length, custommenu_label[row][key], custommenu_length[row][key]); } void clear_prgm_menu() { int i; for (i = 0; i < 9; i++) progmenu_arg[i].type = ARGTYPE_NONE; for (i = 0; i < 6; i++) progmenu_length[i] = 0; } void assign_prgm_key(int keynum, int is_gto, const arg_struct *arg) { int length, i; keynum--; progmenu_arg[keynum] = (arg_struct) *arg; progmenu_is_gto[keynum] = is_gto; length = reg_alpha_length; if (keynum < 6) { if (length > 7) length = 7; for (i = 0; i < length; i++) progmenu_label[keynum][i] = reg_alpha[i]; progmenu_length[keynum] = length; } } void do_prgm_menu_key(int keynum) { int err, oldprgm; int4 oldpc; keynum--; if (keynum == 8) set_menu(MENULEVEL_PLAIN, MENU_NONE); if (progmenu_arg[keynum].type == ARGTYPE_NONE) { if (keynum < 6) pending_command = CMD_NULL; else if (keynum == 8) pending_command = CMD_CANCELLED; return; } if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) print_command(progmenu_is_gto[keynum] ? CMD_GTO : CMD_XEQ, &progmenu_arg[keynum]); oldprgm = current_prgm; oldpc = pc; set_running(true); progmenu_arg[keynum].target = -1; /* Force docmd_gto() to search */ err = docmd_gto(&progmenu_arg[keynum]); if (err != ERR_NONE) { set_running(false); display_error(err, 1); flush_display(); return; } if (!progmenu_is_gto[keynum]) { if (oldpc == -1) oldpc = 0; err = push_rtn_addr(oldprgm, oldpc); if (err != ERR_NONE) { /* Solve/Integ RTN Lost. Someone is writing weird programs * if they're using a programmable menu in the middle of * a solver invocation... */ set_running(false); display_error(err, 1); flush_display(); return; } } } free42-nologo-1.4.77/common/core_display.h000644 000765 000024 00000005615 12110237247 020756 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_DISPLAY_H #define CORE_DISPLAY_H 1 #include "free42.h" #include "core_phloat.h" #include "core_globals.h" bool persist_display(); bool unpersist_display(int version); void clear_display(); void flush_display(); void repaint_display(); void draw_pixel(int x, int y); void draw_pattern(phloat dx, phloat dy, const char *pattern, int pattern_width); void fly_goose(); void squeak(); void tone(int n); void draw_char(int x, int y, char c); void get_char(char *bits, char c); void draw_string(int x, int y, const char *s, int length); void clear_row(int row); void display_prgm_line(int row, int line_offset); void display_x(int row); void display_y(int row); void display_incomplete_command(int row); void display_error(int error, int print); void display_command(int row); void draw_varmenu(); void display_mem(); void show(); void redisplay(); void print_display(); int print_program(int prgm_index, int4 pc, int4 lines, int normal); void print_program_line(int prgm_index, int4 pc); int command2buf(char *buf, int len, int cmd, const arg_struct *arg); #define MENULEVEL_COMMAND 0 #define MENULEVEL_ALPHA 1 #define MENULEVEL_TRANSIENT 2 #define MENULEVEL_PLAIN 3 #define MENULEVEL_APP 4 int appmenu_exitcallback_1(int menuid); int appmenu_exitcallback_2(int menuid); int appmenu_exitcallback_3(int menuid); int appmenu_exitcallback_4(int menuid); int appmenu_exitcallback_5(int menuid); void set_menu(int level, int menuid); int set_menu_return_err(int level, int menuid); void set_appmenu_exitcallback(int callback_id); void set_plainmenu(int menuid); void set_catalog_menu(int direction); int *get_front_menu(); void set_cat_section(int section); int get_cat_section(); void move_cat_row(int direction); void set_cat_row(int row); int get_cat_row(); int get_cat_item(int menukey); void update_catalog(); void clear_custom_menu(); void assign_custom_key(int keynum, const char *name, int length); void get_custom_key(int keynum, char *name, int *length); void clear_prgm_menu(); void assign_prgm_key(int keynum, int is_gto, const arg_struct *arg); void do_prgm_menu_key(int keynum); #endif free42-nologo-1.4.77/common/core_globals.cc000644 000765 000024 00000255517 12110237247 021102 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_globals.h" #include "core_commands2.h" #include "core_commands4.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_math1.h" #include "core_tables.h" #include "core_variables.h" #include "shell.h" error_spec errors[] = { { /* NONE */ NULL, 0 }, { /* ALPHA_DATA_IS_INVALID */ "Alpha Data Is Invalid", 21 }, { /* INSUFFICIENT_MEMORY */ "Insufficient Memory", 19 }, { /* NOT_YET_IMPLEMENTED */ "Not Yet Implemented", 19 }, { /* OUT_OF_RANGE */ "Out of Range", 12 }, { /* DIVIDE_BY_0 */ "Divide by 0", 11 }, { /* INVALID_TYPE */ "Invalid Type", 12 }, { /* INVALID_DATA */ "Invalid Data", 12 }, { /* DIMENSION_ERROR */ "Dimension Error", 15 }, { /* SIZE_ERROR */ "Size Error", 10 }, { /* INTERNAL_ERROR */ "Internal Error", 14 }, { /* NONEXISTENT */ "Nonexistent", 11 }, { /* RESTRICTED_OPERATION */ "Restricted Operation", 20 }, { /* YES */ "Yes", 3 }, { /* NO */ "No", 2 }, { /* STOP */ NULL, 0 }, { /* LABEL_NOT_FOUND */ "Label Not Found", 15 }, { /* NO_REAL_VARIABLES */ "No Real Variables", 17 }, { /* NO_COMPLEX_VARIABLES */ "No Complex Variables", 20 }, { /* NO_MATRIX_VARIABLES */ "No Matrix Variables", 19 }, { /* NO_MENU_VARIABLES */ "No Menu Variables", 17 }, { /* STAT_MATH_ERROR */ "Stat Math Error", 15 }, { /* INVALID_FORECAST_MODEL */ "Invalid Forecast Model", 22 }, { /* SOLVE_INTEG_RTN_LOST */ "Solve/Integ RTN Lost", 20 }, { /* SINGULAR_MATRIX */ "Singular Matrix", 15 }, { /* SOLVE_SOLVE */ "Solve(Solve)", 12 }, { /* INTEG_INTEG */ "Integ(Integ)", 12 }, { /* RUN */ NULL, 0 }, { /* INTERRUPTED */ "Interrupted", 11 }, { /* PRINTING_IS_DISABLED */ "Printing Is Disabled", 20 }, { /* INTERRUPTIBLE */ NULL, 0 }, { /* NO_VARIABLES */ "No Variables", 12 }, { /* SUSPICIOUS_OFF */ "Suspicious OFF", 14 } }; menu_spec menus[] = { { /* MENU_ALPHA1 */ MENU_NONE, MENU_ALPHA2, MENU_ALPHA2, { { MENU_ALPHA_ABCDE1, 5, "ABCDE" }, { MENU_ALPHA_FGHI, 4, "FGHI" }, { MENU_ALPHA_JKLM, 4, "JKLM" }, { MENU_ALPHA_NOPQ1, 4, "NOPQ" }, { MENU_ALPHA_RSTUV1, 5, "RSTUV" }, { MENU_ALPHA_WXYZ, 4, "WXYZ" } } }, { /* MENU_ALPHA2 */ MENU_NONE, MENU_ALPHA1, MENU_ALPHA1, { { MENU_ALPHA_PAREN, 5, "( [ {" }, { MENU_ALPHA_ARROW, 3, "\020^\016" }, { MENU_ALPHA_COMP, 5, "< = >" }, { MENU_ALPHA_MATH, 4, "MATH" }, { MENU_ALPHA_PUNC1, 4, "PUNC" }, { MENU_ALPHA_MISC1, 4, "MISC" } } }, { /* MENU_ALPHA_ABCDE1 */ MENU_ALPHA1, MENU_ALPHA_ABCDE2, MENU_ALPHA_ABCDE2, { { MENU_NONE, 1, "A" }, { MENU_NONE, 1, "B" }, { MENU_NONE, 1, "C" }, { MENU_NONE, 1, "D" }, { MENU_NONE, 1, "E" }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_ABCDE2 */ MENU_ALPHA1, MENU_ALPHA_ABCDE1, MENU_ALPHA_ABCDE1, { { MENU_NONE, 1, "\026" }, { MENU_NONE, 1, "\024" }, { MENU_NONE, 1, "\031" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_FGHI */ MENU_ALPHA1, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "F" }, { MENU_NONE, 1, "G" }, { MENU_NONE, 1, "H" }, { MENU_NONE, 1, "I" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_JKLM */ MENU_ALPHA1, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "J" }, { MENU_NONE, 1, "K" }, { MENU_NONE, 1, "L" }, { MENU_NONE, 1, "M" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_NOPQ1 */ MENU_ALPHA1, MENU_ALPHA_NOPQ2, MENU_ALPHA_NOPQ2, { { MENU_NONE, 1, "N" }, { MENU_NONE, 1, "O" }, { MENU_NONE, 1, "P" }, { MENU_NONE, 1, "Q" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_NOPQ2 */ MENU_ALPHA1, MENU_ALPHA_NOPQ1, MENU_ALPHA_NOPQ1, { { MENU_NONE, 1, "\025" }, { MENU_NONE, 1, "\034" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_RSTUV1 */ MENU_ALPHA1, MENU_ALPHA_RSTUV2, MENU_ALPHA_RSTUV2, { { MENU_NONE, 1, "R" }, { MENU_NONE, 1, "S" }, { MENU_NONE, 1, "T" }, { MENU_NONE, 1, "U" }, { MENU_NONE, 1, "V" }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_RSTUV2 */ MENU_ALPHA1, MENU_ALPHA_RSTUV1, MENU_ALPHA_RSTUV1, { { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, "\035" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_WXYZ */ MENU_ALPHA1, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "W" }, { MENU_NONE, 1, "X" }, { MENU_NONE, 1, "Y" }, { MENU_NONE, 1, "Z" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_PAREN */ MENU_ALPHA2, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "(" }, { MENU_NONE, 1, ")" }, { MENU_NONE, 1, "[" }, { MENU_NONE, 1, "]" }, { MENU_NONE, 1, "{" }, { MENU_NONE, 1, "}" } } }, { /* MENU_ALPHA_ARROW */ MENU_ALPHA2, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "\020" }, { MENU_NONE, 1, "^" }, { MENU_NONE, 1, "\016" }, { MENU_NONE, 1, "\017" }, { MENU_NONE, 1, " " }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_COMP */ MENU_ALPHA2, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "=" }, { MENU_NONE, 1, "\014" }, { MENU_NONE, 1, "<" }, { MENU_NONE, 1, ">" }, { MENU_NONE, 1, "\011" }, { MENU_NONE, 1, "\013" } } }, { /* MENU_ALPHA_MATH */ MENU_ALPHA2, MENU_NONE, MENU_NONE, { { MENU_NONE, 1, "\005" }, { MENU_NONE, 1, "\003" }, { MENU_NONE, 1, "\002" }, { MENU_NONE, 1, "\027" }, { MENU_NONE, 1, "\023" }, { MENU_NONE, 1, "\021" } } }, { /* MENU_ALPHA_PUNC1 */ MENU_ALPHA2, MENU_ALPHA_PUNC2, MENU_ALPHA_PUNC2, { { MENU_NONE, 1, "," }, { MENU_NONE, 1, ";" }, { MENU_NONE, 1, ":" }, { MENU_NONE, 1, "!" }, { MENU_NONE, 1, "?" }, { MENU_NONE, 1, "\"" } } }, { /* MENU_ALPHA_PUNC2 */ MENU_ALPHA2, MENU_ALPHA_PUNC1, MENU_ALPHA_PUNC1, { { MENU_NONE, 1, "\032" }, { MENU_NONE, 1, "_" }, { MENU_NONE, 1, "`" }, { MENU_NONE, 1, "'" }, { MENU_NONE, 1, "\010" }, { MENU_NONE, 1, "\012" } } }, { /* MENU_ALPHA_MISC1 */ MENU_ALPHA2, MENU_ALPHA_MISC2, MENU_ALPHA_MISC2, { { MENU_NONE, 1, "$" }, { MENU_NONE, 1, "*" }, { MENU_NONE, 1, "#" }, { MENU_NONE, 1, "/" }, { MENU_NONE, 1, "\037" }, { MENU_NONE, 1, " " } } }, { /* MENU_ALPHA_MISC2 */ MENU_ALPHA2, MENU_ALPHA_MISC1, MENU_ALPHA_MISC1, { { MENU_NONE, 1, "\022" }, { MENU_NONE, 1, "&" }, { MENU_NONE, 1, "@" }, { MENU_NONE, 1, "\\" }, { MENU_NONE, 1, "~" }, { MENU_NONE, 1, "|" } } }, { /* MENU_ST */ MENU_NONE, MENU_NONE, MENU_NONE, { { MENU_NONE, 4, "ST L" }, { MENU_NONE, 4, "ST X" }, { MENU_NONE, 4, "ST Y" }, { MENU_NONE, 4, "ST Z" }, { MENU_NONE, 4, "ST T" }, { MENU_NONE, 0, "" } } }, { /* MENU_IND_ST */ MENU_NONE, MENU_NONE, MENU_NONE, { { MENU_NONE, 3, "IND" }, { MENU_NONE, 4, "ST L" }, { MENU_NONE, 4, "ST X" }, { MENU_NONE, 4, "ST Y" }, { MENU_NONE, 4, "ST Z" }, { MENU_NONE, 4, "ST T" } } }, { /* MENU_IND */ MENU_NONE, MENU_NONE, MENU_NONE, { { MENU_NONE, 3, "IND" }, { MENU_NONE, 0, "" }, { MENU_NONE, 0, "" }, { MENU_NONE, 0, "" }, { MENU_NONE, 0, "" }, { MENU_NONE, 0, "" } } }, { /* MENU_MODES1 */ MENU_NONE, MENU_MODES2, MENU_MODES2, { { 0x2000 + CMD_DEG, 0, "" }, { 0x2000 + CMD_RAD, 0, "" }, { 0x2000 + CMD_GRAD, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x2000 + CMD_RECT, 0, "" }, { 0x2000 + CMD_POLAR, 0, "" } } }, { /* MENU_MODES2 */ MENU_NONE, MENU_MODES1, MENU_MODES1, { { 0x1000 + CMD_SIZE, 0, "" }, { 0x2000 + CMD_QUIET, 0, "" }, { 0x2000 + CMD_CPXRES, 0, "" }, { 0x2000 + CMD_REALRES, 0, "" }, { 0x2000 + CMD_KEYASN, 0, "" }, { 0x2000 + CMD_LCLBL, 0, "" } } }, { /* MENU_DISP */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x2000 + CMD_FIX, 0, "" }, { 0x2000 + CMD_SCI, 0, "" }, { 0x2000 + CMD_ENG, 0, "" }, { 0x2000 + CMD_ALL, 0, "" }, { 0x2000 + CMD_RDXDOT, 0, "" }, { 0x2000 + CMD_RDXCOMMA, 0, "" } } }, { /* MENU_CLEAR1 */ MENU_NONE, MENU_CLEAR2, MENU_CLEAR2, { { 0x1000 + CMD_CLSIGMA, 0, "" }, { 0x1000 + CMD_CLP, 0, "" }, { 0x1000 + CMD_CLV, 0, "" }, { 0x1000 + CMD_CLST, 0, "" }, { 0x1000 + CMD_CLA, 0, "" }, { 0x1000 + CMD_CLX, 0, "" } } }, { /* MENU_CLEAR2 */ MENU_NONE, MENU_CLEAR1, MENU_CLEAR1, { { 0x1000 + CMD_CLRG, 0, "" }, { 0x1000 + CMD_DEL, 0, "" }, { 0x1000 + CMD_CLKEYS, 0, "" }, { 0x1000 + CMD_CLLCD, 0, "" }, { 0x1000 + CMD_CLMENU, 0, "" }, { 0x1000 + CMD_CLALLa, 0, "" } } }, { /* MENU_CONVERT1 */ MENU_NONE, MENU_CONVERT2, MENU_CONVERT2, { { 0x1000 + CMD_TO_DEG, 0, "" }, { 0x1000 + CMD_TO_RAD, 0, "" }, { 0x1000 + CMD_TO_HR, 0, "" }, { 0x1000 + CMD_TO_HMS, 0, "" }, { 0x1000 + CMD_TO_REC, 0, "" }, { 0x1000 + CMD_TO_POL, 0, "" } } }, { /* MENU_CONVERT2 */ MENU_NONE, MENU_CONVERT1, MENU_CONVERT1, { { 0x1000 + CMD_IP, 0, "" }, { 0x1000 + CMD_FP, 0, "" }, { 0x1000 + CMD_RND, 0, "" }, { 0x1000 + CMD_ABS, 0, "" }, { 0x1000 + CMD_SIGN, 0, "" }, { 0x1000 + CMD_MOD, 0, "" } } }, { /* MENU_FLAGS */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_SF, 0, "" }, { 0x1000 + CMD_CF, 0, "" }, { 0x1000 + CMD_FS_T, 0, "" }, { 0x1000 + CMD_FC_T, 0, "" }, { 0x1000 + CMD_FSC_T, 0, "" }, { 0x1000 + CMD_FCC_T, 0, "" } } }, { /* MENU_PROB */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_COMB, 0, "" }, { 0x1000 + CMD_PERM, 0, "" }, { 0x1000 + CMD_FACT, 0, "" }, { 0x1000 + CMD_GAMMA, 0, "" }, { 0x1000 + CMD_RAN, 0, "" }, { 0x1000 + CMD_SEED, 0, "" } } }, { /* MENU_CUSTOM1 */ MENU_NONE, MENU_CUSTOM2, MENU_CUSTOM3, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_CUSTOM2 */ MENU_NONE, MENU_CUSTOM3, MENU_CUSTOM1, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_CUSTOM3 */ MENU_NONE, MENU_CUSTOM1, MENU_CUSTOM2, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_PGM_FCN1 */ MENU_NONE, MENU_PGM_FCN2, MENU_PGM_FCN4, { { 0x1000 + CMD_LBL, 0, "" }, { 0x1000 + CMD_RTN, 0, "" }, { 0x1000 + CMD_INPUT, 0, "" }, { 0x1000 + CMD_VIEW, 0, "" }, { 0x1000 + CMD_AVIEW, 0, "" }, { 0x1000 + CMD_XEQ, 0, "" } } }, { /* MENU_PGM_FCN2 */ MENU_NONE, MENU_PGM_FCN3, MENU_PGM_FCN1, { { MENU_PGM_XCOMP0, 3, "X?0" }, { MENU_PGM_XCOMPY, 3, "X?Y" }, { 0x1000 + CMD_PROMPT, 0, "" }, { 0x1000 + CMD_PSE, 0, "" }, { 0x1000 + CMD_ISG, 0, "" }, { 0x1000 + CMD_DSE, 0, "" } } }, { /* MENU_PGM_FCN3 */ MENU_NONE, MENU_PGM_FCN4, MENU_PGM_FCN2, { { 0x1000 + CMD_AIP, 0, "" }, { 0x1000 + CMD_XTOA, 0, "" }, { 0x1000 + CMD_AGRAPH, 0, "" }, { 0x1000 + CMD_PIXEL, 0, "" }, { 0x1000 + CMD_BEEP, 0, "" }, { 0x1000 + CMD_TONE, 0, "" } } }, { /* MENU_PGM_FCN4 */ MENU_NONE, MENU_PGM_FCN1, MENU_PGM_FCN3, { { 0x1000 + CMD_MVAR, 0, "" }, { 0x1000 + CMD_VARMENU, 0, "" }, { 0x1000 + CMD_GETKEY, 0, "" }, { 0x1000 + CMD_MENU, 0, "" }, { 0x1000 + CMD_KEYG, 0, "" }, { 0x1000 + CMD_KEYX, 0, "" } } }, { /* MENU_PGM_XCOMP0 */ MENU_PGM_FCN2, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_X_EQ_0, 0, "" }, { 0x1000 + CMD_X_NE_0, 0, "" }, { 0x1000 + CMD_X_LT_0, 0, "" }, { 0x1000 + CMD_X_GT_0, 0, "" }, { 0x1000 + CMD_X_LE_0, 0, "" }, { 0x1000 + CMD_X_GE_0, 0, "" } } }, { /* MENU_PGM_XCOMPY */ MENU_PGM_FCN2, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_X_EQ_Y, 0, "" }, { 0x1000 + CMD_X_NE_Y, 0, "" }, { 0x1000 + CMD_X_LT_Y, 0, "" }, { 0x1000 + CMD_X_GT_Y, 0, "" }, { 0x1000 + CMD_X_LE_Y, 0, "" }, { 0x1000 + CMD_X_GE_Y, 0, "" } } }, { /* MENU_PRINT1 */ MENU_NONE, MENU_PRINT2, MENU_PRINT3, { { 0x1000 + CMD_PRSIGMA, 0, "" }, { 0x1000 + CMD_PRP, 0, "" }, { 0x1000 + CMD_PRV, 0, "" }, { 0x1000 + CMD_PRSTK, 0, "" }, { 0x1000 + CMD_PRA, 0, "" }, { 0x1000 + CMD_PRX, 0, "" } } }, { /* MENU_PRINT2 */ MENU_NONE, MENU_PRINT3, MENU_PRINT1, { { 0x1000 + CMD_PRUSR, 0, "" }, { 0x1000 + CMD_LIST, 0, "" }, { 0x1000 + CMD_ADV, 0, "" }, { 0x1000 + CMD_PRLCD, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_DELAY, 0, "" } } }, { /* MENU_PRINT3 */ MENU_NONE, MENU_PRINT1, MENU_PRINT2, { { 0x2000 + CMD_PON, 0, "" }, { 0x2000 + CMD_POFF, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x2000 + CMD_MAN, 0, "" }, { 0x2000 + CMD_NORM, 0, "" }, { 0x2000 + CMD_TRACE, 0, "" } } }, { /* MENU_TOP_FCN */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_SIGMAADD, 0, "" }, { 0x1000 + CMD_INV, 0, "" }, { 0x1000 + CMD_SQRT, 0, "" }, { 0x1000 + CMD_LOG, 0, "" }, { 0x1000 + CMD_LN, 0, "" }, { 0x1000 + CMD_XEQ, 0, "" } } }, { /* MENU_CATALOG */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_BLANK */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_PROGRAMMABLE */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_VARMENU */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" }, { 0, 0, "" } } }, { /* MENU_STAT1 */ MENU_NONE, MENU_STAT2, MENU_STAT2, { { 0x1000 + CMD_SIGMAADD, 0, "" }, { 0x1000 + CMD_SUM, 0, "" }, { 0x1000 + CMD_MEAN, 0, "" }, { 0x1000 + CMD_WMEAN, 0, "" }, { 0x1000 + CMD_SDEV, 0, "" }, { MENU_STAT_CFIT, 4, "CFIT" } } }, { /* MENU_STAT2 */ MENU_NONE, MENU_STAT1, MENU_STAT1, { { 0x2000 + CMD_ALLSIGMA, 0, "" }, { 0x2000 + CMD_LINSIGMA, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_SIGMAREG, 0, "" }, { 0x1000 + CMD_SIGMAREG_T, 0, "" } } }, { /* MENU_STAT_CFIT */ MENU_STAT1, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_FCSTX, 0, "" }, { 0x1000 + CMD_FCSTY, 0, "" }, { 0x1000 + CMD_SLOPE, 0, "" }, { 0x1000 + CMD_YINT, 0, "" }, { 0x1000 + CMD_CORR, 0, "" }, { MENU_STAT_MODL, 4, "MODL" } } }, { /* MENU_STAT_MODL */ MENU_STAT_CFIT, MENU_NONE, MENU_NONE, { { 0x2000 + CMD_LINF, 0, "" }, { 0x2000 + CMD_LOGF, 0, "" }, { 0x2000 + CMD_EXPF, 0, "" }, { 0x2000 + CMD_PWRF, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_BEST, 0, "" } } }, { /* MENU_MATRIX1 */ MENU_NONE, MENU_MATRIX2, MENU_MATRIX3, { { 0x1000 + CMD_NEWMAT, 0, "" }, { 0x1000 + CMD_INVRT, 0, "" }, { 0x1000 + CMD_DET, 0, "" }, { 0x1000 + CMD_TRANS, 0, "" }, { 0x1000 + CMD_SIMQ, 0, "" }, { 0x1000 + CMD_EDIT, 0, "" } } }, { /* MENU_MATRIX2 */ MENU_NONE, MENU_MATRIX3, MENU_MATRIX1, { { 0x1000 + CMD_DOT, 0, "" }, { 0x1000 + CMD_CROSS, 0, "" }, { 0x1000 + CMD_UVEC, 0, "" }, { 0x1000 + CMD_DIM, 0, "" }, { 0x1000 + CMD_INDEX, 0, "" }, { 0x1000 + CMD_EDITN, 0, "" } } }, { /* MENU_MATRIX3 */ MENU_NONE, MENU_MATRIX1, MENU_MATRIX2, { { 0x1000 + CMD_STOIJ, 0, "" }, { 0x1000 + CMD_RCLIJ, 0, "" }, { 0x1000 + CMD_STOEL, 0, "" }, { 0x1000 + CMD_RCLEL, 0, "" }, { 0x1000 + CMD_PUTM, 0, "" }, { 0x1000 + CMD_GETM, 0, "" } } }, { /* MENU_MATRIX_SIMQ */ MENU_MATRIX1, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_MATA, 0, "" }, { 0x1000 + CMD_MATB, 0, "" }, { 0x1000 + CMD_MATX, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" } } }, { /* MENU_MATRIX_EDIT1 */ MENU_NONE, MENU_MATRIX_EDIT2, MENU_MATRIX_EDIT2, { { 0x1000 + CMD_LEFT, 0, "" }, { 0x1000 + CMD_OLD, 0, "" }, { 0x1000 + CMD_UP, 0, "" }, { 0x1000 + CMD_DOWN, 0, "" }, { 0x1000 + CMD_GOTOROW, 0, "" }, { 0x1000 + CMD_RIGHT, 0, "" } } }, { /* MENU_MATRIX_EDIT2 */ MENU_NONE, MENU_MATRIX_EDIT1, MENU_MATRIX_EDIT1, { { 0x1000 + CMD_INSR, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_DELR, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x2000 + CMD_WRAP, 0, "" }, { 0x2000 + CMD_GROW, 0, "" } } }, { /* MENU_BASE */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_A_THRU_F, 0, "" }, { 0x2000 + CMD_HEXM, 0, "" }, { 0x2000 + CMD_DECM, 0, "" }, { 0x2000 + CMD_OCTM, 0, "" }, { 0x2000 + CMD_BINM, 0, "" }, { MENU_BASE_LOGIC, 5, "LOGIC" } } }, { /* MENU_BASE_A_THRU_F */ MENU_BASE, MENU_NONE, MENU_NONE, { { 0, 1, "A" }, { 0, 1, "B" }, { 0, 1, "C" }, { 0, 1, "D" }, { 0, 1, "E" }, { 0, 1, "F" } } }, { /* MENU_BASE_LOGIC */ MENU_BASE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_AND, 0, "" }, { 0x1000 + CMD_OR, 0, "" }, { 0x1000 + CMD_XOR, 0, "" }, { 0x1000 + CMD_NOT, 0, "" }, { 0x1000 + CMD_BIT_T, 0, "" }, { 0x1000 + CMD_ROTXY, 0, "" } } }, { /* MENU_SOLVE */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_MVAR, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_PGMSLV, 0, "" }, { 0x1000 + CMD_SOLVE, 0, "" } } }, { /* MENU_INTEG */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0x1000 + CMD_MVAR, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_PGMINT, 0, "" }, { 0x1000 + CMD_INTEG, 0, "" } } }, { /* MENU_INTEG_PARAMS */ MENU_NONE, MENU_NONE, MENU_NONE, { { 0, 4, "LLIM" }, { 0, 4, "ULIM" }, { 0, 3, "ACC" }, { 0x1000 + CMD_NULL, 0, "" }, { 0x1000 + CMD_NULL, 0, "" }, { 0, 1, "\003" } } } }; /* By how much do the variables, programs, and labels * arrays grow when they are full */ #define VARS_INCREMENT 25 #define PRGMS_INCREMENT 10 #define LABELS_INCREMENT 10 /* Registers */ vartype *reg_x = NULL; vartype *reg_y = NULL; vartype *reg_z = NULL; vartype *reg_t = NULL; vartype *reg_lastx = NULL; int reg_alpha_length = 0; char reg_alpha[44]; /* Flags */ flags_struct flags; /* Variables */ int vars_capacity = 0; int vars_count = 0; var_struct *vars = NULL; /* Programs */ int prgms_capacity = 0; int prgms_count = 0; prgm_struct *prgms = NULL; int labels_capacity = 0; int labels_count = 0; label_struct *labels = NULL; int current_prgm = -1; int4 pc; int prgm_highlight_row = 0; int varmenu_length; char varmenu[7]; int varmenu_rows; int varmenu_row; int varmenu_labellength[6]; char varmenu_labeltext[6][7]; int varmenu_role; bool mode_clall; int (*mode_interruptible)(int) = NULL; bool mode_stoppable; bool mode_command_entry; bool mode_number_entry; bool mode_alpha_entry; bool mode_shift; int mode_appmenu; int mode_plainmenu; bool mode_plainmenu_sticky; int mode_transientmenu; int mode_alphamenu; int mode_commandmenu; bool mode_running; bool mode_getkey; bool mode_pause = false; bool mode_disable_stack_lift; /* transient */ bool mode_varmenu; bool mode_updown; int4 mode_sigma_reg; int mode_goose; bool mode_time_clktd; bool mode_time_clk24; bool mode_time_dmy; phloat entered_number; int entered_string_length; char entered_string[15]; int pending_command; arg_struct pending_command_arg; int xeq_invisible; /* Multi-keystroke commands -- edit state */ /* Relevant when mode_command_entry != 0 */ int incomplete_command; int incomplete_ind; int incomplete_alpha; int incomplete_length; int incomplete_maxdigits; int incomplete_argtype; int incomplete_num; char incomplete_str[7]; int4 incomplete_saved_pc; int4 incomplete_saved_highlight_row; /* Command line handling temporaries */ char cmdline[100]; int cmdline_length; int cmdline_row; /* Matrix editor / matrix indexing */ int matedit_mode; /* 0=off, 1=index, 2=edit, 3=editn */ char matedit_name[7]; int matedit_length; vartype *matedit_x; int4 matedit_i; int4 matedit_j; int matedit_prev_appmenu; /* INPUT */ char input_name[11]; int input_length; arg_struct input_arg; /* BASE application */ int baseapp = 0; /* Random number generator */ phloat random_number; /* NORM & TRACE mode: number waiting to be printed */ int deferred_print = 0; /* Keystroke buffer - holds keystrokes received while * there is a program running. */ int keybuf_head = 0; int keybuf_tail = 0; int keybuf[16]; int remove_program_catalog = 0; bool bin_dec_mode_switch; bool state_file_has_old_bcd; /* No user interaction: we keep track of whether or not the user * has pressed any keys since powering up, and we don't allow * programmatic OFF until they have. The reason is that we want * to prevent nastiness like * * LBL "YIKES" SF 11 OFF GTO "YIKES" * * from locking the user out. */ bool no_keystrokes_yet; /*******************/ /* Private globals */ /*******************/ static bool state_bool_is_int; #define MAX_RTNS 8 static int rtn_sp = 0; static int rtn_prgm[MAX_RTNS]; static int4 rtn_pc[MAX_RTNS]; #ifdef IPHONE /* For iPhone, we disable OFF by default, to satisfy App Store * policy, but we allow users to enable it using a magic value * in the X register. This flag determines OFF behavior. */ static bool off_enable_flag = false; #endif typedef struct { int type; int4 rows; int4 columns; } matrix_persister; static int array_count; static int array_list_capacity; static void **array_list; static bool read_int(int *n); static bool write_int(int n); static bool read_int4(int4 *n); static bool write_int4(int4 n); static bool read_bool(bool *n); static bool write_bool(bool n); static bool array_list_grow(); static int array_list_search(void *array); static bool persist_vartype(vartype *v); static bool unpersist_vartype(vartype **v); static void update_label_table(int prgm, int4 pc, int inserted); static void invalidate_lclbls(int prgm_index); static int pc_line_convert(int4 loc, int loc_is_pc); static bool convert_programs(); #ifdef IPHONE static void convert_bigstack_drop(); #endif static bool array_list_grow() { if (array_count < array_list_capacity) return true; array_list_capacity += 10; void **p = (void **) realloc(array_list, array_list_capacity * sizeof(void *)); if (p == NULL) return false; array_list = p; return true; } static int array_list_search(void *array) { for (int i = 0; i < array_count; i++) if (array_list[i] == array) return i; return -1; } static bool persist_vartype(vartype *v) { if (v == NULL) { int type = TYPE_NULL; return shell_write_saved_state(&type, sizeof(int)); } switch (v->type) { case TYPE_REAL: return shell_write_saved_state(v, sizeof(vartype_real)); case TYPE_COMPLEX: return shell_write_saved_state(v, sizeof(vartype_complex)); case TYPE_STRING: return shell_write_saved_state(v, sizeof(vartype_string)); case TYPE_REALMATRIX: { matrix_persister mp; vartype_realmatrix *rm = (vartype_realmatrix *) v; mp.type = rm->type; mp.rows = rm->rows; mp.columns = rm->columns; int4 size = mp.rows * mp.columns; bool must_write = true; if (rm->array->refcount > 1) { int n = array_list_search(rm->array); if (n == -1) { // A negative row count signals a new shared matrix mp.rows = -mp.rows; if (!array_list_grow()) return false; array_list[array_count++] = rm->array; } else { // A zero row count means this matrix shares its data // with a previously written matrix mp.rows = 0; mp.columns = n; must_write = false; } } if (!shell_write_saved_state(&mp, sizeof(matrix_persister))) return false; if (must_write) { if (!shell_write_saved_state(rm->array->data, size * sizeof(phloat))) return false; if (!shell_write_saved_state(rm->array->is_string, size)) return false; } return true; } case TYPE_COMPLEXMATRIX: { matrix_persister mp; vartype_complexmatrix *cm = (vartype_complexmatrix *) v; mp.type = cm->type; mp.rows = cm->rows; mp.columns = cm->columns; int4 size = mp.rows * mp.columns; bool must_write = true; if (cm->array->refcount > 1) { int n = array_list_search(cm->array); if (n == -1) { // A negative row count signals a new shared matrix mp.rows = -mp.rows; if (!array_list_grow()) return false; array_list[array_count++] = cm->array; } else { // A zero row count means this matrix shares its data // with a previously written matrix mp.rows = 0; mp.columns = n; must_write = false; } } if (!shell_write_saved_state(&mp, sizeof(matrix_persister))) return false; if (must_write) { if (!shell_write_saved_state(cm->array->data, 2 * size * sizeof(phloat))) return false; } return true; } default: /* Should not happen */ return true; } } // A few declarations to help unpersist_vartype get the offsets right, // in the case it needs to convert the state file. struct fake_bcd { short d_[P+1]; }; struct bin_real { int type; double x; }; struct bin_complex { int type; double re, im; }; struct dec_real { int type; fake_bcd x; }; struct dec_complex { int type; fake_bcd re, im; }; static bool unpersist_vartype(vartype **v) { int type; if (shell_read_saved_state(&type, sizeof(int)) != sizeof(int)) return false; switch (type) { case TYPE_NULL: { *v = NULL; return true; } case TYPE_REAL: { vartype_real *r = (vartype_real *) new_real(0); if (r == NULL) return false; if (bin_dec_mode_switch) { #ifdef BCD_MATH int n = sizeof(bin_real) - sizeof(int); bin_real br; if (shell_read_saved_state(&br.type + 1, n) != n) { free_vartype((vartype *) r); return false; } r->x = br.x; #else int n = sizeof(dec_real) - sizeof(int); dec_real dr; if (shell_read_saved_state(&dr.type + 1, n) != n) { free_vartype((vartype *) r); return false; } r->x = bcd2double(dr.x.d_, state_file_has_old_bcd); #endif } else { int n = sizeof(vartype_real) - sizeof(int); if (shell_read_saved_state(&r->type + 1, n) != n) { free_vartype((vartype *) r); return false; } #ifdef BCD_MATH if (state_file_has_old_bcd) bcdfloat_old2new(r->x.bcd.d_); #endif } *v = (vartype *) r; return true; } case TYPE_COMPLEX: { vartype_complex *c = (vartype_complex *) new_complex(0, 0); if (c == NULL) return false; if (bin_dec_mode_switch) { #ifdef BCD_MATH int n = sizeof(bin_complex) - sizeof(int); bin_complex bc; if (shell_read_saved_state(&bc.type + 1, n) != n) { free_vartype((vartype *) c); return false; } c->re = bc.re; c->im = bc.im; #else int n = sizeof(dec_complex) - sizeof(int); dec_complex dc; if (shell_read_saved_state(&dc.type + 1, n) != n) { free_vartype((vartype *) c); return false; } c->re = bcd2double(dc.re.d_, state_file_has_old_bcd); c->im = bcd2double(dc.im.d_, state_file_has_old_bcd); #endif } else { int n = sizeof(vartype_complex) - sizeof(int); if (shell_read_saved_state(&c->type + 1, n) != n) { free_vartype((vartype *) c); return false; } #ifdef BCD_MATH if (state_file_has_old_bcd) { bcdfloat_old2new(c->re.bcd.d_); bcdfloat_old2new(c->im.bcd.d_); } #endif } *v = (vartype *) c; return true; } case TYPE_STRING: { vartype_string *s = (vartype_string *) new_string("", 0); int n = sizeof(vartype_string) - sizeof(int); if (s == NULL) return false; if (shell_read_saved_state(&s->type + 1, n) != n) { free_vartype((vartype *) s); return false; } else { *v = (vartype *) s; return true; } } case TYPE_REALMATRIX: { matrix_persister mp; int n = sizeof(matrix_persister) - sizeof(int); if (shell_read_saved_state(&mp.type + 1, n) != n) return false; if (mp.rows == 0) { // Shared matrix vartype *m = new_matrix_alias((vartype *) array_list[mp.columns]); if (m == NULL) return false; else { *v = m; return true; } } bool shared = mp.rows < 0; if (shared) mp.rows = -mp.rows; vartype_realmatrix *rm = (vartype_realmatrix *) new_realmatrix(mp.rows, mp.columns); if (rm == NULL) return false; if (bin_dec_mode_switch) { int4 size = mp.rows * mp.columns; #ifdef BCD_MATH int phsz = sizeof(double); #else int phsz = sizeof(fake_bcd); #endif int4 tsz = size * phsz; char *temp = (char *) malloc(tsz); if (temp == NULL) { free_vartype((vartype *) rm); return false; } if (shell_read_saved_state(temp, tsz) != tsz) { free(temp); free_vartype((vartype *) rm); return false; } if (shell_read_saved_state(rm->array->is_string, size) != size) { free(temp); free_vartype((vartype *) rm); return false; } #ifdef BCD_MATH for (int4 i = 0; i < size; i++) { if (rm->array->is_string[i]) { char *src = temp + i * phsz; char *dst = (char *) (rm->array->data + i); for (int j = 0; j < 7; j++) *dst++ = *src++; } else { rm->array->data[i] = ((double *) temp)[i]; } } #else for (int4 i = 0; i < size; i++) { if (rm->array->is_string[i]) { char *src = temp + i * phsz; char *dst = (char *) (rm->array->data + i); for (int j = 0; j < 7; j++) *dst++ = *src++; } else { rm->array->data[i] = bcd2double((short *) (temp + phsz * i), state_file_has_old_bcd); } } #endif free(temp); } else { int4 size = mp.rows * mp.columns * sizeof(phloat); if (shell_read_saved_state(rm->array->data, size) != size) { free_vartype((vartype *) rm); return false; } size = mp.rows * mp.columns; if (shell_read_saved_state(rm->array->is_string, size) != size) { free_vartype((vartype *) rm); return false; } #ifdef BCD_MATH if (state_file_has_old_bcd) for (int4 i = 0; i < size; i++) if (!rm->array->is_string[i]) bcdfloat_old2new(rm->array->data[i].bcd.d_); #endif } if (shared) { if (!array_list_grow()) { free_vartype((vartype *) rm); return false; } array_list[array_count++] = rm; } *v = (vartype *) rm; return true; } case TYPE_COMPLEXMATRIX: { matrix_persister mp; int n = sizeof(matrix_persister) - sizeof(int); if (shell_read_saved_state(&mp.type + 1, n) != n) return false; if (mp.rows == 0) { // Shared matrix vartype *m = new_matrix_alias((vartype *) array_list[mp.columns]); if (m == NULL) return false; else { *v = m; return true; } } bool shared = mp.rows < 0; if (shared) mp.rows = -mp.rows; vartype_complexmatrix *cm = (vartype_complexmatrix *) new_complexmatrix(mp.rows, mp.columns); if (cm == NULL) return false; if (bin_dec_mode_switch) { int4 size = 2 * mp.rows * mp.columns; for (int4 i = 0; i < size; i++) if (!read_phloat(cm->array->data + i)) { free_vartype((vartype *) cm); return false; } } else { int4 size = 2 * mp.rows * mp.columns * sizeof(phloat); if (shell_read_saved_state(cm->array->data, size) != size) { free_vartype((vartype *) cm); return false; } #ifdef BCD_MATH if (state_file_has_old_bcd) for (int4 i = 0; i < size; i++) bcdfloat_old2new(cm->array->data[i].bcd.d_); #endif } if (shared) { if (!array_list_grow()) { free_vartype((vartype *) cm); return false; } array_list[array_count++] = cm; } *v = (vartype *) cm; return true; } default: return false; } } static bool persist_globals() { int i; array_count = 0; array_list_capacity = 0; array_list = NULL; bool ret = false; if (!persist_vartype(reg_x)) goto done; if (!persist_vartype(reg_y)) goto done; if (!persist_vartype(reg_z)) goto done; if (!persist_vartype(reg_t)) goto done; if (!persist_vartype(reg_lastx)) goto done; if (!write_bool(false)) /* No, big stack block does not exist */ goto done; if (!write_int(reg_alpha_length)) goto done; if (!shell_write_saved_state(reg_alpha, 44)) goto done; if (!write_int4(mode_sigma_reg)) goto done; if (!write_int(mode_goose)) goto done; if (!write_bool(mode_time_clktd)) goto done; if (!write_bool(mode_time_clk24)) goto done; if (!write_bool(mode_time_dmy)) goto done; if (!shell_write_saved_state(&flags, sizeof(flags_struct))) goto done; if (!write_int(vars_count)) goto done; if (!shell_write_saved_state(vars, vars_count * sizeof(var_struct))) goto done; for (i = 0; i < vars_count; i++) if (!persist_vartype(vars[i].value)) goto done; if (!write_int(prgms_count)) goto done; if (!shell_write_saved_state(prgms, prgms_count * sizeof(prgm_struct))) goto done; for (i = 0; i < prgms_count; i++) if (!shell_write_saved_state(prgms[i].text, prgms[i].size)) goto done; if (!write_int(current_prgm)) goto done; if (!write_int4(pc)) goto done; if (!write_int(prgm_highlight_row)) goto done; if (!write_int(varmenu_length)) goto done; if (!shell_write_saved_state(varmenu, 7)) goto done; if (!write_int(varmenu_rows)) goto done; if (!write_int(varmenu_row)) goto done; if (!shell_write_saved_state(varmenu_labellength, 6 * sizeof(int))) goto done; if (!shell_write_saved_state(varmenu_labeltext, 42)) goto done; if (!write_int(varmenu_role)) goto done; if (!write_int(rtn_sp)) goto done; if (!shell_write_saved_state(&rtn_prgm, MAX_RTNS * sizeof(int))) goto done; if (!shell_write_saved_state(&rtn_pc, MAX_RTNS * sizeof(int4))) goto done; #ifdef IPHONE if (!write_bool(off_enable_flag)) goto done; #endif ret = true; done: free(array_list); return ret; } static bool unpersist_globals(int4 ver) { int4 n; int i; array_count = 0; array_list_capacity = 0; array_list = NULL; bool ret = false; free_vartype(reg_x); if (!unpersist_vartype(®_x)) goto done; free_vartype(reg_y); if (!unpersist_vartype(®_y)) goto done; free_vartype(reg_z); if (!unpersist_vartype(®_z)) goto done; free_vartype(reg_t); if (!unpersist_vartype(®_t)) goto done; free_vartype(reg_lastx); if (!unpersist_vartype(®_lastx)) goto done; if (ver >= 12) { /* BIGSTACK -- iphone only; no longer used in Free42 proper */ bool bigstack; if (!read_bool(&bigstack)) goto done; } if (!read_int(®_alpha_length)) { reg_alpha_length = 0; goto done; } if (shell_read_saved_state(reg_alpha, 44) != 44) { reg_alpha_length = 0; goto done; } if (!read_int4(&mode_sigma_reg)) { mode_sigma_reg = 11; goto done; } if (!read_int(&mode_goose)) { mode_goose = -1; goto done; } if (ver >= 16) { if (!read_bool(&mode_time_clktd)) { mode_time_clktd = false; goto done; } if (!read_bool(&mode_time_clk24)) { mode_time_clk24 = false; goto done; } if (!read_bool(&mode_time_dmy)) { mode_time_dmy = false; goto done; } } if (shell_read_saved_state(&flags, sizeof(flags_struct)) != sizeof(flags_struct)) goto done; vars_capacity = 0; if (vars != NULL) { free(vars); vars = NULL; } if (!read_int(&vars_count)) { vars_count = 0; goto done; } n = vars_count * sizeof(var_struct); vars = (var_struct *) malloc(n); if (vars == NULL) { vars_count = 0; goto done; } if (shell_read_saved_state(vars, n) != n) { free(vars); vars = NULL; vars_count = 0; goto done; } vars_capacity = vars_count; for (i = 0; i < vars_count; i++) vars[i].value = NULL; for (i = 0; i < vars_count; i++) if (!unpersist_vartype(&vars[i].value)) { purge_all_vars(); goto done; } prgms_capacity = 0; if (prgms != NULL) { free(prgms); prgms = NULL; } if (!read_int(&prgms_count)) { prgms_count = 0; goto done; } n = prgms_count * sizeof(prgm_struct); prgms = (prgm_struct *) malloc(n); if (prgms == NULL) { prgms_count = 0; goto done; } if (shell_read_saved_state(prgms, n) != n) { free(prgms); prgms = NULL; prgms_count = 0; goto done; } prgms_capacity = prgms_count; for (i = 0; i < prgms_count; i++) { prgms[i].capacity = prgms[i].size; prgms[i].text = (unsigned char *) malloc(prgms[i].size); // TODO - handle memory allocation failure } for (i = 0; i < prgms_count; i++) { if (shell_read_saved_state(prgms[i].text, prgms[i].size) != prgms[i].size) { clear_all_prgms(); goto done; } } if (!read_int(¤t_prgm)) { current_prgm = 0; goto done; } if (!read_int4(&pc)) { pc = -1; goto done; } if (!read_int(&prgm_highlight_row)) { prgm_highlight_row = 0; goto done; } if (!read_int(&varmenu_length)) { varmenu_length = 0; goto done; } if (shell_read_saved_state(varmenu, 7) != 7) { varmenu_length = 0; goto done; } if (!read_int(&varmenu_rows)) { varmenu_length = 0; goto done; } if (!read_int(&varmenu_row)) { varmenu_length = 0; goto done; } if (shell_read_saved_state(varmenu_labellength, 6 * sizeof(int)) != 6 * sizeof(int)) goto done; if (shell_read_saved_state(varmenu_labeltext, 42) != 42) goto done; if (!read_int(&varmenu_role)) goto done; if (!read_int(&rtn_sp)) goto done; if (shell_read_saved_state(rtn_prgm, MAX_RTNS * sizeof(int)) != MAX_RTNS * sizeof(int)) goto done; if (shell_read_saved_state(rtn_pc, MAX_RTNS * sizeof(int4)) != MAX_RTNS * sizeof(int4)) goto done; #ifdef IPHONE if (ver >= 17) if (!read_bool(&off_enable_flag)) goto done; #endif if (bin_dec_mode_switch) if (!convert_programs()) { clear_all_prgms(); goto done; } #ifdef IPHONE if (ver == 12 || ver == 13) { // CMD_DROP redefined from 315 to 329, to resolve clash with // Underhill's COPAN extensions. convert_bigstack_drop(); } #endif rebuild_label_table(); ret = true; done: free(array_list); return ret; } void clear_all_prgms() { if (prgms != NULL) { int i; for (i = 0; i < prgms_count; i++) if (prgms[i].text != NULL) free(prgms[i].text); free(prgms); } prgms = NULL; prgms_capacity = 0; prgms_count = 0; if (labels != NULL) free(labels); labels = NULL; labels_capacity = 0; labels_count = 0; } int clear_prgm(const arg_struct *arg) { int prgm_index; if (arg->type == ARGTYPE_LBLINDEX) prgm_index = labels[arg->val.num].prgm; else if (arg->type == ARGTYPE_STR) { if (arg->length == 0) { if (current_prgm < 0 || current_prgm >= prgms_count) return ERR_INTERNAL_ERROR; prgm_index = current_prgm; } else { int i; for (i = labels_count - 1; i >= 0; i--) if (string_equals(arg->val.text, arg->length, labels[i].name, labels[i].length)) goto found; return ERR_LABEL_NOT_FOUND; found: prgm_index = labels[i].prgm; } } return clear_prgm_by_index(prgm_index); } int clear_prgm_by_index(int prgm_index) { int i, j; if (prgm_index < 0 || prgm_index >= prgms_count) return ERR_LABEL_NOT_FOUND; clear_all_rtns(); if (prgm_index == current_prgm) pc = -1; else if (current_prgm > prgm_index) current_prgm--; free(prgms[prgm_index].text); for (i = prgm_index; i < prgms_count - 1; i++) prgms[i] = prgms[i + 1]; prgms_count--; i = j = 0; while (j < labels_count) { if (j > i) labels[i] = labels[j]; j++; if (labels[i].prgm > prgm_index) { labels[i].prgm--; i++; } else if (labels[i].prgm < prgm_index) i++; } labels_count = i; if (prgms_count == 0 || prgm_index == prgms_count) { int saved_prgm = current_prgm; int saved_pc = pc; goto_dot_dot(); current_prgm = saved_prgm; pc = saved_pc; } update_catalog(); return ERR_NONE; } void clear_prgm_lines(int4 count) { int4 frompc, deleted, i, j; if (pc == -1) pc = 0; frompc = pc; while (count > 0) { int command; arg_struct arg; get_next_command(&pc, &command, &arg, 0); if (command == CMD_END) { pc -= 2; break; } count--; } deleted = pc - frompc; for (i = pc; i < prgms[current_prgm].size; i++) prgms[current_prgm].text[i - deleted] = prgms[current_prgm].text[i]; prgms[current_prgm].size -= deleted; pc = frompc; i = j = 0; while (j < labels_count) { if (j > i) labels[i] = labels[j]; j++; if (labels[i].prgm == current_prgm) { if (labels[i].pc < frompc) i++; else if (labels[i].pc >= frompc + deleted) { labels[i].pc -= deleted; i++; } } else i++; } labels_count = i; invalidate_lclbls(current_prgm); clear_all_rtns(); } void goto_dot_dot() { int command; arg_struct arg; if (prgms_count != 0) { /* Check if last program is empty */ pc = 0; current_prgm = prgms_count - 1; get_next_command(&pc, &command, &arg, 0); if (command == CMD_END) { pc = -1; return; } } if (prgms_count == prgms_capacity) { prgm_struct *newprgms; int i; prgms_capacity += 10; newprgms = (prgm_struct *) malloc(prgms_capacity * sizeof(prgm_struct)); // TODO - handle memory allocation failure for (i = 0; i < prgms_count; i++) newprgms[i] = prgms[i]; if (prgms != NULL) free(prgms); prgms = newprgms; } current_prgm = prgms_count++; prgms[current_prgm].capacity = 0; prgms[current_prgm].size = 0; prgms[current_prgm].lclbl_invalid = 1; prgms[current_prgm].text = NULL; command = CMD_END; arg.type = ARGTYPE_NONE; store_command(0, command, &arg); pc = -1; } int mvar_prgms_exist() { int i; for (i = 0; i < labels_count; i++) if (label_has_mvar(i)) return 1; return 0; } int label_has_mvar(int lblindex) { int saved_prgm; int4 pc; int command; arg_struct arg; if (labels[lblindex].length == 0) return 0; saved_prgm = current_prgm; current_prgm = labels[lblindex].prgm; pc = labels[lblindex].pc; pc += get_command_length(current_prgm, pc); get_next_command(&pc, &command, &arg, 0); current_prgm = saved_prgm; return command == CMD_MVAR; } int get_command_length(int prgm_index, int4 pc) { prgm_struct *prgm = prgms + prgm_index; int4 pc2 = pc; int command = prgm->text[pc2++]; int argtype = prgm->text[pc2++]; command |= (argtype & 240) << 4; argtype &= 15; if ((command == CMD_GTO || command == CMD_XEQ) && (argtype == ARGTYPE_NUM || argtype == ARGTYPE_LCLBL)) pc2 += 4; switch (argtype) { case ARGTYPE_NUM: case ARGTYPE_NEG_NUM: case ARGTYPE_IND_NUM: { while ((prgm->text[pc2++] & 128) == 0); break; } case ARGTYPE_STK: case ARGTYPE_IND_STK: case ARGTYPE_COMMAND: case ARGTYPE_LCLBL: pc2++; break; case ARGTYPE_STR: case ARGTYPE_IND_STR: { pc2 += prgm->text[pc2] + 1; break; } case ARGTYPE_DOUBLE: pc2 += sizeof(phloat); break; } return pc2 - pc; } void get_next_command(int4 *pc, int *command, arg_struct *arg, int find_target){ prgm_struct *prgm = prgms + current_prgm; int i; int4 target_pc; int4 orig_pc = *pc; *command = prgm->text[(*pc)++]; arg->type = prgm->text[(*pc)++]; *command |= (arg->type & 240) << 4; arg->type &= 15; if ((*command == CMD_GTO || *command == CMD_XEQ) && (arg->type == ARGTYPE_NUM || arg->type == ARGTYPE_LCLBL)) { if (find_target) { target_pc = 0; for (i = 0; i < 4; i++) target_pc = (target_pc << 8) | prgm->text[(*pc)++]; if (target_pc != -1) { arg->target = target_pc; find_target = 0; } } else (*pc) += 4; } else { find_target = 0; arg->target = -1; } switch (arg->type) { case ARGTYPE_NUM: case ARGTYPE_NEG_NUM: case ARGTYPE_IND_NUM: { int4 num = 0; unsigned char c; do { c = prgm->text[(*pc)++]; num = (num << 7) | (c & 127); } while ((c & 128) == 0); if (arg->type == ARGTYPE_NEG_NUM) { arg->type = ARGTYPE_NUM; num = -num; } arg->val.num = num; break; } case ARGTYPE_STK: case ARGTYPE_IND_STK: arg->val.stk = prgm->text[(*pc)++]; break; case ARGTYPE_COMMAND: arg->val.cmd = prgm->text[(*pc)++]; break; case ARGTYPE_LCLBL: arg->val.lclbl = prgm->text[(*pc)++]; break; case ARGTYPE_STR: case ARGTYPE_IND_STR: { arg->length = prgm->text[(*pc)++]; for (i = 0; i < arg->length; i++) arg->val.text[i] = prgm->text[(*pc)++]; break; } case ARGTYPE_DOUBLE: { unsigned char *b = (unsigned char *) &arg->val_d; for (int i = 0; i < (int) sizeof(phloat); i++) *b++ = prgm->text[(*pc)++]; break; } } if (*command == CMD_NUMBER && arg->type != ARGTYPE_DOUBLE) { /* argtype is ARGTYPE_NUM; convert to phloat */ arg->val_d = arg->val.num; arg->type = ARGTYPE_DOUBLE; } if (find_target) { target_pc = find_local_label(arg); arg->target = target_pc; for (i = 5; i >= 2; i--) { prgm->text[orig_pc + i] = target_pc; target_pc >>= 8; } prgm->lclbl_invalid = 0; } } void rebuild_label_table() { /* TODO -- this is *not* efficient; inserting and deleting ENDs and * global LBLs should not cause every single program to get rescanned! * But, I don't feel like dealing with that at the moment, so just * this ugly brute force approach for now. */ int prgm_index; int4 pc; labels_count = 0; for (prgm_index = 0; prgm_index < prgms_count; prgm_index++) { prgm_struct *prgm = prgms + prgm_index; pc = 0; while (pc < prgm->size) { int command = prgm->text[pc]; int argtype = prgm->text[pc + 1]; command |= (argtype & 240) << 4; argtype &= 15; if (command == CMD_END || (command == CMD_LBL && argtype == ARGTYPE_STR)) { label_struct *newlabel; if (labels_count == labels_capacity) { label_struct *newlabels; int i; labels_capacity += 50; newlabels = (label_struct *) malloc(labels_capacity * sizeof(label_struct)); // TODO - handle memory allocation failure for (i = 0; i < labels_count; i++) newlabels[i] = labels[i]; if (labels != NULL) free(labels); labels = newlabels; } newlabel = labels + labels_count++; if (command == CMD_END) newlabel->length = 0; else { int i; newlabel->length = prgm->text[pc + 2]; for (i = 0; i < newlabel->length; i++) newlabel->name[i] = prgm->text[pc + 3 + i]; } newlabel->prgm = prgm_index; newlabel->pc = pc; } pc += get_command_length(prgm_index, pc); } } } static void update_label_table(int prgm, int4 pc, int inserted) { int i; for (i = 0; i < labels_count; i++) { if (labels[i].prgm > prgm) return; if (labels[i].prgm == prgm && labels[i].pc >= pc) labels[i].pc += inserted; } } static void invalidate_lclbls(int prgm_index) { prgm_struct *prgm = prgms + prgm_index; if (!prgm->lclbl_invalid) { int4 pc2 = 0; while (pc2 < prgm->size) { int command = prgm->text[pc2]; int argtype = prgm->text[pc2 + 1]; command |= (argtype & 240) << 4; argtype &= 15; if ((command == CMD_GTO || command == CMD_XEQ) && (argtype == ARGTYPE_NUM || argtype == ARGTYPE_LCLBL)) { /* A dest_pc value of -1 signals 'unknown', * -2 means 'nonexistent', and anything else is * the pc where the destination label is found. */ int4 pos; for (pos = pc2 + 2; pos < pc2 + 6; pos++) prgm->text[pos] = 255; } pc2 += get_command_length(prgm_index, pc2); } prgm->lclbl_invalid = 1; } } void delete_command(int4 pc) { prgm_struct *prgm = prgms + current_prgm; int command = prgm->text[pc]; int argtype = prgm->text[pc + 1]; int length = get_command_length(current_prgm, pc); int4 pos; command |= (argtype & 240) << 4; argtype &= 15; if (command == CMD_END) { int4 newsize; prgm_struct *nextprgm; if (current_prgm == prgms_count - 1) /* Don't allow deletion of last program's END. */ return; nextprgm = prgm + 1; prgm->size -= 2; newsize = prgm->size + nextprgm->size; if (newsize > prgm->capacity) { int4 newcapacity = (newsize + 511) & ~511; unsigned char *newtext = (unsigned char *) malloc(newcapacity); // TODO - handle memory allocation failure for (pos = 0; pos < prgm->size; pos++) newtext[pos] = prgm->text[pos]; free(prgm->text); prgm->text = newtext; prgm->capacity = newcapacity; } for (pos = 0; pos < nextprgm->size; pos++) prgm->text[prgm->size++] = nextprgm->text[pos]; free(nextprgm->text); for (pos = current_prgm + 1; pos < prgms_count - 1; pos++) prgms[pos] = prgms[pos + 1]; prgms_count--; rebuild_label_table(); invalidate_lclbls(current_prgm); clear_all_rtns(); draw_varmenu(); return; } for (pos = pc; pos < prgm->size - length; pos++) prgm->text[pos] = prgm->text[pos + length]; prgm->size -= length; if (command == CMD_LBL && argtype == ARGTYPE_STR) rebuild_label_table(); else update_label_table(current_prgm, pc, -length); invalidate_lclbls(current_prgm); clear_all_rtns(); draw_varmenu(); } void store_command(int4 pc, int command, arg_struct *arg) { unsigned char buf[100]; int bufptr = 0; int i; int4 pos; prgm_struct *prgm = prgms + current_prgm; /* We should never be called with pc = -1, but just to be safe... */ if (pc == -1) pc = 0; if (arg->type == ARGTYPE_NUM && arg->val.num < 0) { arg->type = ARGTYPE_NEG_NUM; arg->val.num = -arg->val.num; } else if (command == CMD_NUMBER) { /* arg.type is always ARGTYPE_DOUBLE for CMD_NUMBER, but for storage * efficiency, we handle integers specially and store them as * ARGTYPE_NUM or ARGTYPE_NEG_NUM instead. */ int4 n = to_int4(arg->val_d); if (n == arg->val_d && n != (int4) 0x80000000) { if (n >= 0) { arg->val.num = n; arg->type = ARGTYPE_NUM; } else { arg->val.num = -n; arg->type = ARGTYPE_NEG_NUM; } } } else if (arg->type == ARGTYPE_LBLINDEX) { int li = arg->val.num; arg->length = labels[li].length; for (i = 0; i < arg->length; i++) arg->val.text[i] = labels[li].name[i]; arg->type = ARGTYPE_STR; } buf[bufptr++] = command & 255; buf[bufptr++] = arg->type | ((command & ~255) >> 4); /* If the program is nonempty, it must already contain an END, * since that's the very first thing that gets stored in any new * program. In this case, we need to split the program. */ if (command == CMD_END && prgm->size > 0) { prgm_struct *new_prgm; if (prgms_count == prgms_capacity) { prgm_struct *new_prgms; int i; prgms_capacity += 10; new_prgms = (prgm_struct *) malloc(prgms_capacity * sizeof(prgm_struct)); // TODO - handle memory allocation failure for (i = 0; i <= current_prgm; i++) new_prgms[i] = prgms[i]; for (i = current_prgm + 1; i < prgms_count; i++) new_prgms[i + 1] = prgms[i]; free(prgms); prgms = new_prgms; prgm = prgms + current_prgm; } else { for (i = prgms_count - 1; i > current_prgm; i--) prgms[i + 1] = prgms[i]; } prgms_count++; new_prgm = prgm + 1; new_prgm->size = prgm->size - pc; new_prgm->capacity = (new_prgm->size + 511) & ~511; new_prgm->text = (unsigned char *) malloc(new_prgm->capacity); // TODO - handle memory allocation failure for (i = pc; i < prgm->size; i++) new_prgm->text[i - pc] = prgm->text[i]; current_prgm++; /* Truncate the previously 'current' program and append an END. * No need to check the size against the capacity and grow the * program; since it contained an END before, it still has the * capacity for one now; */ prgm->size = pc; prgm->text[prgm->size++] = CMD_END; prgm->text[prgm->size++] = ARGTYPE_NONE; if (flags.f.trace_print || flags.f.normal_print) print_program_line(current_prgm - 1, pc); rebuild_label_table(); invalidate_lclbls(current_prgm); invalidate_lclbls(current_prgm - 1); clear_all_rtns(); draw_varmenu(); return; } if ((command == CMD_GTO || command == CMD_XEQ) && (arg->type == ARGTYPE_NUM || arg->type == ARGTYPE_LCLBL)) for (i = 0; i < 4; i++) buf[bufptr++] = 255; switch (arg->type) { case ARGTYPE_NUM: case ARGTYPE_NEG_NUM: case ARGTYPE_IND_NUM: { int4 num = arg->val.num; char tmpbuf[5]; int tmplen = 0; while (num > 127) { tmpbuf[tmplen++] = num & 127; num >>= 7; } tmpbuf[tmplen++] = num; tmpbuf[0] |= 128; while (--tmplen >= 0) buf[bufptr++] = tmpbuf[tmplen]; break; } case ARGTYPE_STK: case ARGTYPE_IND_STK: buf[bufptr++] = arg->val.stk; break; case ARGTYPE_STR: case ARGTYPE_IND_STR: { buf[bufptr++] = arg->length; for (i = 0; i < arg->length; i++) buf[bufptr++] = arg->val.text[i]; break; } case ARGTYPE_LCLBL: buf[bufptr++] = arg->val.lclbl; break; case ARGTYPE_DOUBLE: { unsigned char *b = (unsigned char *) &arg->val_d; for (int i = 0; i < (int) sizeof(phloat); i++) buf[bufptr++] = *b++; break; } } if (bufptr + prgm->size > prgm->capacity) { unsigned char *newtext; prgm->capacity += 512; newtext = (unsigned char *) malloc(prgm->capacity); // TODO - handle memory allocation failure for (pos = 0; pos < pc; pos++) newtext[pos] = prgm->text[pos]; for (pos = pc; pos < prgm->size; pos++) newtext[pos + bufptr] = prgm->text[pos]; if (prgm->text != NULL) free(prgm->text); prgm->text = newtext; } else { for (pos = prgm->size - 1; pos >= pc; pos--) prgm->text[pos + bufptr] = prgm->text[pos]; } for (pos = 0; pos < bufptr; pos++) prgm->text[pc + pos] = buf[pos]; prgm->size += bufptr; if (command != CMD_END && (flags.f.trace_print || flags.f.normal_print)) print_program_line(current_prgm, pc); if (command == CMD_END || (command == CMD_LBL && arg->type == ARGTYPE_STR)) rebuild_label_table(); else update_label_table(current_prgm, pc, bufptr); invalidate_lclbls(current_prgm); clear_all_rtns(); draw_varmenu(); } void store_command_after(int4 *pc, int command, arg_struct *arg) { if (*pc == -1) *pc = 0; else if (prgms[current_prgm].text[*pc] != CMD_END) *pc += get_command_length(current_prgm, *pc); store_command(*pc, command, arg); } static int pc_line_convert(int4 loc, int loc_is_pc) { int4 pc = 0; int4 line = 1; prgm_struct *prgm = prgms + current_prgm; while (1) { if (loc_is_pc) { if (pc >= loc) return line; } else { if (line >= loc) return pc; } if (prgm->text[pc] == CMD_END) return loc_is_pc ? line : pc; pc += get_command_length(current_prgm, pc); line++; } } int4 pc2line(int4 pc) { if (pc == -1) return 0; else return pc_line_convert(pc, 1); } int4 line2pc(int4 line) { if (line == 0) return -1; else return pc_line_convert(line, 0); } int4 find_local_label(const arg_struct *arg) { int4 orig_pc = pc; int4 search_pc; int wrapped = 0; prgm_struct *prgm = prgms + current_prgm; if (orig_pc == -1) orig_pc = 0; search_pc = orig_pc; while (!wrapped || search_pc < orig_pc) { int command, argtype; if (search_pc >= prgm->size - 2) { if (orig_pc == 0) break; search_pc = 0; wrapped = 1; } command = prgm->text[search_pc]; argtype = prgm->text[search_pc + 1]; command |= (argtype & 240) << 4; argtype &= 15; if (command == CMD_LBL && argtype == arg->type) { if (argtype == ARGTYPE_NUM) { int num = 0; unsigned char c; int pos = search_pc + 2; do { c = prgm->text[pos++]; num = (num << 7) | (c & 127); } while ((c & 128) == 0); if (num == arg->val.num) return search_pc; } else { char lclbl = prgm->text[search_pc + 2]; if (lclbl == arg->val.lclbl) return search_pc; } } search_pc += get_command_length(current_prgm, search_pc); } return -2; } int find_global_label(const arg_struct *arg, int *prgm, int4 *pc) { int i; const char *name = arg->val.text; int namelen = arg->length; for (i = labels_count - 1; i >= 0; i--) { int j; char *labelname; if (labels[i].length != namelen) continue; labelname = labels[i].name; for (j = 0; j < namelen; j++) if (labelname[j] != name[j]) goto nomatch; *prgm = labels[i].prgm; *pc = labels[i].pc; return 1; nomatch:; } return 0; } int push_rtn_addr(int prgm, int4 pc) { int err = ERR_NONE; if (rtn_sp == MAX_RTNS) { int i; if (rtn_prgm[0] == -2 || rtn_prgm[0] == -3) err = ERR_SOLVE_INTEG_RTN_LOST; for (i = 0; i < MAX_RTNS - 1; i++) { rtn_prgm[i] = rtn_prgm[i + 1]; rtn_pc[i] = rtn_pc[i + 1]; } rtn_sp--; } rtn_prgm[rtn_sp] = prgm; rtn_pc[rtn_sp] = pc; rtn_sp++; return err; } void pop_rtn_addr(int *prgm, int4 *pc) { if (rtn_sp == 0) { *prgm = -1; *pc = -1; } else { rtn_sp--; *prgm = rtn_prgm[rtn_sp]; *pc = rtn_pc[rtn_sp]; } } void clear_all_rtns() { rtn_sp = 0; } bool solve_active() { int i; for (i = 0; i < rtn_sp; i++) if (rtn_prgm[i] == -2) return true; return false; } bool integ_active() { int i; for (i = 0; i < rtn_sp; i++) if (rtn_prgm[i] == -3) return true; return false; } void unwind_stack_until_solve() { while (rtn_prgm[--rtn_sp] != -2); } static bool read_int(int *n) { return shell_read_saved_state(n, sizeof(int)) == sizeof(int); } static bool write_int(int n) { return shell_write_saved_state(&n, sizeof(int)); } static bool read_int4(int4 *n) { return shell_read_saved_state(n, sizeof(int4)) == sizeof(int4); } static bool write_int4(int4 n) { return shell_write_saved_state(&n, sizeof(int4)); } static bool read_bool(bool *b) { if (state_bool_is_int) { int t; if (!read_int(&t)) return false; if (t != 0 && t != 1) return false; *b = t != 0; return true; } else { return shell_read_saved_state(b, sizeof(bool)) == sizeof(bool); } } static bool write_bool(bool b) { return shell_write_saved_state(&b, sizeof(bool)); } bool read_phloat(phloat *d) { if (bin_dec_mode_switch) { #ifdef BCD_MATH double dbl; if (shell_read_saved_state(&dbl, sizeof(double)) != sizeof(double)) return false; *d = dbl; return true; #else short bcd[P + 1]; if (shell_read_saved_state(bcd, (P + 1) * sizeof(short)) != (P + 1) * sizeof(short)) return false; *d = bcd2double(bcd, state_file_has_old_bcd); return true; #endif } else { if (shell_read_saved_state(d, sizeof(phloat)) != sizeof(phloat)) return false; #ifdef BCD_MATH if (state_file_has_old_bcd) bcdfloat_old2new(d->bcd.d_); #endif return true; } } bool write_phloat(phloat d) { return shell_write_saved_state(&d, sizeof(phloat)); } bool load_state(int4 ver) { int4 magic; int4 version; /* The shell has verified the initial magic and version numbers, * and loaded the shell state, before we got called. */ state_bool_is_int = ver < 9; #ifdef BCD_MATH if (ver < 9) { bin_dec_mode_switch = true; state_file_has_old_bcd = false; } else { bool state_is_decimal; if (!read_bool(&state_is_decimal)) return false; bin_dec_mode_switch = !state_is_decimal; state_file_has_old_bcd = state_is_decimal && ver < 12; } #else if (ver < 9) { bin_dec_mode_switch = false; state_file_has_old_bcd = false; } else { bool state_is_decimal; if (!read_bool(&state_is_decimal)) return false; bin_dec_mode_switch = state_is_decimal; state_file_has_old_bcd = state_is_decimal && ver < 12; } #endif if (ver < 2) { core_settings.matrix_singularmatrix = false; core_settings.matrix_outofrange = false; } else { if (!read_bool(&core_settings.matrix_singularmatrix)) return false; if (!read_bool(&core_settings.matrix_outofrange)) return false; if (ver < 9) { int dummy; if (!read_int(&dummy)) return false; } } if (ver < 5) core_settings.raw_text = false; else { if (!read_bool(&core_settings.raw_text)) return false; if (ver < 8) { int dummy; if (!read_int(&dummy)) return false; } } if (ver < 11) core_settings.auto_repeat = true; else if (!read_bool(&core_settings.auto_repeat)) return false; if (ver < 15) { #if defined(COPAN) core_settings.enable_ext_copan = true; #else core_settings.enable_ext_copan = false; #endif #if defined(BIGSTACK) core_settings.enable_ext_bigstack = true; #else core_settings.enable_ext_bigstack = false; #endif #if defined(ANDROID) || defined(IPHONE) core_settings.enable_ext_accel = true; core_settings.enable_ext_locat = true; core_settings.enable_ext_heading = true; #else core_settings.enable_ext_accel = false; core_settings.enable_ext_locat = false; core_settings.enable_ext_heading = false; #endif core_settings.enable_ext_time = true; } else { if (!read_bool(&core_settings.enable_ext_copan)) return false; if (!read_bool(&core_settings.enable_ext_bigstack)) return false; if (!read_bool(&core_settings.enable_ext_accel)) return false; if (!read_bool(&core_settings.enable_ext_locat)) return false; if (!read_bool(&core_settings.enable_ext_heading)) return false; if (!read_bool(&core_settings.enable_ext_time)) return false; } if (!read_bool(&mode_clall)) return false; if (!read_bool(&mode_command_entry)) return false; if (!read_bool(&mode_number_entry)) return false; if (!read_bool(&mode_alpha_entry)) return false; if (!read_bool(&mode_shift)) return false; if (!read_int(&mode_appmenu)) return false; if (!read_int(&mode_plainmenu)) return false; if (!read_bool(&mode_plainmenu_sticky)) return false; if (!read_int(&mode_transientmenu)) return false; if (!read_int(&mode_alphamenu)) return false; if (!read_int(&mode_commandmenu)) return false; if (!read_bool(&mode_running)) return false; if (!read_bool(&mode_varmenu)) return false; if (!read_bool(&mode_updown)) return false; if (ver < 6) mode_getkey = false; else if (!read_bool(&mode_getkey)) return false; if (!read_phloat(&entered_number)) return false; if (!read_int(&entered_string_length)) return false; if (shell_read_saved_state(entered_string, 15) != 15) return false; if (!read_int(&pending_command)) return false; if (!read_arg(&pending_command_arg, ver < 9)) return false; if (!read_int(&xeq_invisible)) return false; if (!read_int(&incomplete_command)) return false; if (!read_int(&incomplete_ind)) return false; if (!read_int(&incomplete_alpha)) return false; if (!read_int(&incomplete_length)) return false; if (!read_int(&incomplete_maxdigits)) return false; if (!read_int(&incomplete_argtype)) return false; if (!read_int(&incomplete_num)) return false; if (shell_read_saved_state(incomplete_str, 7) != 7) return false; if (!read_int4(&incomplete_saved_pc)) return false; if (!read_int4(&incomplete_saved_highlight_row)) return false; if (shell_read_saved_state(cmdline, 100) != 100) return false; if (!read_int(&cmdline_length)) return false; if (!read_int(&cmdline_row)) return false; if (!read_int(&matedit_mode)) return false; if (shell_read_saved_state(matedit_name, 7) != 7) return false; if (!read_int(&matedit_length)) return false; if (!unpersist_vartype(&matedit_x)) return false; if (!read_int4(&matedit_i)) return false; if (!read_int4(&matedit_j)) return false; if (!read_int(&matedit_prev_appmenu)) return false; if (shell_read_saved_state(input_name, 11) != 11) return false; if (!read_int(&input_length)) return false; if (!read_arg(&input_arg, ver < 9)) return false; if (!read_int(&baseapp)) return false; if (!read_phloat(&random_number)) return false; if (ver < 3) { deferred_print = 0; } else { if (!read_int(&deferred_print)) return false; } if (!read_int(&keybuf_head)) return false; if (!read_int(&keybuf_tail)) return false; if (shell_read_saved_state(keybuf, 16 * sizeof(int)) != 16 * sizeof(int)) return false; if (!unpersist_display(ver)) return false; if (!unpersist_globals(ver)) return false; if (ver < 4) { /* Before state file version 4, I used to save the BCD table in the * state file. As of state file version 4, the Unix and Windows * versions don't do that any more because they don't need to * (generating the table on startup is fast enough); the PalmOS version * now persists the BCD table in a database, which is faster because it * doesn't need to be loaded and saved each time the application is * started and stopped. * This code is to skip the saved BCD table in state versions up to * and including version 3. */ int min_pow2, max_pow2; uint4 n1, n2, n3, n4, n; char dummy[1024]; if (!read_int(&min_pow2)) return false; if (!read_int(&max_pow2)) return false; n1 = 16 * (max_pow2 + 1); /* size of pos_pow2mant table */ n2 = sizeof(int) * (max_pow2 + 1); /* size of pos_pow2exp table */ n3 = 16 * (-min_pow2); /* size of neg_pow2mant table */ n4 = sizeof(int) * (-min_pow2); /* size of neg_pow2exp table */ n = n1 + n2 + n3 + n4; /* total number of bytes to skip */ while (n > 0) { int count = n < 1024 ? n : 1024; if (shell_read_saved_state(dummy, count) != count) return false; n -= count; } } if (!unpersist_math(bin_dec_mode_switch)) return false; if (!read_int4(&magic)) return false; if (magic != FREE42_MAGIC) return false; if (!read_int4(&version)) return false; if (version != ver) return false; return true; } void save_state() { /* The shell has written the initial magic and version numbers, * and the shell state, before we got called. */ #ifdef BCD_MATH if (!write_bool(true)) return; #else if (!write_bool(false)) return; #endif if (!write_bool(core_settings.matrix_singularmatrix)) return; if (!write_bool(core_settings.matrix_outofrange)) return; if (!write_bool(core_settings.raw_text)) return; if (!write_bool(core_settings.auto_repeat)) return; if (!write_bool(core_settings.enable_ext_copan)) return; if (!write_bool(core_settings.enable_ext_bigstack)) return; if (!write_bool(core_settings.enable_ext_accel)) return; if (!write_bool(core_settings.enable_ext_locat)) return; if (!write_bool(core_settings.enable_ext_heading)) return; if (!write_bool(core_settings.enable_ext_time)) return; if (!write_bool(mode_clall)) return; if (!write_bool(mode_command_entry)) return; if (!write_bool(mode_number_entry)) return; if (!write_bool(mode_alpha_entry)) return; if (!write_bool(mode_shift)) return; if (!write_int(mode_appmenu)) return; if (!write_int(mode_plainmenu)) return; if (!write_bool(mode_plainmenu_sticky)) return; if (!write_int(mode_transientmenu)) return; if (!write_int(mode_alphamenu)) return; if (!write_int(mode_commandmenu)) return; if (!write_bool(mode_running)) return; if (!write_bool(mode_varmenu)) return; if (!write_bool(mode_updown)) return; if (!write_bool(mode_getkey)) return; if (!write_phloat(entered_number)) return; if (!write_int(entered_string_length)) return; if (!shell_write_saved_state(entered_string, 15)) return; if (!write_int(pending_command)) return; if (!shell_write_saved_state(&pending_command_arg, sizeof(arg_struct))) return; if (!write_int(xeq_invisible)) return; if (!write_int(incomplete_command)) return; if (!write_int(incomplete_ind)) return; if (!write_int(incomplete_alpha)) return; if (!write_int(incomplete_length)) return; if (!write_int(incomplete_maxdigits)) return; if (!write_int(incomplete_argtype)) return; if (!write_int(incomplete_num)) return; if (!shell_write_saved_state(incomplete_str, 7)) return; if (!write_int4(incomplete_saved_pc)) return; if (!write_int4(incomplete_saved_highlight_row)) return; if (!shell_write_saved_state(cmdline, 100)) return; if (!write_int(cmdline_length)) return; if (!write_int(cmdline_row)) return; if (!write_int(matedit_mode)) return; if (!shell_write_saved_state(matedit_name, 7)) return; if (!write_int(matedit_length)) return; if (!persist_vartype(matedit_x)) return; if (!write_int4(matedit_i)) return; if (!write_int4(matedit_j)) return; if (!write_int(matedit_prev_appmenu)) return; if (!shell_write_saved_state(input_name, 11)) return; if (!write_int(input_length)) return; if (!shell_write_saved_state(&input_arg, sizeof(arg_struct))) return; if (!write_int(baseapp)) return; if (!write_phloat(random_number)) return; if (!write_int(deferred_print)) return; if (!write_int(keybuf_head)) return; if (!write_int(keybuf_tail)) return; if (!shell_write_saved_state(keybuf, 16 * sizeof(int))) return; if (!persist_display()) return; if (!persist_globals()) return; if (!persist_math()) return; if (!write_int4(FREE42_MAGIC)) return; if (!write_int4(FREE42_VERSION)) return; } void hard_reset(int bad_state_file) { vartype *regs; /* Clear stack */ free_vartype(reg_x); free_vartype(reg_y); free_vartype(reg_z); free_vartype(reg_t); free_vartype(reg_lastx); reg_x = new_real(0); reg_y = new_real(0); reg_z = new_real(0); reg_t = new_real(0); reg_lastx = new_real(0); /* Clear alpha */ reg_alpha_length = 0; /* Clear variables */ purge_all_vars(); regs = new_realmatrix(25, 1); store_var("REGS", 4, regs); /* Clear programs */ if (prgms != NULL) { free(prgms); prgms = NULL; prgms_capacity = 0; prgms_count = 0; } if (labels != NULL) { free(labels); labels = NULL; labels_capacity = 0; labels_count = 0; } goto_dot_dot(); pending_command = CMD_NONE; matedit_mode = 0; input_length = 0; baseapp = 0; random_number = shell_random_seed(); flags.f.f00 = flags.f.f01 = flags.f.f02 = flags.f.f03 = flags.f.f04 = 0; flags.f.f05 = flags.f.f06 = flags.f.f07 = flags.f.f08 = flags.f.f09 = 0; flags.f.f10 = 0; flags.f.auto_exec = 0; flags.f.double_wide_print = 0; flags.f.lowercase_print = 0; flags.f.f14 = 0; flags.f.trace_print = 0; flags.f.normal_print = 0; flags.f.f17 = flags.f.f18 = flags.f.f19 = flags.f.f20 = 0; flags.f.printer_enable = 1; // HP-42S sets this to 0 on hard reset flags.f.numeric_data_input = 0; flags.f.alpha_data_input = 0; flags.f.range_error_ignore = 0; flags.f.error_ignore = 0; flags.f.audio_enable = 1; /* flags.f.VIRTUAL_custom_menu = 0; */ flags.f.decimal_point = 1; flags.f.thousands_separators = 1; flags.f.stack_lift_disable = 0; flags.f.f31 = flags.f.f32 = flags.f.f33 = 0; flags.f.agraph_control1 = 0; flags.f.agraph_control0 = 0; flags.f.digits_bit3 = 0; flags.f.digits_bit2 = 1; flags.f.digits_bit1 = 0; flags.f.digits_bit0 = 0; flags.f.fix_or_all = 1; flags.f.eng_or_all = 0; flags.f.grad = 0; flags.f.rad = 0; flags.f.continuous_on = 0; /* flags.f.VIRTUAL_solving = 0; */ /* flags.f.VIRTUAL_integrating = 0; */ /* flags.f.VIRTUAL_variable_menu = 0; */ flags.f.alpha_mode = 0; /* flags.f.VIRTUAL_low_battery = 0; */ flags.f.message = 1; flags.f.two_line_message = 0; flags.f.prgm_mode = 0; /* flags.f.VIRTUAL_input = 0; */ flags.f.f54 = 0; flags.f.printer_exists = 1; // HP-42S sets this to 0 on hard reset flags.f.lin_fit = 1; flags.f.log_fit = 0; flags.f.exp_fit = 0; flags.f.pwr_fit = 0; flags.f.all_sigma = 1; flags.f.log_fit_invalid = 0; flags.f.exp_fit_invalid = 0; flags.f.pwr_fit_invalid = 0; flags.f.f64 = 0; /* flags.f.VIRTUAL_matrix_editor = 0; */ flags.f.grow = 0; flags.f.f67 = 0; flags.f.base_bit0 = 0; flags.f.base_bit1 = 0; flags.f.base_bit2 = 0; flags.f.base_bit3 = 0; flags.f.local_label = 0; flags.f.polar = 0; flags.f.real_result_only = 0; /* flags.f.VIRTUAL_programmable_menu = 0; */ flags.f.matrix_edge_wrap = 0; flags.f.matrix_end_wrap = 0; flags.f.f78 = flags.f.f79 = flags.f.f80 = flags.f.f81 = flags.f.f82 = 0; flags.f.f83 = flags.f.f84 = flags.f.f85 = flags.f.f86 = flags.f.f87 = 0; flags.f.f88 = flags.f.f89 = flags.f.f90 = flags.f.f91 = flags.f.f92 = 0; flags.f.f93 = flags.f.f94 = flags.f.f95 = flags.f.f96 = flags.f.f97 = 0; flags.f.f98 = flags.f.f99 = 0; mode_clall = false; mode_command_entry = false; mode_number_entry = false; mode_alpha_entry = false; mode_shift = false; mode_commandmenu = MENU_NONE; mode_alphamenu = MENU_NONE; mode_transientmenu = MENU_NONE; mode_plainmenu = MENU_NONE; mode_appmenu = MENU_NONE; mode_running = false; mode_getkey = false; mode_pause = false; mode_varmenu = false; prgm_highlight_row = 0; varmenu_length = 0; mode_updown = false; mode_sigma_reg = 11; mode_goose = -1; mode_time_clktd = false; mode_time_clk24 = false; mode_time_dmy = false; core_settings.auto_repeat = true; #if defined(COPAN) core_settings.enable_ext_copan = true; #else core_settings.enable_ext_copan = false; #endif #if defined(BIGSTACK) core_settings.enable_ext_bigstack = true; #else core_settings.enable_ext_bigstack = false; #endif #if defined(ANDROID) || defined(IPHONE) core_settings.enable_ext_accel = true; core_settings.enable_ext_locat = true; core_settings.enable_ext_heading = true; #else core_settings.enable_ext_accel = false; core_settings.enable_ext_locat = false; core_settings.enable_ext_heading = false; #endif core_settings.enable_ext_time = true; reset_math(); clear_display(); clear_custom_menu(); clear_prgm_menu(); if (bad_state_file) draw_string(0, 0, "State File Corrupt", 18); else draw_string(0, 0, "Memory Clear", 12); display_x(1); flush_display(); } struct dec_arg_struct { unsigned char type; unsigned char length; int4 target; union { int4 num; char text[15]; char stk; int cmd; char lclbl; } val; fake_bcd val_d; }; struct bin_arg_struct { unsigned char type; unsigned char length; int4 target; union { int4 num; char text[15]; char stk; int cmd; char lclbl; } val; double val_d; }; bool read_arg(arg_struct *arg, bool old) { if (old) { // Prior to core state version 9, the arg_struct type saved a bit of // by using a union to hold the argument value. // In version 9, we switched from using 'double' as our main numeric // data type to 'phloat' -- but since 'phloat' is a class, with a // constructor, it cannot be a member of a union. // So, I had to change the 'val' member from a union to a struct. // Of course, this means that the arg_struct layout is now different, // and when deserializing a pre-9 state file, I must make sure to // deserialize an old-stype arg_struct and then convert it to a // new one. struct { unsigned char type; unsigned char length; int4 target; union { int4 num; char text[15]; char stk; int cmd; /* For backward compatibility only! */ char lclbl; double d; } val; } old_arg; if (shell_read_saved_state(&old_arg, sizeof(old_arg)) != sizeof(old_arg)) return false; arg->type = old_arg.type; arg->length = old_arg.length; arg->target = old_arg.target; char *d = (char *) &arg->val; char *s = (char *) &old_arg.val; for (unsigned int i = 0; i < sizeof(old_arg.val); i++) *d++ = *s++; arg->val_d = old_arg.val.d; return true; } else if (bin_dec_mode_switch) { #ifdef BCD_MATH bin_arg_struct ba; if (shell_read_saved_state(&ba, sizeof(bin_arg_struct)) != sizeof(bin_arg_struct)) return false; arg->type = ba.type; arg->length = ba.length; arg->target = ba.target; char *d = (char *) &arg->val; char *s = (char *) &ba.val; for (unsigned int i = 0; i < sizeof(ba.val); i++) *d++ = *s++; arg->val_d = ba.val_d; #else dec_arg_struct da; if (shell_read_saved_state(&da, sizeof(dec_arg_struct)) != sizeof(dec_arg_struct)) return false; arg->type = da.type; arg->length = da.length; arg->target = da.target; char *d = (char *) &arg->val; char *s = (char *) &da.val; for (unsigned int i = 0; i < sizeof(da.val); i++) *d++ = *s++; arg->val_d = bcd2double(da.val_d.d_, state_file_has_old_bcd); #endif return true; } else { return shell_read_saved_state(arg, sizeof(arg_struct)) == sizeof(arg_struct); } } static bool convert_programs() { // This function is called if the setting of mode_decimal recorded in the // state file does not match the current setting (i.e., if we're a binary // Free42 and the state file was written by the decimal version, or vice // versa). // This function looks for floating-point number literals (command = // CMD_NUMBER with arg.type = ARGTYPE_DOUBLE) and converts them from double // to Phloat or the other way around. int saved_prgm = current_prgm; int4 saved_pc = pc; int i; // Since converting programs can cause instructions to move, I have to // update all stored PC values to correct for this. PCs are stored in the // 'pc' and 'rtn_pc[]' globals. I copy those values into a local array, // which I then sort by program index and pc; this allows me to do the // updates very efficiently later on. int mod_prgm[MAX_RTNS + 2]; int4 mod_pc[MAX_RTNS + 2]; int mod_sp[MAX_RTNS + 2]; int mod_count = 0; for (i = 0; i < rtn_sp; i++) { int prgm = rtn_prgm[i]; if (prgm == -2 || prgm == -3) { // Return-to-solve and return-to-integ // On a binary/decimal mode switch, unpersist_math() discards all // the SOLVE and INTEG state. If SOLVE or INTEG are actually // active, we have to clear the RTN stack, too. rtn_sp = 0; mod_count = 0; break; } mod_prgm[mod_count] = prgm; mod_pc[mod_count] = rtn_pc[i]; mod_sp[mod_count] = i; mod_count++; } if (saved_pc > 0) { mod_prgm[mod_count] = current_prgm; mod_pc[mod_count] = saved_pc; mod_sp[mod_count] = -1; mod_count++; } if (incomplete_saved_pc > 0) { mod_prgm[mod_count] = current_prgm; mod_pc[mod_count] = incomplete_saved_pc; mod_sp[mod_count] = -2; mod_count++; } mod_count--; for (i = 0; i < mod_count; i++) for (int j = i + 1; j <= mod_count; j++) if (mod_prgm[i] < mod_prgm[j] || mod_prgm[i] == mod_prgm[j] && mod_pc[i] < mod_pc[j]) { int tmp = mod_prgm[i]; mod_prgm[i] = mod_prgm[j]; mod_prgm[j] = tmp; int4 tmp4 = mod_pc[i]; mod_pc[i] = mod_pc[j]; mod_pc[j] = tmp4; tmp = mod_sp[i]; mod_sp[i] = mod_sp[j]; mod_sp[j] = tmp; } for (i = 0; i < prgms_count; i++) { current_prgm = i; pc = 0; int4 oldpc = 0; prgm_struct *prgm = prgms + i; prgm->lclbl_invalid = 1; while (true) { while (mod_count >= 0 && current_prgm == mod_prgm[mod_count] && oldpc >= mod_pc[mod_count]) { // oldpc should never be greater than mod_pc[mod_count]; this // means that something is out of whack, because we have an old // PC value that does not actually coincide with the beginning // of an instruction. int s = mod_sp[mod_count]; if (s == -1) saved_pc = pc; else if (s == -2) incomplete_saved_pc = pc; else rtn_pc[s] = pc; mod_count--; } int4 prevpc = pc; int command = prgm->text[pc++]; int argtype = prgm->text[pc++]; command |= (argtype & 240) << 4; argtype &= 15; if (command == CMD_END) break; if ((command == CMD_GTO || command == CMD_XEQ) && (argtype == ARGTYPE_NUM || argtype == ARGTYPE_LCLBL)) { // Invalidate local label offsets prgm->text[pc++] = 255; prgm->text[pc++] = 255; prgm->text[pc++] = 255; prgm->text[pc++] = 255; } switch (argtype) { case ARGTYPE_NUM: case ARGTYPE_NEG_NUM: case ARGTYPE_IND_NUM: { while ((prgm->text[pc++] & 128) == 0); break; } case ARGTYPE_STK: case ARGTYPE_IND_STK: case ARGTYPE_COMMAND: case ARGTYPE_LCLBL: pc++; break; case ARGTYPE_STR: case ARGTYPE_IND_STR: { pc += prgm->text[pc] + 1; break; } case ARGTYPE_DOUBLE: #ifdef BCD_MATH double d; int j; unsigned char *b = (unsigned char *) &d; for (j = 0; j < (int) sizeof(double); j++) *b++ = prgm->text[pc++]; pc -= sizeof(double); int growth = sizeof(phloat) - sizeof(double); int4 pos; if (prgm->size + growth > prgm->capacity) { unsigned char *newtext; prgm->capacity += 512; newtext = (unsigned char *) malloc(prgm->capacity); if (newtext == NULL) // Failed to grow program; abort. return false; for (pos = 0; pos < pc; pos++) newtext[pos] = prgm->text[pos]; for (pos = pc; pos < prgm->size; pos++) newtext[pos + growth] = prgm->text[pos]; if (prgm->text != NULL) free(prgm->text); prgm->text = newtext; } else { for (pos = prgm->size - 1; pos >= pc; pos--) prgm->text[pos + growth] = prgm->text[pos]; } prgm->size += growth; oldpc -= growth; phloat p; p.bcd = double2bcd(d, true); b = (unsigned char *) &p; for (j = 0; j < (int) sizeof(phloat); j++) prgm->text[pc++] = *b++; #else fake_bcd bcd; int j; unsigned char *b = (unsigned char *) &bcd; for (j = 0; j < (int) sizeof(fake_bcd); j++) *b++ = prgm->text[pc++]; double dbl = bcd2double(bcd.d_, state_file_has_old_bcd); if (isinf(dbl)) dbl = dbl > 0 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else if (dbl == 0) { if (bcd.d_[0] != 0) if ((bcd.d_[P] & 0x8000) == 0) dbl = POS_TINY_PHLOAT; else dbl = NEG_TINY_PHLOAT; } pc -= sizeof(fake_bcd); b = (unsigned char *) &dbl; for (j = 0; j < (int) sizeof(double); j++) prgm->text[pc++] = *b++; int shrinkage = sizeof(fake_bcd) - sizeof(double); prgm->size -= shrinkage; for (int4 pos = pc; pos < prgm->size; pos++) prgm->text[pos] = prgm->text[pos + shrinkage]; oldpc += shrinkage; #endif break; } oldpc += pc - prevpc; } } current_prgm = saved_prgm; pc = saved_pc; return true; } #ifdef IPHONE static void convert_bigstack_drop() { // This function is called when we've read an iPhone version state file // with version number 12 or 13. In those two versions, the DROP command // was at index 315 of the commands table, but that conflicted with // Underhill's COPAN extensions. In version 14 and later, I moved DROP // to index 329 to fix this clash. This will allow all extensions to // coexist in the future, should someone want to merge them all into one // build at some point -- and even if that never happens, at least now // all programs in all versions are encoded identically. for (int i = 0; i < prgms_count; i++) { int pc = 0; prgm_struct *prgm = prgms + i; while (true) { int command = prgm->text[pc++]; int argtype = prgm->text[pc++]; command |= (argtype & 240) << 4; argtype &= 15; if (command == CMD_END) break; if (command == 315) { // Pre-version-14 value of CMD_DROP prgm->text[pc - 2] = (unsigned char) CMD_DROP; prgm->text[pc - 1] = (unsigned char) ((CMD_DROP & 0xF00) >> 4 | argtype); } if ((command == CMD_GTO || command == CMD_XEQ) && (argtype == ARGTYPE_NUM || argtype == ARGTYPE_LCLBL)) { pc += 4; } switch (argtype) { case ARGTYPE_NUM: case ARGTYPE_NEG_NUM: case ARGTYPE_IND_NUM: { while ((prgm->text[pc++] & 128) == 0); break; } case ARGTYPE_STK: case ARGTYPE_IND_STK: case ARGTYPE_COMMAND: case ARGTYPE_LCLBL: pc++; break; case ARGTYPE_STR: case ARGTYPE_IND_STR: { pc += prgm->text[pc] + 1; break; } case ARGTYPE_DOUBLE: pc += sizeof(phloat); break; } } } } #endif #ifdef ANDROID void reinitialize_globals() { /* The Android version may call core_init() after core_quit(), in other * words, the globals may live for more than one session. This caused * crashes in the initial builds, because of course global initializers * are only invoked once, and core_quit() did not bother to clean things * up so that core_init() would be able to run safely. * In my defense, this wasn't sloppy coding; core_quit() does deallocate * everything -- I've tested Free42 for memory leaks using POSE many * times, and it is solid in that regard. The dangling pointers left * by core_quit() are never a problem as long as core_init() and * core_quit() are only called once per process. * Anyway: the following are re-initializations of some globals that * could cause double-free() memory corruption, or other (less fatal, but * still annoying) misbehaviors if left as they are. */ reg_x = NULL; reg_y = NULL; reg_z = NULL; reg_t = NULL; reg_lastx = NULL; reg_alpha_length = 0; vars_capacity = 0; vars_count = 0; vars = NULL; prgms_capacity = 0; prgms_count = 0; prgms = NULL; labels_capacity = 0; labels_count = 0; labels = NULL; current_prgm = -1; prgm_highlight_row = 0; mode_interruptible = NULL; mode_pause = false; baseapp = 0; deferred_print = 0; keybuf_head = 0; keybuf_tail = 0; remove_program_catalog = 0; rtn_sp = 0; } #endif #ifdef IPHONE bool off_enabled() { if (off_enable_flag) return true; if (reg_x->type != TYPE_STRING) return false; vartype_string *str = (vartype_string *) reg_x; off_enable_flag = str->length == 6 && str->text[0] == 'Y' && str->text[1] == 'E' && str->text[2] == 'S' && str->text[3] == 'O' && str->text[4] == 'F' && str->text[5] == 'F'; return off_enable_flag; } #endif free42-nologo-1.4.77/common/core_globals.h000644 000765 000024 00000032322 12110237247 020727 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_GLOBALS_H #define CORE_GLOBALS_H 1 #include "free42.h" #include "core_phloat.h" #include "core_tables.h" /**********/ /* Errors */ /**********/ #define ERR_NONE 0 #define ERR_ALPHA_DATA_IS_INVALID 1 #define ERR_INSUFFICIENT_MEMORY 2 #define ERR_NOT_YET_IMPLEMENTED 3 #define ERR_OUT_OF_RANGE 4 #define ERR_DIVIDE_BY_0 5 #define ERR_INVALID_TYPE 6 #define ERR_INVALID_DATA 7 #define ERR_DIMENSION_ERROR 8 #define ERR_SIZE_ERROR 9 #define ERR_INTERNAL_ERROR 10 #define ERR_NONEXISTENT 11 #define ERR_RESTRICTED_OPERATION 12 #define ERR_YES 13 #define ERR_NO 14 #define ERR_STOP 15 #define ERR_LABEL_NOT_FOUND 16 #define ERR_NO_REAL_VARIABLES 17 #define ERR_NO_COMPLEX_VARIABLES 18 #define ERR_NO_MATRIX_VARIABLES 19 #define ERR_NO_MENU_VARIABLES 20 #define ERR_STAT_MATH_ERROR 21 #define ERR_INVALID_FORECAST_MODEL 22 #define ERR_SOLVE_INTEG_RTN_LOST 23 #define ERR_SINGULAR_MATRIX 24 #define ERR_SOLVE_SOLVE 25 #define ERR_INTEG_INTEG 26 #define ERR_RUN 27 #define ERR_INTERRUPTED 28 #define ERR_PRINTING_IS_DISABLED 29 #define ERR_INTERRUPTIBLE 30 #define ERR_NO_VARIABLES 31 #define ERR_SUSPICIOUS_OFF 32 typedef struct { const char *text; int length; } error_spec; extern error_spec errors[]; /*************/ /* Key codes */ /*************/ #define KEY_SIGMA 1 #define KEY_INV 2 #define KEY_SQRT 3 #define KEY_LOG 4 #define KEY_LN 5 #define KEY_XEQ 6 #define KEY_STO 7 #define KEY_RCL 8 #define KEY_RDN 9 #define KEY_SIN 10 #define KEY_COS 11 #define KEY_TAN 12 #define KEY_ENTER 13 #define KEY_SWAP 14 #define KEY_CHS 15 #define KEY_E 16 #define KEY_BSP 17 #define KEY_UP 18 #define KEY_7 19 #define KEY_8 20 #define KEY_9 21 #define KEY_DIV 22 #define KEY_DOWN 23 #define KEY_4 24 #define KEY_5 25 #define KEY_6 26 #define KEY_MUL 27 #define KEY_SHIFT 28 #define KEY_1 29 #define KEY_2 30 #define KEY_3 31 #define KEY_SUB 32 #define KEY_EXIT 33 #define KEY_0 34 #define KEY_DOT 35 #define KEY_RUN 36 #define KEY_ADD 37 /*********/ /* Menus */ /*********/ #define MENU_NONE -1 #define MENU_SHORTCUT -2 #define MENU_ALPHA1 0 #define MENU_ALPHA2 1 #define MENU_ALPHA_ABCDE1 2 #define MENU_ALPHA_ABCDE2 3 #define MENU_ALPHA_FGHI 4 #define MENU_ALPHA_JKLM 5 #define MENU_ALPHA_NOPQ1 6 #define MENU_ALPHA_NOPQ2 7 #define MENU_ALPHA_RSTUV1 8 #define MENU_ALPHA_RSTUV2 9 #define MENU_ALPHA_WXYZ 10 #define MENU_ALPHA_PAREN 11 #define MENU_ALPHA_ARROW 12 #define MENU_ALPHA_COMP 13 #define MENU_ALPHA_MATH 14 #define MENU_ALPHA_PUNC1 15 #define MENU_ALPHA_PUNC2 16 #define MENU_ALPHA_MISC1 17 #define MENU_ALPHA_MISC2 18 #define MENU_ST 19 #define MENU_IND_ST 20 #define MENU_IND 21 #define MENU_MODES1 22 #define MENU_MODES2 23 #define MENU_DISP 24 #define MENU_CLEAR1 25 #define MENU_CLEAR2 26 #define MENU_CONVERT1 27 #define MENU_CONVERT2 28 #define MENU_FLAGS 29 #define MENU_PROB 30 #define MENU_CUSTOM1 31 #define MENU_CUSTOM2 32 #define MENU_CUSTOM3 33 #define MENU_PGM_FCN1 34 #define MENU_PGM_FCN2 35 #define MENU_PGM_FCN3 36 #define MENU_PGM_FCN4 37 #define MENU_PGM_XCOMP0 38 #define MENU_PGM_XCOMPY 39 #define MENU_PRINT1 40 #define MENU_PRINT2 41 #define MENU_PRINT3 42 #define MENU_TOP_FCN 43 #define MENU_CATALOG 44 #define MENU_BLANK 45 #define MENU_PROGRAMMABLE 46 #define MENU_VARMENU 47 #define MENU_STAT1 48 #define MENU_STAT2 49 #define MENU_STAT_CFIT 50 #define MENU_STAT_MODL 51 #define MENU_MATRIX1 52 #define MENU_MATRIX2 53 #define MENU_MATRIX3 54 #define MENU_MATRIX_SIMQ 55 #define MENU_MATRIX_EDIT1 56 #define MENU_MATRIX_EDIT2 57 #define MENU_BASE 58 #define MENU_BASE_A_THRU_F 59 #define MENU_BASE_LOGIC 60 #define MENU_SOLVE 61 #define MENU_INTEG 62 #define MENU_INTEG_PARAMS 63 typedef struct { int menuid; unsigned char title_length; char title[7]; } menu_item_spec; typedef struct { int parent; int next; int prev; menu_item_spec child[6]; } menu_spec; extern menu_spec menus[]; /***********************/ /* Variable data types */ /***********************/ #define TYPE_NULL 0 #define TYPE_REAL 1 #define TYPE_COMPLEX 2 #define TYPE_REALMATRIX 3 #define TYPE_COMPLEXMATRIX 4 #define TYPE_STRING 5 typedef struct { int type; } vartype; typedef struct { int type; phloat x; } vartype_real; typedef struct { int type; phloat re, im; } vartype_complex; typedef struct { int refcount; phloat *data; char *is_string; } realmatrix_data; typedef struct { int type; int4 rows; int4 columns; realmatrix_data *array; } vartype_realmatrix; typedef struct { int refcount; phloat *data; } complexmatrix_data; typedef struct { int type; int4 rows; int4 columns; complexmatrix_data *array; } vartype_complexmatrix; typedef struct { int type; int length; char text[6]; } vartype_string; /******************/ /* Emulator state */ /******************/ /* Registers */ extern vartype *reg_x; extern vartype *reg_y; extern vartype *reg_z; extern vartype *reg_t; extern vartype *reg_lastx; extern int reg_alpha_length; extern char reg_alpha[44]; /* FLAGS * Note: flags whose names start with VIRTUAL_ are named here for reference * only; they are actually handled by virtual_flag_handler(). Setting or * clearing them in 'flags' has no effect. * Flags whose names are the letter 'f' followed by two digits have no * specified meaning according to the HP-42S manual; they are either user flags * or reserved. */ typedef union { char farray[100]; struct { char f00; char f01; char f02; char f03; char f04; char f05; char f06; char f07; char f08; char f09; char f10; char auto_exec; char double_wide_print; char lowercase_print; char f14; char trace_print; /* 'normal_print' ignored if this is set */ char normal_print; char f17; char f18; char f19; char f20; char printer_enable; char numeric_data_input; char alpha_data_input; char range_error_ignore; char error_ignore; char audio_enable; char VIRTUAL_custom_menu; char decimal_point; char thousands_separators; char stack_lift_disable; char f31; char f32; char f33; char agraph_control1; /* 0 (default): dst = dst | src, 1: dst = src, */ char agraph_control0; /* 2: dst = dst & ~src, 3: dst = dst ^ src */ char digits_bit3; char digits_bit2; char digits_bit1; char digits_bit0; char fix_or_all; char eng_or_all; char grad; char rad; char continuous_on; char VIRTUAL_solving; char VIRTUAL_integrating; char VIRTUAL_variable_menu; char alpha_mode; char VIRTUAL_low_battery; char message; char two_line_message; char prgm_mode; char VIRTUAL_input; char f54; char printer_exists; char lin_fit; char log_fit; char exp_fit; char pwr_fit; char all_sigma; char log_fit_invalid; char exp_fit_invalid; char pwr_fit_invalid; char f64; char VIRTUAL_matrix_editor; char grow; char f67; char base_bit0; /* Note: dec=0, bin=1, oct=7, hex=15 */ char base_bit1; char base_bit2; char base_bit3; char local_label; char polar; char real_result_only; char VIRTUAL_programmable_menu; char matrix_edge_wrap; char matrix_end_wrap; char f78; char f79; char f80; char f81; char f82; char f83; char f84; char f85; char f86; char f87; char f88; char f89; char f90; char f91; char f92; char f93; char f94; char f95; char f96; char f97; char f98; char f99; } f; } flags_struct; extern flags_struct flags; /* Variables */ typedef struct { unsigned char length; char name[7]; vartype *value; } var_struct; extern int vars_capacity; extern int vars_count; extern var_struct *vars; /* Programs */ typedef struct { int4 capacity; int4 size; int lclbl_invalid; unsigned char *text; } prgm_struct; extern int prgms_capacity; extern int prgms_count; extern prgm_struct *prgms; typedef struct { unsigned char length; char name[7]; int prgm; int4 pc; } label_struct; extern int labels_capacity; extern int labels_count; extern label_struct *labels; extern int current_prgm; extern int4 pc; extern int prgm_highlight_row; extern int varmenu_length; extern char varmenu[7]; extern int varmenu_rows; extern int varmenu_row; extern int varmenu_labellength[6]; extern char varmenu_labeltext[6][7]; extern int varmenu_role; /****************/ /* More globals */ /****************/ extern bool mode_clall; extern int (*mode_interruptible)(int); extern bool mode_stoppable; extern bool mode_command_entry; extern bool mode_number_entry; extern bool mode_alpha_entry; extern bool mode_shift; extern int mode_appmenu; extern int mode_plainmenu; extern bool mode_plainmenu_sticky; extern int mode_transientmenu; extern int mode_alphamenu; extern int mode_commandmenu; extern bool mode_running; extern bool mode_getkey; extern bool mode_pause; extern bool mode_disable_stack_lift; extern bool mode_varmenu; extern bool mode_updown; extern int4 mode_sigma_reg; extern int mode_goose; extern bool mode_time_clktd; extern bool mode_time_clk24; extern bool mode_time_dmy; extern phloat entered_number; extern int entered_string_length; extern char entered_string[15]; extern int pending_command; extern arg_struct pending_command_arg; extern int xeq_invisible; /* Multi-keystroke commands -- edit state */ /* Relevant when mode_command_entry != 0 */ extern int incomplete_command; extern int incomplete_ind; extern int incomplete_alpha; extern int incomplete_length; extern int incomplete_maxdigits; extern int incomplete_argtype; extern int incomplete_num; extern char incomplete_str[7]; extern int4 incomplete_saved_pc; extern int4 incomplete_saved_highlight_row; #define CATSECT_TOP 0 #define CATSECT_FCN 1 #define CATSECT_PGM 2 #define CATSECT_REAL 3 #define CATSECT_CPX 4 #define CATSECT_MAT 5 #define CATSECT_PGM_ONLY 6 #define CATSECT_REAL_ONLY 7 #define CATSECT_MAT_ONLY 8 #define CATSECT_VARS_ONLY 9 #define CATSECT_PGM_SOLVE 10 #define CATSECT_PGM_INTEG 11 /* Command line handling temporaries */ extern char cmdline[100]; extern int cmdline_length; extern int cmdline_row; /* Matrix editor / matrix indexing */ extern int matedit_mode; /* 0=off, 1=index, 2=edit, 3=editn */ extern char matedit_name[7]; extern int matedit_length; extern vartype *matedit_x; extern int4 matedit_i; extern int4 matedit_j; extern int matedit_prev_appmenu; /* INPUT */ extern char input_name[11]; extern int input_length; extern arg_struct input_arg; /* BASE application */ extern int baseapp; /* Random number generator */ extern phloat random_number; /* NORM & TRACE mode: number waiting to be printed */ extern int deferred_print; /* Keystroke buffer - holds keystrokes received while * there is a program running. */ extern int keybuf_head; extern int keybuf_tail; extern int keybuf[16]; extern int remove_program_catalog; extern bool bin_dec_mode_switch; extern bool state_file_has_old_bcd; extern bool no_keystrokes_yet; /*********************/ /* Utility functions */ /*********************/ void clear_all_prgms(); int clear_prgm(const arg_struct *arg); int clear_prgm_by_index(int prgm_index); void clear_prgm_lines(int4 count); void goto_dot_dot(); int mvar_prgms_exist(); int label_has_mvar(int lblindex); int get_command_length(int prgm, int4 pc); void get_next_command(int4 *pc, int *command, arg_struct *arg, int find_target); void rebuild_label_table(); void delete_command(int4 pc); void store_command(int4 pc, int command, arg_struct *arg); void store_command_after(int4 *pc, int command, arg_struct *arg); int4 pc2line(int4 pc); int4 line2pc(int4 line); int4 find_local_label(const arg_struct *arg); int find_global_label(const arg_struct *arg, int *prgm, int4 *pc); int push_rtn_addr(int prgm, int4 pc); void pop_rtn_addr(int *prgm, int4 *pc); void clear_all_rtns(); bool solve_active(); bool integ_active(); void unwind_stack_until_solve(); bool load_state(int4 version); void save_state(); void hard_reset(int bad_state_file); bool read_arg(arg_struct *arg, bool old); bool read_phloat(phloat *d); bool write_phloat(phloat d); #ifdef ANDROID void reinitialize_globals(); #endif #ifdef IPHONE bool off_enabled(); #endif #endif free42-nologo-1.4.77/common/core_helpers.cc000644 000765 000024 00000075342 12110237247 021115 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_helpers.h" #include "core_commands2.h" #include "core_display.h" #include "core_phloat.h" #include "core_main.h" #include "core_variables.h" #include "shell.h" int resolve_ind_arg(arg_struct *arg) { vartype *v; switch (arg->type) { case ARGTYPE_IND_NUM: { vartype *regs = recall_var("REGS", 4); if (regs == NULL) return ERR_SIZE_ERROR; if (regs->type != TYPE_REALMATRIX) return ERR_INVALID_TYPE; else { vartype_realmatrix *rm = (vartype_realmatrix *) regs; int4 size = rm->rows * rm->columns; int4 num = arg->val.num; if (num >= size) return ERR_SIZE_ERROR; if (rm->array->is_string[num]) { int i; phloat *d = &rm->array->data[num]; arg->type = ARGTYPE_STR; arg->length = phloat_length(*d); for (i = 0; i < phloat_length(*d); i++) arg->val.text[i] = phloat_text(*d)[i]; } else { phloat x = rm->array->data[num]; if (x < 0) x = -x; if (x >= 2147483648.0) arg->val.num = 2147483647; else arg->val.num = to_int4(x); arg->type = ARGTYPE_NUM; } return ERR_NONE; } } case ARGTYPE_IND_STK: { switch (arg->val.stk) { case 'X': v = reg_x; break; case 'Y': v = reg_y; break; case 'Z': v = reg_z; break; case 'T': v = reg_t; break; case 'L': v = reg_lastx; break; } goto finish_resolve; } case ARGTYPE_IND_STR: { v = recall_var(arg->val.text, arg->length); if (v == NULL) return ERR_NONEXISTENT; finish_resolve: if (v->type == TYPE_REAL) { phloat x = ((vartype_real *) v)->x; if (x < 0) x = -x; if (x >= 2147483648.0) arg->val.num = 2147483647; else arg->val.num = to_int4(x); arg->type = ARGTYPE_NUM; return ERR_NONE; } else if (v->type == TYPE_STRING) { vartype_string *s = (vartype_string *) v; int i; arg->type = ARGTYPE_STR; arg->length = s->length; for (i = 0; i < s->length; i++) arg->val.text[i] = s->text[i]; return ERR_NONE; } else return ERR_INVALID_TYPE; } default: /* Should not happen; this function should only * be called to resolve indirect arguments. */ return ERR_INTERNAL_ERROR; } } int arg_to_num(arg_struct *arg, int4 *num) { if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (arg->type == ARGTYPE_NUM) { *num = arg->val.num; return ERR_NONE; } else return ERR_INVALID_TYPE; } int is_pure_real(const vartype *matrix) { vartype_realmatrix *rm; int4 size, i; if (matrix->type != TYPE_REALMATRIX) return 0; rm = (vartype_realmatrix *) matrix; size = rm->rows * rm->columns; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return 0; return 1; } void recall_result(vartype *v) { if (flags.f.stack_lift_disable) free_vartype(reg_x); else { free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = reg_x; } reg_x = v; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } void recall_two_results(vartype *x, vartype *y) { if (flags.f.stack_lift_disable) { free_vartype(reg_t); free_vartype(reg_x); reg_t = reg_z; reg_z = reg_y; } else { free_vartype(reg_t); free_vartype(reg_z); reg_t = reg_y; reg_z = reg_x; } reg_y = y; reg_x = x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } void unary_result(vartype *x) { free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = x; if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } void binary_result(vartype *x) { free_vartype(reg_lastx); reg_lastx = reg_x; reg_x = x; free_vartype(reg_y); reg_y = reg_z; reg_z = dup_vartype(reg_t); if (flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } phloat rad_to_angle(phloat x) { if (flags.f.rad) return x; else if (flags.f.grad) return x * (200 / PI); else return x * (180 / PI); } phloat rad_to_deg(phloat x) { return x * (180 / PI); } phloat deg_to_rad(phloat x) { return x / (180 / PI); } void append_alpha_char(char c) { if (reg_alpha_length == 44) { int i; for (i = 0; i < 43; i++) reg_alpha[i] = reg_alpha[i + 1]; reg_alpha[43] = c; } else reg_alpha[reg_alpha_length++] = c; } void append_alpha_string(const char *buf, int buflen, int reverse) { int needed, i; if (buflen > 44) { if (!reverse) buf += buflen - 44; buflen = 44; } needed = reg_alpha_length + buflen - 44; if (needed > 0) { for (i = 0; i < reg_alpha_length - needed; i++) reg_alpha[i] = reg_alpha[i + needed]; reg_alpha_length -= needed; } if (reverse) for (i = buflen - 1; i >= 0; i--) reg_alpha[reg_alpha_length++] = buf[i]; else for (i = 0; i < buflen; i++) reg_alpha[reg_alpha_length++] = buf[i]; } void string_copy(char *dst, int *dstlen, const char *src, int srclen) { int i; *dstlen = srclen; for (i = 0; i < srclen; i++) dst[i] = src[i]; } int string_equals(const char *s1, int s1len, const char *s2, int s2len) { int i; if (s1len != s2len) return 0; for (i = 0; i < s1len; i++) if (s1[i] != s2[i]) return 0; return 1; } int virtual_flag_handler(int flagop, int flagnum) { /* NOTE: the determination which flag numbers are handled by this * function is made by docmd_sf() etc.; they do this based on a constant * string 'virtual_flags' defined locally in core_commands.c. */ switch (flagnum) { case 27: /* custom_menu */ { int its_on = mode_plainmenu == MENU_CUSTOM1 || mode_plainmenu == MENU_CUSTOM2 || mode_plainmenu == MENU_CUSTOM3; switch (flagop) { case FLAGOP_SF: if (!its_on) set_menu(MENULEVEL_PLAIN, MENU_CUSTOM1); return ERR_NONE; case FLAGOP_CF: if (its_on) set_menu(MENULEVEL_PLAIN, MENU_NONE); return ERR_NONE; case FLAGOP_FS_T: return its_on ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return its_on ? ERR_NO : ERR_YES; case FLAGOP_FSC_T: if (its_on) set_menu(MENULEVEL_PLAIN, MENU_NONE); return its_on ? ERR_YES : ERR_NO; case FLAGOP_FCC_T: if (its_on) set_menu(MENULEVEL_PLAIN, MENU_NONE); return its_on ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 45: /* solving */ { switch (flagop) { case FLAGOP_FS_T: return solve_active() ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return solve_active() ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 46: /* integrating */ { switch (flagop) { case FLAGOP_FS_T: return integ_active() ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return integ_active() ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 47: /* variable_menu */ { int its_on = mode_appmenu == MENU_VARMENU && varmenu_role == 0; switch (flagop) { case FLAGOP_FS_T: return its_on ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return its_on ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 49: /* low_battery */ { int lowbat = shell_low_battery(); switch (flagop) { case FLAGOP_FS_T: return lowbat ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return lowbat ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 53: /* input */ { switch (flagop) { case FLAGOP_FS_T: return input_length > 0 ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return input_length > 0 ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 65: /* matrix_editor */ { int its_on = matedit_mode == 2 || matedit_mode == 3; switch (flagop) { case FLAGOP_FS_T: return its_on ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return its_on ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } case 75: /* programmable_menu */ { int its_on = mode_plainmenu = MENU_PROGRAMMABLE; switch (flagop) { case FLAGOP_FS_T: return its_on ? ERR_YES : ERR_NO; case FLAGOP_FC_T: return its_on ? ERR_NO : ERR_YES; default: return ERR_INTERNAL_ERROR; } } default: return ERR_INTERNAL_ERROR; } } int get_base() { int base = 0; if (flags.f.base_bit0) base += 1; if (flags.f.base_bit1) base += 2; if (flags.f.base_bit2) base += 4; if (flags.f.base_bit3) base += 8; if (base == 1 || base == 7 || base == 15) return base + 1; else return 10; } void set_base(int base) { int oldbase = 0; if (flags.f.base_bit0) oldbase += 1; if (flags.f.base_bit1) oldbase += 2; if (flags.f.base_bit2) oldbase += 4; if (flags.f.base_bit3) oldbase += 8; if (base == 2 || base == 8 || base == 16) base--; else base = 0; flags.f.base_bit0 = (base & 1) != 0; flags.f.base_bit1 = (base & 2) != 0; flags.f.base_bit2 = (base & 4) != 0; flags.f.base_bit3 = (base & 8) != 0; if (mode_appmenu == MENU_BASE_A_THRU_F) set_menu(MENULEVEL_APP, MENU_BASE); if (base != oldbase && flags.f.trace_print && flags.f.printer_exists) docmd_prx(NULL); } int get_base_param(const vartype *v, int8 *n) { if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (v->type != TYPE_REAL) return ERR_INVALID_TYPE; phloat x = ((vartype_real *) v)->x; if (x > 34359738367.0 || x < -34359738368.0) return ERR_INVALID_DATA; int8 t = to_int8(x); if ((t & LL(0x800000000)) != 0) *n = t | LL(0xfffffff000000000); else *n = t; return ERR_NONE; } int base_range_check(int8 *n) { if (*n < LL(-34359738368)) { if (flags.f.range_error_ignore) *n = LL(-34359738368); else return ERR_OUT_OF_RANGE; } else if (*n > LL(34359738367)) { if (flags.f.range_error_ignore) *n = LL(34359738367); else return ERR_OUT_OF_RANGE; } return ERR_NONE; } void print_text(const char *text, int length, int left_justified) { /* TODO: check preferences so we don't waste any time generating * print-outs that aren't going to be accepted by the shell anyway */ char buf[24]; int width = flags.f.double_wide_print ? 12 : 24; int bufptr = 0, i; char bitmap[162]; /* Handle left/right justification and lowercase printing */ if (length > width) length = width; if (!left_justified) for (i = length; i < width; i++) buf[bufptr++] = ' '; for (i = 0; i < length; i++) { char c = text[i]; if (flags.f.lowercase_print && (c & 127) >= 'A' && (c & 127) <= 'Z') c += 32; else if (c == 10) c = (char) 138; buf[bufptr++] = c; } /* Do bit-mapped printing */ for (i = 0; i < 162; i++) bitmap[i] = 0; for (i = 0; i < bufptr; i++) { char charbits[5]; int j; get_char(charbits, buf[i]); for (j = 0; j < 5; j++) { int x1 = i * 6 + j; int x2 = x1 + 1; int x, y; if (flags.f.double_wide_print) { x1 <<= 1; x2 <<= 1; } for (x = x1; x < x2; x++) for (y = 0; y < 8; y++) if ((charbits[j] & (1 << y)) != 0) bitmap[y * 18 + (x >> 3)] |= 1 << (x & 7); } } /* Handle text-mode double-width printing by inserting spaces and * underscores; do text-mode printing */ if (flags.f.double_wide_print) { for (i = bufptr - 1; i >= 0; i--) { char c = buf[i]; buf[2 * i] = c; buf[2 * i + 1] = c == ' ' ? ' ' : '_'; } bufptr *= 2; } shell_print(buf, bufptr, bitmap, 18, 0, 0, 143, 9); } void print_lines(const char *text, int length, int left_justified) { int line_start = 0; int width = flags.f.double_wide_print ? 12 : 24; while (line_start + width < length) { print_text(text + line_start, width, left_justified); line_start += width; } print_text(text + line_start, length - line_start, left_justified); } void print_right(const char *left, int leftlen, const char *right, int rightlen) { char buf[100]; int len; int width = flags.f.double_wide_print ? 12 : 24; int i, pad; string_copy(buf, &len, left, leftlen); if (len + rightlen + 1 <= width) { buf[len++] = ' '; pad = width - len - rightlen; if (pad > 6 - rightlen) pad = 6 - rightlen; if (pad < 0) pad = 0; for (i = 0; i < pad; i++) buf[len++] = ' '; for (i = 0; i < rightlen; i++) buf[len++] = right[i]; print_text(buf, len, 0); } else { int line_start = 0; while (line_start + width < len) { print_text(buf + line_start, width, 1); line_start += width; } pad = width - (len - line_start) - rightlen; if (pad < 1) pad = 1; for (i = 0; i < pad; i++) buf[len++] = ' '; for (i = 0; i < rightlen; i++) buf[len++] = right[i]; print_lines(buf + line_start, len - line_start, 1); } } void print_wide(const char *left, int leftlen, const char *right, int rightlen) { int width = flags.f.double_wide_print ? 12 : 24; char buf[24]; int bufptr = 0, i; if (leftlen + rightlen <= width) { for (i = 0; i < leftlen; i++) buf[bufptr++] = left[i]; for (i = width - leftlen - rightlen; i > 0; i--) buf[bufptr++] = ' '; for (i = 0; i < rightlen; i++) buf[bufptr++] = right[i]; print_text(buf, bufptr, 1); } else { for (i = 0; i < leftlen; i++) { buf[bufptr++] = left[i]; if (bufptr == width) { print_text(buf, width, 1); bufptr = 0; } } for (i = 0; i < rightlen; i++) { buf[bufptr++] = right[i]; if (bufptr == width) { print_text(buf, width, 1); bufptr = 0; } } if (bufptr > 0) print_text(buf, bufptr, 1); } } void print_command(int cmd, const arg_struct *arg) { char buf[100]; int bufptr = 0; if (cmd == CMD_NULL && !deferred_print) return; shell_annunciators(-1, -1, 1, -1, -1, -1); if (cmd != CMD_NULL) bufptr += command2buf(buf, 100, cmd, arg); if (deferred_print) { /* If the display mode is FIX n, and the user has not entered * an exponent, pad the fractional part to n, in order to get * decimal points to line up. */ if (flags.f.fix_or_all && !flags.f.eng_or_all) { int i, n, dot_found = 0, ip_digits = 0, fp_digits = 0; char dot = flags.f.decimal_point ? '.' : ','; for (i = 0; i < cmdline_length; i++) { char c = cmdline[i]; if (c == 24) goto never_mind; if (c == dot) dot_found = 1; if (c >= '0' && c <= '9') { if (dot_found) fp_digits++; else ip_digits++; } } n = 0; if (flags.f.digits_bit3) n += 8; if (flags.f.digits_bit2) n += 4; if (flags.f.digits_bit1) n += 2; if (flags.f.digits_bit0) n += 1; if (!dot_found) cmdline[cmdline_length++] = dot; while (fp_digits < n && ip_digits + fp_digits < 12) { cmdline[cmdline_length++] = '0'; fp_digits++; } never_mind:; } print_right(cmdline, cmdline_length, buf, bufptr); } else { /* Normally we print commands right-justified, but if they don't fit * on one line, we print them left-justified, because having the excess * go near the right margin looks weird and confusing. */ int left = bufptr > (flags.f.double_wide_print ? 12 : 24); print_lines(buf, bufptr, left); } deferred_print = 0; shell_annunciators(-1, -1, 0, -1, -1, -1); } void generic_r2p(phloat re, phloat im, phloat *r, phloat *phi) { if (im == 0) { if (re >= 0) { *r = re; *phi = 0; } else { *r = -re; *phi = flags.f.grad ? 200 : flags.f.rad ? PI : 180; } } else if (re == 0) { if (im > 0) { *r = im; *phi = flags.f.grad ? 100 : flags.f.rad ? PI / 2 : 90; } else { *r = -im; *phi = flags.f.grad ? -100 : flags.f.rad ? -PI / 2: -90; } } else { *r = hypot(re, im); *phi = rad_to_angle(atan2(im, re)); } } void generic_p2r(phloat r, phloat phi, phloat *re, phloat *im) { phloat tre, tim; if (flags.f.rad) { sincos(phi, &tim, &tre); } else if (flags.f.grad) { phi = fmod(phi, 400); if (phi < 0) phi += 400; if (phi == 0) { tre = 1; tim = 0; } else if (phi == 100) { tre = 0; tim = 1; } else if (phi == 200) { tre = -1; tim = 0; } else if (phi == 300) { tre = 0; tim = -1; } else sincos(phi / (200 / PI), &tim, &tre); } else { phi = fmod(phi, 360); if (phi < 0) phi += 360; if (phi == 0) { tre = 1; tim = 0; } else if (phi == 90) { tre = 0; tim = 1; } else if (phi == 180) { tre = -1; tim = 0; } else if (phi == 270) { tre = 0; tim = -1; } else sincos(phi / (180 / PI), &tim, &tre); } *re = r * tre; *im = r * tim; } int dimension_array(const char *name, int namelen, int4 rows, int4 columns) { vartype *matrix = recall_var(name, namelen); /* NOTE: 'size' will only ever be 0 when we're called from * docmd_size(); docmd_dim() does not allow 0-size matrices. */ int4 size = rows * columns; if (matrix == NULL || (matrix->type != TYPE_REALMATRIX && matrix->type != TYPE_COMPLEXMATRIX)) { vartype *newmatrix; if (size == 0) return ERR_NONE; newmatrix = new_realmatrix(rows, columns); if (newmatrix == NULL) return ERR_INSUFFICIENT_MEMORY; store_var(name, namelen, newmatrix); return ERR_NONE; } else if (size == 0) { purge_var(name, namelen); return ERR_NONE; } else return dimension_array_ref(matrix, rows, columns); } int dimension_array_ref(vartype *matrix, int4 rows, int4 columns) { int4 size = rows * columns; if (matrix->type == TYPE_REALMATRIX) { vartype_realmatrix *oldmatrix = (vartype_realmatrix *) matrix; if (oldmatrix->rows == rows && oldmatrix->columns == columns) return ERR_NONE; if (oldmatrix->array->refcount == 1) { /* Since there are no shared references to this array, * I can modify it in place using a realloc(). However, I * only use realloc() on the 'data' array, not on the * 'is_string' array -- if I used it on both, and the second * call fails, I might be unable to roll back the first. * So, playing safe -- shouldn't be too big a handicap since * 'is_string' is a lot smaller than 'data', so the transient * memory overhead is only about 12.5%. */ char *new_is_string = (char *) malloc(size); if (new_is_string == NULL) return ERR_INSUFFICIENT_MEMORY; int4 i, s, oldsize; phloat *new_data = (phloat *) realloc(oldmatrix->array->data, size * sizeof(phloat)); if (new_data == NULL) { free(new_is_string); return ERR_INSUFFICIENT_MEMORY; } oldsize = oldmatrix->rows * oldmatrix->columns; s = oldsize < size ? oldsize : size; for (i = 0; i < s; i++) new_is_string[i] = oldmatrix->array->is_string[i]; for (i = s; i < size; i++) { new_is_string[i] = 0; new_data[i] = 0; } free(oldmatrix->array->is_string); oldmatrix->array->is_string = new_is_string; oldmatrix->array->data = new_data; oldmatrix->rows = rows; oldmatrix->columns = columns; return ERR_NONE; } else { /* There are shared references to the matrix. This means I * can't realloc() it; I'm going to allocate a brand-new instance, * and copy the contents from the old instance. I don't use * disentangle(); that's only useful if you want to eliminate * shared references without resizing. */ realmatrix_data *new_array; int4 i, s, oldsize; new_array = (realmatrix_data *) malloc(sizeof(realmatrix_data)); if (new_array == NULL) return ERR_INSUFFICIENT_MEMORY; new_array->data = (phloat *) malloc(size * sizeof(phloat)); if (new_array->data == NULL) { free(new_array); return ERR_INSUFFICIENT_MEMORY; } new_array->is_string = (char *) malloc(size); if (new_array->is_string == NULL) { free(new_array->data); free(new_array); return ERR_INSUFFICIENT_MEMORY; } oldsize = oldmatrix->rows * oldmatrix->columns; s = oldsize < size ? oldsize : size; for (i = 0; i < s; i++) { new_array->is_string[i] = oldmatrix->array->is_string[i]; new_array->data[i] = oldmatrix->array->data[i]; } for (i = s; i < size; i++) { new_array->is_string[i] = 0; new_array->data[i] = 0; } new_array->refcount = 1; oldmatrix->array->refcount--; oldmatrix->array = new_array; oldmatrix->rows = rows; oldmatrix->columns = columns; return ERR_NONE; } } else /* matrix->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *oldmatrix = (vartype_complexmatrix *) matrix; if (oldmatrix->rows == rows && oldmatrix->columns == columns) return ERR_NONE; if (oldmatrix->array->refcount == 1) { /* Since there are no shared references to this array, * I can modify it in place using a realloc(). */ int4 i, oldsize; phloat *new_data = (phloat *) realloc(oldmatrix->array->data, 2 * size * sizeof(phloat)); if (new_data == NULL) return ERR_INSUFFICIENT_MEMORY; oldsize = oldmatrix->rows * oldmatrix->columns; for (i = 2 * oldsize; i < 2 * size; i++) new_data[i] = 0; oldmatrix->array->data = new_data; oldmatrix->rows = rows; oldmatrix->columns = columns; return ERR_NONE; } else { /* There are shared references to the matrix. This means I * can't realloc() it; I'm going to allocate a brand-new instance, * and copy the contents from the old instance. I don't use * disentangle(); that's only useful if you want to eliminate * shared references without resizing. */ complexmatrix_data *new_array; int4 i, s, oldsize; new_array = (complexmatrix_data *) malloc(sizeof(complexmatrix_data)); if (new_array == NULL) return ERR_INSUFFICIENT_MEMORY; new_array->data = (phloat *) malloc(2 * size * sizeof(phloat)); if (new_array->data == NULL) { free(new_array); return ERR_INSUFFICIENT_MEMORY; } oldsize = oldmatrix->rows * oldmatrix->columns; s = oldsize < size ? oldsize : size; for (i = 0; i < 2 * s; i++) new_array->data[i] = oldmatrix->array->data[i]; for (i = 2 * s; i < 2 * size; i++) new_array->data[i] = 0; new_array->refcount = 1; oldmatrix->array->refcount--; oldmatrix->array = new_array; oldmatrix->rows = rows; oldmatrix->columns = columns; return ERR_NONE; } } } phloat fix_hms(phloat x) { #ifdef BCD_MATH const phloat sec_corr(4, 1000); const phloat min_corr(4, 10); #endif bool neg = x < 0; if (neg) x = -x; if (x == x + 1) return neg ? -x : x; if (x < 0.0059) return neg ? -x : x; #ifdef BCD_MATH if (floor(fmod(x * 10000, 100)) == 60) x += sec_corr; if (floor(fmod(x * 100, 100)) == 60) { x += min_corr; if (floor(fmod(x * 10000, 100)) == 60) x += sec_corr; } #else if (to_int8(x * 10000) % 100 == 60) x += 0.004; if (to_int8(x * 100) % 100 == 60) { x += 0.4; if (to_int8(x * 10000) % 100 == 60) x += 0.004; } #endif return neg ? -x : x; } #if defined(NO_SINCOS) && !defined(BCD_MATH) void sincos(double x, double *sinx, double *cosx) { *sinx = sin(x); *cosx = cos(x); } #endif void char2buf(char *buf, int buflen, int *bufptr, char c) { if (*bufptr < buflen) buf[(*bufptr)++] = c; else buf[buflen - 1] = 26; } void string2buf(char *buf, int buflen, int *bufptr, const char *s, int slen) { int i; for (i = 0; i < slen; i++) char2buf(buf, buflen, bufptr, s[i]); } int uint2string(uint4 n, char *buf, int buflen) { uint4 pt = 1; int count = 0; while (n / pt >= 10) pt *= 10; while (pt != 0) { char2buf(buf, buflen, &count, (char) ('0' + (n / pt) % 10)); pt /= 10; } return count; } int int2string(int4 n, char *buf, int buflen) { uint4 u; int count = 0; if (n < 0) { char2buf(buf, buflen, &count, '-'); u = -n; } else u = n; return count + uint2string(u, buf + count, buflen - count); } int vartype2string(const vartype *v, char *buf, int buflen) { int dispmode; int digits = 0; if (flags.f.fix_or_all) dispmode = flags.f.eng_or_all ? 3 : 0; else dispmode = flags.f.eng_or_all ? 2 : 1; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; switch (v->type) { case TYPE_REAL: return phloat2string(((vartype_real *) v)->x, buf, buflen, 1, digits, dispmode, flags.f.thousands_separators); case TYPE_COMPLEX: { phloat x, y; char x_buf[22]; int x_len; char y_buf[22]; int y_len; int i, ret_len; if (flags.f.polar) { generic_r2p(((vartype_complex *) v)->re, ((vartype_complex *) v)->im, &x, &y); if (p_isinf(x)) x = POS_HUGE_PHLOAT; } else { x = ((vartype_complex *) v)->re; y = ((vartype_complex *) v)->im; } x_len = phloat2string(x, x_buf, 22, 0, digits, dispmode, flags.f.thousands_separators); y_len = phloat2string(y, y_buf, 22, 0, digits, dispmode, flags.f.thousands_separators); if (x_len + y_len + 2 > buflen) { /* Too long? Fall back on ENG 2 */ x_len = phloat2string(x, x_buf, 22, 0, 2, 2, flags.f.thousands_separators); y_len = phloat2string(y, y_buf, 22, 0, 2, 2, flags.f.thousands_separators); } for (i = 0; i < buflen; i++) { if (i < x_len) buf[i] = x_buf[i]; else if (i < x_len + 1) buf[i] = ' '; else if (i < x_len + 2) { if (y_buf[0] == '-' && !flags.f.polar) buf[i] = '-'; else buf[i] = flags.f.polar ? 23 : 'i'; } else if (i < x_len + 3) { if (y_buf[0] == '-' && !flags.f.polar) buf[i] = flags.f.polar ? 23 : 'i'; else buf[i] = y_buf[0]; } else buf[i] = y_buf[i - x_len - 2]; } ret_len = x_len + y_len + 2; if (ret_len > buflen) { buf[buflen - 1] = 26; ret_len = buflen; } return ret_len; } case TYPE_REALMATRIX: { vartype_realmatrix *m = (vartype_realmatrix *) v; int i; int chars_so_far = 0; string2buf(buf, buflen, &chars_so_far, "[ ", 2); i = int2string(m->rows, buf + chars_so_far, buflen - chars_so_far); chars_so_far += i; char2buf(buf, buflen, &chars_so_far, 'x'); i = int2string(m->columns, buf + chars_so_far, buflen - chars_so_far); chars_so_far += i; string2buf(buf, buflen, &chars_so_far, " Matrix ]", 9); return chars_so_far; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *m = (vartype_complexmatrix *) v; int i; int chars_so_far = 0; string2buf(buf, buflen, &chars_so_far, "[ ", 2); i = int2string(m->rows, buf + chars_so_far, buflen - chars_so_far); chars_so_far += i; char2buf(buf, buflen, &chars_so_far, 'x'); i = int2string(m->columns, buf + chars_so_far, buflen - chars_so_far); chars_so_far += i; string2buf(buf, buflen, &chars_so_far, " Cpx Matrix ]", 13); return chars_so_far; } case TYPE_STRING: { vartype_string *s = (vartype_string *) v; int i; int chars_so_far = 0; char2buf(buf, buflen, &chars_so_far, '"'); for (i = 0; i < s->length; i++) char2buf(buf, buflen, &chars_so_far, s->text[i]); char2buf(buf, buflen, &chars_so_far, '"'); return chars_so_far; } default: { const char *msg = "UnsuppVarType"; int msglen = 13; int i; for (i = 0; i < msglen; i++) buf[i] = msg[i]; return msglen; } } } char *phloat2program(phloat d) { /* Converts a phloat to its most compact representation; * used for generating HP-42S style number literals in programs. */ static char allbuf[25]; static char scibuf[25]; int alllen; int scilen; char dot = flags.f.decimal_point ? '.' : ','; int decimal, zeroes, last_nonzero, exponent; int i; alllen = phloat2string(d, allbuf, 24, 0, 0, 3, 0); scilen = phloat2string(d, scibuf, 24, 0, 11, 1, 0); /* Shorten SCI representation by removing trailing zeroes, * and decreasing the exponent until the decimal point * shifts out of the mantissa. */ decimal = -1; exponent = -1; for (i = 0; i < scilen; i++) { char c = scibuf[i]; if (c == dot) { decimal = i; last_nonzero = i; zeroes = 0; } else if (c == 24) { exponent = i; break; } else if (c != '0') { last_nonzero = i; zeroes = 0; } else zeroes++; } if (decimal != -1) { if (zeroes > 0) { for (i = last_nonzero + 1; i < scilen - zeroes; i++) scibuf[i] = scibuf[i + zeroes]; scilen -= zeroes; } if ((exponent == -1 && decimal == scilen - 1) || (exponent != -1 && exponent - decimal == 1)){ for (i = decimal; i < scilen - 1; i++) scibuf[i] = scibuf[i + 1]; scilen--; } else if (exponent != -1) { int offset, ex, neg, newexplen, t; exponent -= zeroes; offset = exponent - decimal - 1; ex = 0; neg = 0; for (i = exponent + 1; i < scilen; i++) { char c = scibuf[i]; if (c == '-') neg = 1; else ex = ex * 10 + c - '0'; } if (neg) ex = -ex; ex -= offset; if (ex < 0) { ex = -ex; neg = 1; } else neg = 0; newexplen = neg ? 2 : 1; t = 10; while (ex >= t) { newexplen++; t *= 10; } if (newexplen <= scilen - exponent - 1) { for (i = decimal; i < exponent; i++) scibuf[i] = scibuf[i + 1]; scilen = exponent; if (neg) ex = -ex; scilen += int2string(ex, scibuf + exponent, 50 - exponent); } } } if (scilen < alllen) { scibuf[scilen] = 0; return scibuf; } else { allbuf[alllen] = 0; return allbuf; } } int easy_phloat2string(phloat d, char *buf, int buflen, int base_mode) { int dispmode; int digits = 0; if (flags.f.fix_or_all) dispmode = flags.f.eng_or_all ? 3 : 0; else dispmode = flags.f.eng_or_all ? 2 : 1; if (flags.f.digits_bit3) digits += 8; if (flags.f.digits_bit2) digits += 4; if (flags.f.digits_bit1) digits += 2; if (flags.f.digits_bit0) digits += 1; return phloat2string(d, buf, buflen, base_mode, digits, dispmode, flags.f.thousands_separators); } int ip2revstring(phloat d, char *buf, int buflen) { int s = 1; int bufpos = 0; if (d < 0) { d = -d; s = -1; } d = floor(d); while (d != 0 && bufpos < buflen) { char c = '0' + to_digit(d); buf[bufpos++] = c; d = floor(d / 10); } if (bufpos == 0) buf[bufpos++] = '0'; if (s == -1 && bufpos < buflen) buf[bufpos++] = '-'; return bufpos; } free42-nologo-1.4.77/common/core_helpers.h000644 000765 000024 00000006063 12110237247 020751 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_HELPERS_H #define CORE_HELPERS_H 1 #include "free42.h" #include "core_phloat.h" #include "core_globals.h" /*********************/ /* Utility functions */ /*********************/ int resolve_ind_arg(arg_struct *arg); int arg_to_num(arg_struct *arg, int4 *num); int is_pure_real(const vartype *matrix); void recall_result(vartype *v); void recall_two_results(vartype *x, vartype *y); void unary_result(vartype *x); void binary_result(vartype *x); phloat rad_to_angle(phloat x); phloat rad_to_deg(phloat x); phloat deg_to_rad(phloat x); void append_alpha_char(char c); void append_alpha_string(const char *buf, int buflen, int reverse); void string_copy(char *dst, int *dstlen, const char *src, int srclen); int string_equals(const char *s1, int s1len, const char *s2, int s2len); #define FLAGOP_SF 0 #define FLAGOP_CF 1 #define FLAGOP_FS_T 2 #define FLAGOP_FC_T 3 #define FLAGOP_FSC_T 4 #define FLAGOP_FCC_T 5 int virtual_flag_handler(int flagop, int flagnum); int get_base(); void set_base(int base); int get_base_param(const vartype *v, int8 *n); int base_range_check(int8 *n); void print_text(const char *text, int length, int left_justified); void print_lines(const char *text, int length, int left_justified); void print_right(const char *left, int leftlen, const char *right, int rightlen); void print_wide(const char *left, int leftlen, const char *right, int rightlen); void print_command(int cmd, const arg_struct *arg); void generic_r2p(phloat re, phloat im, phloat *r, phloat *phi); void generic_p2r(phloat r, phloat phi, phloat *re, phloat *im); /***********************/ /* Miscellaneous stuff */ /***********************/ int dimension_array(const char *name, int namelen, int4 rows, int4 columns); int dimension_array_ref(vartype *matrix, int4 rows, int4 columns); phloat fix_hms(phloat x); void char2buf(char *buf, int buflen, int *bufptr, char c); void string2buf(char *buf, int buflen, int *bufptr, const char *s, int slen); int uint2string(uint4 n, char *buf, int buflen); int int2string(int4 n, char *buf, int buflen); int vartype2string(const vartype *v, char *buf, int buflen); char *phloat2program(phloat d); int easy_phloat2string(phloat d, char *buf, int buflen, int base_mode); int ip2revstring(phloat d, char *buf, int buflen); #endif free42-nologo-1.4.77/common/core_keydown.cc000644 000765 000024 00000203651 12110237247 021127 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_keydown.h" #include "core_commands2.h" #include "core_commands3.h" #include "core_display.h" #include "core_helpers.h" #include "core_main.h" #include "core_math1.h" #include "core_tables.h" #include "core_variables.h" #include "shell.h" static int is_number_key(int shift, int key) { int *menu = get_front_menu(); if (menu != NULL && *menu == MENU_BASE_A_THRU_F && (key == KEY_SIGMA || key == KEY_INV || key == KEY_SQRT || key == KEY_LOG || key == KEY_LN || key == KEY_XEQ)) return 1; return !shift && (key == KEY_0 || key == KEY_1 || key == KEY_2 || key == KEY_3 || key == KEY_4 || key == KEY_5 || key == KEY_6 || key == KEY_7 || key == KEY_8 || key == KEY_9 || key == KEY_DOT || key == KEY_E); } static int basekeys() { if (!baseapp) return 0; int *menu = get_front_menu(); return menu != NULL && (*menu == MENU_BASE || *menu == MENU_BASE_A_THRU_F || *menu == MENU_BASE_LOGIC); } static void set_solve_integ(int solve) { if (flags.f.prgm_mode || !mvar_prgms_exist()) { set_menu(MENULEVEL_APP, solve ? MENU_SOLVE : MENU_INTEG); if (!flags.f.prgm_mode) display_error(ERR_NO_MENU_VARIABLES, 0); } else { int err = set_menu_return_err(MENULEVEL_APP, MENU_CATALOG); if (err == ERR_NONE) { set_cat_section(solve ? CATSECT_PGM_SOLVE : CATSECT_PGM_INTEG); move_cat_row(0); clear_row(0); if (solve) draw_string(0, 0, "Select Solve Program", 20); else draw_string(0, 0, "Select \003f(x) Program", 20); flags.f.message = 1; flags.f.two_line_message = 0; } else display_error(err, 1); } redisplay(); } static void view(const char *varname, int varlength) { arg_struct arg; int i, err; arg.type = ARGTYPE_STR; arg.length = varlength; for (i = 0; i < varlength; i++) arg.val.text[i] = varname[i]; err = view_helper(&arg, 0); if (err != ERR_NONE) { display_error(err, 1); flush_display(); pending_command = CMD_NONE; } else { pending_command = CMD_LINGER1; shell_request_timeout3(2000); } } void keydown(int shift, int key) { int *menu; pending_command = CMD_NONE; if (key >= 1024) { /* TODO: is this actually needed? * Filtering out ASCII key events if the alpha menu is not actually * active, just in case the subsequent key handling code doesn't * handle them properly in all cases. Using a code coverage tool on * the keydown handler might be an idea... */ menu = get_front_menu(); if (menu == NULL || *menu < MENU_ALPHA1 || *menu > MENU_ALPHA_MISC2) return; } else if (key < 1 || key > 37) { /* Bad key code */ squeak(); return; } if (mode_clall) { if (!shift && key == KEY_SIGMA) pending_command = CMD_CLALLb; else if (key == KEY_EXIT) pending_command = CMD_CANCELLED; else pending_command = CMD_NULL; mode_clall = false; return; } if (mode_getkey) { vartype *result = new_real(shift ? key + 37 : key); if (result != NULL) { recall_result(result); flags.f.stack_lift_disable = 0; } else { display_error(ERR_INSUFFICIENT_MEMORY, 1); set_running(false); } if (key == KEY_EXIT || (!shift && key == KEY_RUN)) set_running(false); mode_getkey = false; if (!mode_running) redisplay(); return; } if (shift && key == KEY_EXIT) { pending_command = CMD_SILENT_OFF; return; } if (shift && key == KEY_RUN) { if (mode_command_entry) { squeak(); return; } if (flags.f.prgm_mode) { if (mode_alpha_entry) finish_alpha_prgm_line(); else if (mode_number_entry) { arg_struct arg; arg.type = ARGTYPE_DOUBLE; arg.val_d = entered_number; store_command(pc, CMD_NUMBER, &arg); prgm_highlight_row = 1; } } else if (mode_alpha_entry) { if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) docmd_pra(NULL); } else if (mode_number_entry) { if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) { deferred_print = 1; print_command(CMD_NULL, NULL); } } mode_alpha_entry = false; mode_number_entry = false; flags.f.prgm_mode = !flags.f.prgm_mode; mode_varmenu = false; if (flags.f.prgm_mode) { if (mode_appmenu == MENU_BASE_A_THRU_F) set_menu(MENULEVEL_APP, MENU_BASE); else if (mode_plainmenu == MENU_PROGRAMMABLE) set_menu(MENULEVEL_PLAIN, MENU_NONE); input_length = 0; } flags.f.message = 0; flags.f.two_line_message = 0; redisplay(); return; } if (flags.f.message && !shift && key == KEY_BSP) { flags.f.message = 0; flags.f.two_line_message = 0; redisplay(); return; } flags.f.message = 0; flags.f.two_line_message = 0; if (mode_number_entry && get_base() == 16 && key == KEY_SIGMA && (menu = get_front_menu()) != NULL && *menu == MENU_BASE) { /* Special case -- entering the A...F menu while in base 16 * does *not* cancel number entry mode (unlike all other menu * keys)... So we intercept and handle it before all the other * logic can mess things up. */ keydown_number_entry(-1, -1); return; } if (mode_number_entry && !is_number_key(shift, key) && (key != KEY_CHS || shift || basekeys() || get_base() != 10) && (key != KEY_BSP || shift)) { /* Leaving number entry mode */ mode_number_entry = false; if (flags.f.prgm_mode) { arg_struct arg; arg.type = ARGTYPE_DOUBLE; arg.val_d = entered_number; store_command(pc, CMD_NUMBER, &arg); prgm_highlight_row = 1; } else if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) deferred_print = 1; } if (mode_command_entry && shift && (key == KEY_UP || key == KEY_DOWN)) { /* Trying to do SST or BST while in command entry mode */ squeak(); return; } if (key == KEY_UP || (key == KEY_DOWN && (flags.f.prgm_mode || (!shift && get_front_menu() != NULL)))) { /* UP, DOWN, BST, or prgm-mode SST */ repeating = 1; repeating_shift = shift; repeating_key = key; } if (flags.f.prgm_mode && (key == KEY_UP || key == KEY_DOWN) && (shift || get_front_menu() == NULL)) { /* Stepping through the program in prgm mode */ if (flags.f.prgm_mode && mode_alpha_entry) finish_alpha_prgm_line(); clear_all_rtns(); if (key == KEY_UP) bst(); else sst(); redisplay(); return; } if (!flags.f.prgm_mode && key == KEY_UP && (shift || get_front_menu() == NULL)) { /* BST in normal or alpha mode */ if (mode_alpha_entry && (flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) docmd_pra(NULL); mode_alpha_entry = false; clear_all_rtns(); bst(); flags.f.prgm_mode = 1; redisplay(); flags.f.prgm_mode = 0; pending_command = CMD_CANCELLED; return; } if (mode_number_entry) keydown_number_entry(shift, key); else if (mode_command_entry) keydown_command_entry(shift, key); else if (flags.f.alpha_mode) keydown_alpha_mode(shift, key); else keydown_normal_mode(shift, key); } void keydown_number_entry(int shift, int key) { phloat x; char buf[100]; int bufptr; int base = get_base(); if (key == -1) { /* Hack... The user is switching to the A...F menu */ set_menu(MENULEVEL_APP, MENU_BASE_A_THRU_F); redisplay(); goto draw_number; } if (base != 10 && (key == KEY_E || key == KEY_DOT)) return; /* NOTE: 'key' can only be KEY_CHS at this point in the code * if 'baseapp' is false and get_base() returns 10; in all other * cases, the +/- key will end number entry mode and we won't * get here, and the CMD_CHS or CMD_BASECHS function will be * invoked instead. */ if (key == KEY_BSP && cmdline_length == 1) { mode_number_entry = false; if (flags.f.prgm_mode) { pc = line2pc(pc2line(pc) - 1); prgm_highlight_row = 0; redisplay(); return; } else { pending_command = CMD_CLX; return; } } if (key == KEY_BSP) { cmdline_length--; if (!flags.f.prgm_mode && base == 10) fix_thousands_separators(cmdline, &cmdline_length); if (core_settings.auto_repeat) { repeating = 2; repeating_key = key; repeating_shift = shift; } } else if (key == KEY_CHS) { /* Check if mantissa or exponent gets the sign change */ int i; int exp_pos = -1; for (i = 0; i < cmdline_length; i++) if (cmdline[i] == 24) { exp_pos = i; break; } if (exp_pos != -1) { /* Change exponent sign */ int failed = 0; phloat d; undo_chs_exp: if (cmdline_length > exp_pos + 1 && cmdline[exp_pos + 1] == '-') { for (i = exp_pos + 1; i < cmdline_length - 1; i++) cmdline[i] = cmdline[i + 1]; cmdline_length--; } else { for (i = cmdline_length; i > exp_pos + 1; i--) cmdline[i] = cmdline[i - 1]; cmdline[exp_pos + 1] = '-'; cmdline_length++; } if (!failed) { if (string2phloat(cmdline, cmdline_length, &d) != 0) { failed = 1; goto undo_chs_exp; } } else return; } else { /* Change mantissa sign */ if (cmdline[0] == '-') { if (cmdline_length == 1) { if (flags.f.prgm_mode) { mode_number_entry = false; pc = line2pc(pc2line(pc) - 1); prgm_highlight_row = 0; redisplay(); return; } else { /* This is a bit odd, but it's how the HP-42S * does it, so there. */ mode_number_entry = false; free_vartype(reg_x); reg_x = new_real(0); pending_command = CMD_CLX; return; } } for (i = 0; i < cmdline_length - 1; i++) cmdline[i] = cmdline[i + 1]; cmdline_length--; } else { for (i = cmdline_length; i > 0; i--) cmdline[i] = cmdline[i - 1]; cmdline[0] = '-'; cmdline_length++; } } } else if (key == KEY_E) { int exp_pos = -1; int only_zeroes = 1; int seen_dot = 0; char dot = flags.f.decimal_point ? '.' : ','; int i; for (i = 0; i < cmdline_length; i++) { char c = cmdline[i]; if (c >= '1' && c <= '9') only_zeroes = 0; else if (c == dot) seen_dot = 1; else if (c == 24) { exp_pos = i; break; } } if (exp_pos == -1) { if (only_zeroes) { if (cmdline_length > 0 && cmdline[0] == '-') cmdline_length = 1; else cmdline_length = 0; cmdline[cmdline_length++] = '1'; if (seen_dot) cmdline[cmdline_length++] = dot; } cmdline[cmdline_length++] = 24; } else return; } else if (key == KEY_DOT) { if (cmdline_length == 0 || (cmdline_length == 1 && cmdline[0] == '-')) { cmdline[cmdline_length++] = '0'; cmdline[cmdline_length++] = flags.f.decimal_point ? '.' : ','; } else { /* Only allow dot if there isn't one already, and * there is no exponent either */ int dot_or_exp_pos = -1; int i; char dot = flags.f.decimal_point ? '.' : ','; for (i = 0; i < cmdline_length; i++) if (cmdline[i] == dot || cmdline[i] == 24) { dot_or_exp_pos = i; break; } if (dot_or_exp_pos == -1) cmdline[cmdline_length++] = dot; else return; } } else /* KEY_0 .. KEY_9 or hex A-F */ { int digit; char c; switch (key) { case KEY_0: digit = 0; break; case KEY_1: digit = 1; break; case KEY_2: digit = 2; break; case KEY_3: digit = 3; break; case KEY_4: digit = 4; break; case KEY_5: digit = 5; break; case KEY_6: digit = 6; break; case KEY_7: digit = 7; break; case KEY_8: digit = 8; break; case KEY_9: digit = 9; break; case KEY_SIGMA: digit = 10; break; case KEY_INV: digit = 11; break; case KEY_SQRT: digit = 12; break; case KEY_LOG: digit = 13; break; case KEY_LN: digit = 14; break; case KEY_XEQ: digit = 15; break; } if (digit >= base) return; if (core_settings.auto_repeat) { repeating = 2; repeating_key = key; repeating_shift = shift; } c = digit < 10 ? '0' + digit : 'A' + digit - 10; cmdline[cmdline_length++] = c; if (base == 10) { if (string2phloat(cmdline, cmdline_length, &x) == 0) { if (!flags.f.prgm_mode) fix_thousands_separators(cmdline, &cmdline_length); } else { cmdline_length--; return; } } else { int bits = base == 2 ? 1 : base == 8 ? 3 : 4; bits *= cmdline_length; if (bits > 36) { cmdline_length--; return; } } } if (base == 10) { if (string2phloat(cmdline, cmdline_length, &x) != 0) /* Should never happen */ x = 0; } else { int8 n = 0; int i; for (i = 0; i < cmdline_length; i++) { char c = cmdline[i]; int digit = c <= '9' ? c - '0' : c - 'A' + 10; n = n * base + digit; } if (n & LL(0x800000000)) n |= LL(0xfffffff000000000); x = (phloat) n; } if (flags.f.prgm_mode) entered_number = x; else { free_vartype(reg_x); reg_x = new_real(x); } draw_number: bufptr = 0; if (flags.f.prgm_mode) { int line = pc2line(pc); if (line < 10) char2buf(buf, 100, &bufptr, '0'); bufptr += int2string(line, buf + bufptr, 100 - bufptr); char2buf(buf, 100, &bufptr, 6); } else if (matedit_mode == 2 || matedit_mode == 3) { bufptr += int2string(matedit_i + 1, buf + bufptr, 100 - bufptr); char2buf(buf, 100, &bufptr, ':'); bufptr += int2string(matedit_j + 1, buf + bufptr, 100 - bufptr); char2buf(buf, 100, &bufptr, '='); } else if (input_length > 0) { string2buf(buf, 100, &bufptr, input_name, input_length); char2buf(buf, 100, &bufptr, '?'); } else string2buf(buf, 100, &bufptr, "x\200", 2); string2buf(buf, 100, &bufptr, cmdline, cmdline_length); char2buf(buf, 100, &bufptr, '_'); clear_row(cmdline_row); if (bufptr <= 22) draw_string(0, cmdline_row, buf, bufptr); else { draw_char(0, cmdline_row, 26); draw_string(1, cmdline_row, buf + bufptr - 21, 21); } flush_display(); return; } void keydown_command_entry(int shift, int key) { if (mode_commandmenu == MENU_ST || mode_commandmenu == MENU_IND_ST) { int menukey; if (!shift && key == KEY_BSP) { pending_command = CMD_NULL; finish_command_entry(false); return; } if (key == KEY_EXIT) { pending_command = CMD_CANCELLED; finish_command_entry(false); return; } menukey = find_menu_key(key); if (!shift && ((menukey >= 0 && menukey <= 4) || (menukey == 5 && mode_commandmenu == MENU_IND_ST))) { if (mode_commandmenu == MENU_IND_ST && menukey == 0) { incomplete_ind = 1; incomplete_maxdigits = 2; set_catalog_menu(CATSECT_REAL_ONLY); redisplay(); return; } if (mode_commandmenu == MENU_IND_ST) menukey--; pending_command = incomplete_command; pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_STK : ARGTYPE_STK; pending_command_arg.val.stk = "LXYZT"[menukey]; finish_command_entry(true); return; } squeak(); return; } if (incomplete_command == CMD_LBL && incomplete_length == 0 && mode_commandmenu != MENU_CATALOG) { /* LBL is weird. It's sort of like you have alpha and numeric * at the same time. When we're at length 0, we have to handle * both possibilities and pick the right one. */ /* On the Palm, we accept 0-9 as alphanumeric in this case; * if the user wants numeric, it's easy enough to just tap * the appropriate keys on the virtual keyboard. * On PCs, on the other hand, we want the number keys to * behave like they always do, so you can enter LBL 00 without * having to use the virtual keyboard. If an alpha LBL with * a name starting with '0'-'9' is desired, the user will have * to use the usual trick: activate a submenu of ALPHA by * pressing any menu key (F1-F6), and then typing the number. */ if ((mode_commandmenu == MENU_ALPHA1 || mode_commandmenu == MENU_ALPHA2) && key >= 1024 + '0' && key <= 1024 + '9') switch (key - 1024) { case '0': key = KEY_0; break; case '1': key = KEY_1; break; case '2': key = KEY_2; break; case '3': key = KEY_3; break; case '4': key = KEY_4; break; case '5': key = KEY_5; break; case '6': key = KEY_6; break; case '7': key = KEY_7; break; case '8': key = KEY_8; break; case '9': key = KEY_9; break; } if (key >= 1024 || (key == KEY_SIGMA || key == KEY_INV || key == KEY_SQRT || key == KEY_LOG || key == KEY_LN || key == KEY_XEQ) || (!shift && (key == KEY_E || key == KEY_UP || key == KEY_DOWN || key == KEY_DIV || key == KEY_MUL || key == KEY_SUB || key == KEY_ADD || key == KEY_DOT)) || (shift && (key == KEY_RCL || key == KEY_RDN)) || (mode_commandmenu >= MENU_ALPHA_ABCDE1 && mode_commandmenu <= MENU_ALPHA_MISC2 && ( key == KEY_0 || key == KEY_1 || key == KEY_2 || key == KEY_3 || key == KEY_4 || key == KEY_5 || key == KEY_6 || key == KEY_7 || key == KEY_8 || key == KEY_9))) goto do_incomplete_alpha; if (shift && key == KEY_ADD) { if (mode_commandmenu == MENU_CATALOG) squeak(); else { incomplete_alpha = 1; set_catalog_menu(CATSECT_TOP); redisplay(); } return; } if (key == KEY_EXIT && mode_commandmenu >= MENU_ALPHA_ABCDE1 && mode_commandmenu <= MENU_ALPHA_MISC2) { if (mode_commandmenu <= MENU_ALPHA_WXYZ) mode_commandmenu = MENU_ALPHA1; else mode_commandmenu = MENU_ALPHA2; redisplay(); return; } } if ((incomplete_command == CMD_ASTO || incomplete_command == CMD_ARCL) && incomplete_length == 1 && mode_commandmenu == MENU_NONE && mode_alphamenu >= MENU_ALPHA1 && mode_alphamenu <= MENU_ALPHA_MISC2) { /* A similar oddity are ASTO and ARCL, when entered using the * STO and RCL keys while ALPHA mode is active. Initially, * you'll get a menu of all variables, but when you type a * digit, the variables menu disappears, and the ALPHA menu * returns... but we still want 0-9 to be treated as numeric. */ switch (key - 1024) { case '0': key = KEY_0; break; case '1': key = KEY_1; break; case '2': key = KEY_2; break; case '3': key = KEY_3; break; case '4': key = KEY_4; break; case '5': key = KEY_5; break; case '6': key = KEY_6; break; case '7': key = KEY_7; break; case '8': key = KEY_8; break; case '9': key = KEY_9; break; } } /* Another oddity: ASSIGN... */ if (incomplete_argtype == ARG_CKEY) { int menukey = find_menu_key(key); if (menukey != -1) { pending_command = CMD_ASGN01 + menukey + 6 * (mode_commandmenu - MENU_CUSTOM1); finish_command_entry(true); } else if (!shift && key == KEY_BSP) { pending_command = CMD_NULL; finish_command_entry(false); } else if (key == KEY_EXIT) { pending_command = CMD_CANCELLED; finish_command_entry(false); } else if (!shift && (key == KEY_UP || key == KEY_DOWN)) { mode_commandmenu += key == KEY_UP ? -1 : 1; if (mode_commandmenu < MENU_CUSTOM1) set_menu(MENULEVEL_COMMAND, MENU_CUSTOM3); else if (mode_commandmenu > MENU_CUSTOM3) set_menu(MENULEVEL_COMMAND, MENU_CUSTOM1); redisplay(); } else squeak(); return; } /* And yet another oddity: KEYG and KEYX */ if (incomplete_argtype == ARG_MKEY) { int cmd = incomplete_command == CMD_KEYG ? CMD_KEY1G : CMD_KEY1X; if (shift) { squeak(); return; } switch (key) { case KEY_SIGMA: case KEY_1: break; case KEY_INV: case KEY_2: cmd++; break; case KEY_SQRT: case KEY_3: cmd += 2; break; case KEY_LOG: case KEY_4: cmd += 3; break; case KEY_LN: case KEY_5: cmd += 4; break; case KEY_XEQ: case KEY_6: cmd += 5; break; case KEY_UP: case KEY_7: cmd += 6; break; case KEY_DOWN: case KEY_8: cmd += 7; break; case KEY_EXIT: case KEY_9: cmd += 8; break; case KEY_BSP: pending_command = CMD_NULL; finish_command_entry(false); return; default: squeak(); return; } start_incomplete_command(cmd); return; } if (mode_commandmenu == MENU_CATALOG) { int menukey = find_menu_key(key); int catsect = get_cat_section(); if (menukey != -1) { if (catsect == CATSECT_TOP) { switch (menukey) { case 0: set_cat_section(CATSECT_FCN); move_cat_row(0); break; case 1: set_cat_section(CATSECT_PGM); move_cat_row(0); break; case 2: if (!vars_exist(1, 0, 0)) { squeak(); return; } else { set_cat_section(CATSECT_REAL); move_cat_row(0); break; } case 3: if (!vars_exist(0, 1, 0)) { squeak(); return; } else { set_cat_section(CATSECT_CPX); move_cat_row(0); break; } case 4: if (!vars_exist(0, 0, 1)) { squeak(); return; } else { set_cat_section(CATSECT_MAT); move_cat_row(0); break; } case 5: display_mem(); pending_command = CMD_LINGER1; shell_request_timeout3(2000); return; } redisplay(); return; } else { int i, itemindex; itemindex = get_cat_item(menukey); if (itemindex == -1) { squeak(); return; } if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY) { if (labels[itemindex].length == 0) { /* END or .END. */ if (incomplete_command != CMD_GTODOT && incomplete_command != CMD_PRP && (flags.f.prgm_mode || (incomplete_command != CMD_GTO && incomplete_command != CMD_XEQ && incomplete_command != CMD_CLP))) { squeak(); return; } } pending_command = incomplete_command; if (incomplete_command == CMD_GTO || incomplete_command == CMD_GTODOT || incomplete_command == CMD_XEQ || incomplete_command == CMD_CLP || incomplete_command == CMD_PRP) { pending_command_arg.type = ARGTYPE_LBLINDEX; pending_command_arg.val.num = itemindex; xeq_invisible = 0; } else { pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_STR : ARGTYPE_STR; pending_command_arg.length = labels[itemindex].length; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = labels[itemindex].name[i]; } finish_command_entry(true); return; } pending_command = incomplete_command; pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_STR : ARGTYPE_STR; if (catsect == CATSECT_FCN) { const command_spec *cs = cmdlist(itemindex); pending_command_arg.length = cs->name_length; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = cs->name[i]; } else { pending_command_arg.length = vars[itemindex].length; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = vars[itemindex].name[i]; } if (!incomplete_ind && incomplete_command == CMD_XEQ) finish_xeq(); else finish_command_entry(true); return; } } if (!shift && (key == KEY_UP || key == KEY_DOWN)) { move_cat_row(key == KEY_UP ? -1 : 1); redisplay(); return; } if (key == KEY_EXIT) { if (catsect == CATSECT_FCN || catsect == CATSECT_PGM || catsect == CATSECT_REAL || catsect == CATSECT_CPX || catsect == CATSECT_MAT) { set_cat_section(CATSECT_TOP); redisplay(); } else { pending_command = CMD_CANCELLED; finish_command_entry(false); } return; } } if (mode_commandmenu == MENU_VARMENU) { int menukey = find_menu_key(key); if (menukey != -1) { int i; if (varmenu_labellength[menukey] == 0) { squeak(); return; } pending_command = incomplete_command; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = varmenu_labellength[menukey]; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = varmenu_labeltext[menukey][i]; finish_command_entry(false); return; } if (!shift && (key == KEY_UP || key == KEY_DOWN)) { if (varmenu_rows > 1) { if (key == KEY_UP) { varmenu_row--; if (varmenu_row < 0) varmenu_row = varmenu_rows - 1; } else { varmenu_row++; if (varmenu_row >= varmenu_rows) varmenu_row = 0; } redisplay(); } return; } if (key == KEY_EXIT) { pending_command = CMD_CANCELLED; finish_command_entry(false); return; } } else if (mode_commandmenu == MENU_INTEG_PARAMS) { int menukey = find_menu_key(key); if (menukey != -1) { const char *name; int length, i; switch (menukey) { case 0: name = "LLIM"; length = 4; break; case 1: name = "ULIM"; length = 4; break; case 2: name = "ACC"; length = 3; break; default: squeak(); return; } pending_command = incomplete_command; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = length; for (i = 0; i < length; i++) pending_command_arg.val.text[i] = name[i]; finish_command_entry(false); return; } if (key == KEY_EXIT) { pending_command = CMD_CANCELLED; finish_command_entry(false); return; } } if (!incomplete_alpha) { if (key == KEY_EXIT) { pending_command = CMD_CANCELLED; finish_command_entry(false); return; } if (mode_commandmenu == MENU_IND) { if (!shift && key == KEY_SIGMA) { incomplete_command = CMD_GTO; incomplete_argtype = ARG_LBL; incomplete_ind = 1; incomplete_maxdigits = 2; set_catalog_menu(CATSECT_REAL_ONLY); redisplay(); return; } else if (key == KEY_ENTER) { incomplete_argtype = ARG_LBL; incomplete_alpha = 1; set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); redisplay(); return; } } if (incomplete_command == CMD_STO && !shift && incomplete_length == 0 && (key == KEY_DIV || key == KEY_MUL || key == KEY_SUB || key == KEY_ADD)) { switch (key) { case KEY_DIV: incomplete_command = CMD_STO_DIV; break; case KEY_MUL: incomplete_command = CMD_STO_MUL; break; case KEY_SUB: incomplete_command = CMD_STO_SUB; break; case KEY_ADD: incomplete_command = CMD_STO_ADD; break; } redisplay(); return; } if (incomplete_command == CMD_RCL && !shift && incomplete_length == 0 && (key == KEY_DIV || key == KEY_MUL || key == KEY_SUB || key == KEY_ADD)) { switch (key) { case KEY_DIV: incomplete_command = CMD_RCL_DIV; break; case KEY_MUL: incomplete_command = CMD_RCL_MUL; break; case KEY_SUB: incomplete_command = CMD_RCL_SUB; break; case KEY_ADD: incomplete_command = CMD_RCL_ADD; break; } redisplay(); return; } if (incomplete_command == CMD_GTO && !incomplete_ind && !shift && incomplete_length == 0 && key == KEY_DOT) { incomplete_command = CMD_GTODOT; incomplete_argtype = ARG_OTHER; incomplete_maxdigits = 4; set_menu(MENULEVEL_COMMAND, MENU_IND); redisplay(); return; } if (incomplete_command == CMD_GTODOT && !shift && incomplete_length == 0 && key == KEY_DOT) { pending_command = CMD_GTODOTDOT; pending_command_arg.type = ARGTYPE_NONE; finish_command_entry(false); return; } if (key == KEY_ENTER) { if (incomplete_length == 0) { if (incomplete_ind && !flags.f.prgm_mode && !vars_exist(1, 0, 0)) squeak(); else if (incomplete_ind || incomplete_argtype == ARG_VAR || incomplete_argtype == ARG_REAL || incomplete_argtype == ARG_NAMED || incomplete_argtype == ARG_LBL || incomplete_argtype == ARG_PRGM) { incomplete_alpha = 1; set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); redisplay(); } else squeak(); return; } else if (!shift) { if (incomplete_command == CMD_GOTOROW) { pending_command_arg.val.num = incomplete_num; start_incomplete_command(CMD_GOTOCOLUMN); return; } else if (incomplete_command == CMD_GOTOCOLUMN) { matedit_goto(pending_command_arg.val.num, incomplete_num); pending_command = CMD_NONE; finish_command_entry(true); return; } pending_command = incomplete_command; pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_NUM : ARGTYPE_NUM; pending_command_arg.length = incomplete_maxdigits; pending_command_arg.val.num = incomplete_num; finish_command_entry(false); return; } } if (incomplete_length == 0 && !shift && key == KEY_DOT) { if (incomplete_ind) { if (incomplete_argtype == ARG_VAR || incomplete_argtype == ARG_REAL || incomplete_argtype == ARG_MAT || incomplete_argtype == ARG_RVAR || incomplete_argtype == ARG_NAMED || incomplete_argtype == ARG_LBL || incomplete_argtype == ARG_PRGM || incomplete_argtype == ARG_NUM9 || incomplete_argtype == ARG_NUM11 || incomplete_argtype == ARG_NUM99) { set_menu(MENULEVEL_COMMAND, MENU_ST); redisplay(); } } else { if (incomplete_argtype == ARG_VAR || incomplete_argtype == ARG_REAL) { set_menu(MENULEVEL_COMMAND, MENU_IND_ST); redisplay(); } else if (incomplete_argtype == ARG_NUM9 || incomplete_argtype == ARG_NUM11 || incomplete_argtype == ARG_NUM99 || incomplete_argtype == ARG_LBL) { incomplete_ind = 1; incomplete_maxdigits = 2; set_catalog_menu(CATSECT_REAL_ONLY); redisplay(); } else { squeak(); } } return; } if (incomplete_length < incomplete_maxdigits && !shift && (key == KEY_0 || key == KEY_1 || key == KEY_2 || key == KEY_3 || key == KEY_4 || key == KEY_5 || key == KEY_6 || key == KEY_7 || key == KEY_8 || key == KEY_9)) { int digit; switch (key) { case KEY_0: digit = 0; break; case KEY_1: digit = 1; break; case KEY_2: digit = 2; break; case KEY_3: digit = 3; break; case KEY_4: digit = 4; break; case KEY_5: digit = 5; break; case KEY_6: digit = 6; break; case KEY_7: digit = 7; break; case KEY_8: digit = 8; break; case KEY_9: digit = 9; break; } incomplete_num = incomplete_num * 10 + digit; incomplete_length++; if (incomplete_length == incomplete_maxdigits) { if (incomplete_command == CMD_GOTOROW) { pending_command_arg.val.num = incomplete_num; start_incomplete_command(CMD_GOTOCOLUMN); return; } else if (incomplete_command == CMD_GOTOCOLUMN) { matedit_goto(pending_command_arg.val.num, incomplete_num); pending_command = CMD_NONE; finish_command_entry(true); return; } pending_command = incomplete_command; pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_NUM : ARGTYPE_NUM; pending_command_arg.length = incomplete_maxdigits; if (!incomplete_ind && incomplete_argtype == ARG_NUM11 && incomplete_num > 11) incomplete_num = 11; pending_command_arg.val.num = incomplete_num; finish_command_entry(true); return; } else set_menu(MENULEVEL_COMMAND, MENU_NONE); redisplay(); return; } if (incomplete_length < incomplete_maxdigits && !shift && key == KEY_BSP) { if (incomplete_length == 0) { pending_command = CMD_NULL; finish_command_entry(false); return; } else { incomplete_length--; incomplete_num /= 10; if (incomplete_length == 0) { if (incomplete_command >= CMD_KEY1G && incomplete_command <= CMD_KEY9X) start_incomplete_command( incomplete_command <= CMD_KEY9G ? CMD_KEYG : CMD_KEYX); else if (incomplete_argtype == ARG_VAR) if (mode_appmenu == MENU_VARMENU) set_menu(MENULEVEL_COMMAND, MENU_VARMENU); else if (mode_appmenu == MENU_INTEG_PARAMS) set_menu(MENULEVEL_COMMAND, MENU_INTEG_PARAMS); else set_catalog_menu(CATSECT_VARS_ONLY); else if (incomplete_ind) set_catalog_menu(CATSECT_REAL_ONLY); else if (incomplete_argtype == ARG_REAL) if (mode_appmenu == MENU_VARMENU) set_menu(MENULEVEL_COMMAND, MENU_VARMENU); else if (mode_appmenu == MENU_INTEG_PARAMS) set_menu(MENULEVEL_COMMAND, MENU_INTEG_PARAMS); else set_catalog_menu(CATSECT_REAL_ONLY); else if (incomplete_argtype == ARG_LBL) set_catalog_menu(CATSECT_PGM_ONLY); else if (incomplete_command == CMD_GTODOT) set_menu(MENULEVEL_COMMAND, MENU_IND); else if (incomplete_command == CMD_LBL) set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); } redisplay(); return; } } /* Some bad key... */ squeak(); return; } else { /* incomplete_alpha */ int menukey; const menu_spec *m; char c; do_incomplete_alpha: m = NULL; if (key >= 1024) { c = key - 1024; goto handle_char; } else if (mode_commandmenu != MENU_NONE && (menukey = find_menu_key(key)) != -1) { const menu_item_spec *mi; m = menus + mode_commandmenu; mi = m->child + menukey; if (mi->menuid != MENU_NONE) { set_menu(MENULEVEL_COMMAND, mi->menuid); redisplay(); return; } c = mi->title[0]; if (shift && c >= 'A' && c <= 'Z') c += 'a' - 'A'; handle_char: if (incomplete_length < 7) incomplete_str[incomplete_length++] = c; if (m != NULL) set_menu(MENULEVEL_COMMAND, m->parent); /* incomplete_alpha can be 0 at this point if * the command is CMD_LBL. */ incomplete_alpha = 1; redisplay(); return; } if ((incomplete_argtype == ARG_NAMED || incomplete_argtype == ARG_PRGM || incomplete_argtype == ARG_RVAR || incomplete_argtype == ARG_MAT) && incomplete_command != CMD_ASSIGNa && incomplete_command != CMD_CLP && incomplete_command != CMD_PRP && incomplete_command != CMD_MVAR && !incomplete_ind && incomplete_length == 0 && (mode_commandmenu < MENU_ALPHA1 || mode_commandmenu > MENU_ALPHA_MISC2) && !shift && key == KEY_DOT) { incomplete_ind = 1; incomplete_alpha = 0; set_catalog_menu(CATSECT_REAL_ONLY); redisplay(); return; } if (incomplete_length == 0 && shift && key == KEY_ADD) { if (mode_commandmenu == MENU_CATALOG) squeak(); else { set_catalog_menu(CATSECT_TOP); redisplay(); } return; } /* Handle keys that represent characters */ if (mode_commandmenu == MENU_CATALOG) goto nocharkey1; if (!shift) { switch (key) { case KEY_0: c = '0'; break; case KEY_1: c = '1'; break; case KEY_2: c = '2'; break; case KEY_3: c = '3'; break; case KEY_4: c = '4'; break; case KEY_5: c = '5'; break; case KEY_6: c = '6'; break; case KEY_7: c = '7'; break; case KEY_8: c = '8'; break; case KEY_9: c = '9'; break; case KEY_DOT: c = '.'; break; case KEY_E: c = 24; break; case KEY_DIV: c = 0; break; case KEY_MUL: c = 1; break; case KEY_SUB: c = '-'; break; case KEY_ADD: c = '+'; break; default: goto nocharkey1; } } else { switch (key) { case KEY_RCL: c = '%'; break; case KEY_RDN: c = 7; break; default: goto nocharkey1; } } if (incomplete_length < 7) incomplete_str[incomplete_length++] = c; /* incomplete_alpha can be 0 at this point if * the command is CMD_LBL. */ incomplete_alpha = 1; redisplay(); return; nocharkey1: /* End of handling keys that represent characters */ if (!shift && (key == KEY_UP || key == KEY_DOWN)) { const menu_spec *m = menus + mode_commandmenu; int nextmenu = key == KEY_UP ? m->prev : m->next; if (nextmenu != MENU_NONE) { set_menu(MENULEVEL_COMMAND, nextmenu); redisplay(); } return; } if (key == KEY_EXIT) { const menu_spec *m; if (mode_commandmenu == MENU_NONE) { pending_command = CMD_CANCELLED; finish_command_entry(false); return; } if (mode_commandmenu == MENU_CATALOG) { int catsect = get_cat_section(); if (catsect == CATSECT_PGM || catsect == CATSECT_REAL || catsect == CATSECT_CPX || catsect == CATSECT_MAT) { set_cat_section(CATSECT_TOP); redisplay(); return; } } m = menus + mode_commandmenu; set_menu(MENULEVEL_COMMAND, m->parent); if (mode_commandmenu == MENU_NONE) { pending_command = CMD_CANCELLED; finish_command_entry(false); } else redisplay(); return; } if (!shift && key == KEY_BSP) { if (incomplete_command >= CMD_KEY1G && incomplete_command <= CMD_KEY9X) { if (incomplete_length == 0) { if (mode_commandmenu >= MENU_ALPHA1 && mode_commandmenu <= MENU_ALPHA_MISC2) start_incomplete_command( incomplete_command <= CMD_KEY9G ? CMD_KEYG : CMD_KEYX); else { pending_command = CMD_NULL; finish_command_entry(false); } } else { incomplete_length--; redisplay(); } return; } if (incomplete_length == 0) { int catsect; if (mode_commandmenu >= MENU_ALPHA1 && mode_commandmenu <= MENU_ALPHA_MISC2) { if (incomplete_command == CMD_GTODOT) { incomplete_argtype = ARG_OTHER; incomplete_maxdigits = 4; incomplete_alpha = 0; set_menu(MENULEVEL_COMMAND, MENU_IND); redisplay(); return; } else if (incomplete_argtype == ARG_NAMED) { if (incomplete_command == CMD_ASSIGNa) set_catalog_menu(CATSECT_TOP); else set_catalog_menu(CATSECT_VARS_ONLY); if (incomplete_ind) goto out_of_alpha; } else if (incomplete_argtype == ARG_RVAR) { if (incomplete_command == CMD_MVAR) { pending_command = CMD_NULL; finish_command_entry(false); } else goto out_of_alpha; } else if (incomplete_argtype == ARG_MAT) { if (vars_exist(0, 0, 1)) set_catalog_menu(CATSECT_MAT_ONLY); else set_menu(MENULEVEL_COMMAND, MENU_NONE); } else if (incomplete_argtype == ARG_PRGM) set_catalog_menu(CATSECT_PGM_ONLY); else goto out_of_alpha; redisplay(); } else if (mode_commandmenu == MENU_CATALOG && ((catsect = get_cat_section()) == CATSECT_FCN || catsect == CATSECT_PGM || catsect == CATSECT_REAL || catsect == CATSECT_CPX || catsect == CATSECT_MAT)) { set_catalog_menu(CATSECT_TOP); redisplay(); } else { pending_command = CMD_NULL; finish_command_entry(false); } return; } incomplete_length--; if (incomplete_length == 0) { if (incomplete_command == CMD_GTODOT) { incomplete_argtype = ARG_OTHER; incomplete_maxdigits = 4; incomplete_alpha = 0; set_menu(MENULEVEL_COMMAND, MENU_IND); redisplay(); return; } else if (incomplete_argtype == ARG_NAMED) { if (incomplete_command == CMD_ASSIGNa) set_catalog_menu(CATSECT_TOP); else set_catalog_menu(CATSECT_VARS_ONLY); if (incomplete_ind) goto out_of_alpha; } else if (incomplete_argtype == ARG_RVAR) { if (vars_exist(1, 0, 0)) set_catalog_menu(CATSECT_REAL_ONLY); } else if (incomplete_argtype == ARG_MAT) { if (vars_exist(0, 0, 1)) set_catalog_menu(CATSECT_MAT_ONLY); } else if (incomplete_argtype == ARG_PRGM) set_catalog_menu(CATSECT_PGM_ONLY); else { out_of_alpha: if (incomplete_ind || incomplete_argtype != ARG_RVAR) incomplete_alpha = 0; if (incomplete_ind) set_catalog_menu(CATSECT_REAL_ONLY); else if (incomplete_argtype == ARG_VAR) if (mode_appmenu == MENU_VARMENU) set_menu(MENULEVEL_COMMAND, MENU_VARMENU); else if (mode_appmenu == MENU_INTEG_PARAMS) set_menu(MENULEVEL_COMMAND, MENU_INTEG_PARAMS); else set_catalog_menu(CATSECT_VARS_ONLY); else if (incomplete_argtype == ARG_REAL) if (mode_appmenu == MENU_VARMENU) set_menu(MENULEVEL_COMMAND, MENU_VARMENU); else if (mode_appmenu == MENU_INTEG_PARAMS) set_menu(MENULEVEL_COMMAND, MENU_INTEG_PARAMS); else set_catalog_menu(CATSECT_REAL_ONLY); else if (incomplete_argtype == ARG_LBL) set_catalog_menu(CATSECT_PGM_ONLY); else if (incomplete_command == CMD_GTODOT) set_menu(MENULEVEL_COMMAND, MENU_IND); else if (incomplete_command == CMD_LBL) set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); else set_menu(MENULEVEL_COMMAND, MENU_NONE); } } redisplay(); return; } if (key == KEY_ENTER) { int i; if (incomplete_length == 0) { int catsect; if (mode_commandmenu == MENU_NONE || (mode_commandmenu == MENU_CATALOG && (catsect = get_cat_section()) != CATSECT_FCN && catsect != CATSECT_PGM && catsect != CATSECT_REAL && catsect != CATSECT_CPX && catsect != CATSECT_MAT)) { set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); redisplay(); return; } else if ((incomplete_command != CMD_ASSIGNa && incomplete_command != CMD_CLP && incomplete_command != CMD_PRP) || mode_commandmenu < MENU_ALPHA1 || mode_commandmenu > MENU_ALPHA_MISC2) { squeak(); return; } /* ASSIGN, CLP, or PRP, alpha menu active, zero-length string: * these are the only cases where an empty string is allowed * as an argument. We fall through to the command completion * code below. */ } pending_command = incomplete_command; if (!incomplete_ind && (incomplete_command == CMD_GTO || incomplete_command == CMD_XEQ || incomplete_command == CMD_LBL || (incomplete_command >= CMD_KEY1G && incomplete_command <= CMD_KEY9X)) && incomplete_length == 1 && ((incomplete_str[0] >= 'A' && incomplete_str[0] <= 'J') || (incomplete_str[0] >= 'a' && incomplete_str[0] <= 'e'))) { /* Display XEQ "A" briefly before changing to XEQ A */ mode_command_entry = false; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = 1; pending_command_arg.val.text[0] = incomplete_str[0]; if (flags.f.prgm_mode) { flags.f.prgm_mode = 0; redisplay(); flags.f.prgm_mode = 1; shell_delay(125); } else redisplay(); pending_command_arg.type = ARGTYPE_LCLBL; pending_command_arg.val.lclbl = incomplete_str[0]; set_menu(MENULEVEL_COMMAND, MENU_NONE); finish_command_entry(false); return; } else { pending_command_arg.type = incomplete_ind ? ARGTYPE_IND_STR : ARGTYPE_STR; pending_command_arg.length = incomplete_length; for (i = 0; i < incomplete_length; i++) pending_command_arg.val.text[i] = incomplete_str[i]; if (!incomplete_ind && incomplete_command == CMD_XEQ) finish_xeq(); else finish_command_entry(true); return; } } squeak(); return; } } void keydown_alpha_mode(int shift, int key) { int menukey; const menu_spec *m = NULL; char c; int command; if (key >= 1024) { c = key - 1024; goto handle_char; } menukey = find_menu_key(key); if (menukey != -1) { const menu_item_spec *mi; m = menus + mode_alphamenu; mi = m->child + menukey; if (mi->menuid != MENU_NONE) { set_menu(MENULEVEL_ALPHA, mi->menuid); redisplay(); return; } c = mi->title[0]; if (shift && c >= 'A' && c <= 'Z') c += 'a' - 'A'; handle_char: if (flags.f.prgm_mode) { if (!mode_alpha_entry) start_alpha_prgm_line(); if (entered_string_length < 15) entered_string[entered_string_length++] = c; } else { if (!mode_alpha_entry) { reg_alpha_length = 0; flags.f.alpha_data_input = 1; mode_alpha_entry = true; } append_alpha_char(c); if (reg_alpha_length == 44) squeak(); } if (core_settings.auto_repeat) { repeating = 2; repeating_key = c + 1024; repeating_shift = 0; } if (m != NULL) set_menu(MENULEVEL_ALPHA, m->parent); redisplay(); return; } /* Handle keys that represent characters */ if (!shift) { switch (key) { case KEY_0: c = '0'; break; case KEY_1: c = '1'; break; case KEY_2: c = '2'; break; case KEY_3: c = '3'; break; case KEY_4: c = '4'; break; case KEY_5: c = '5'; break; case KEY_6: c = '6'; break; case KEY_7: c = '7'; break; case KEY_8: c = '8'; break; case KEY_9: c = '9'; break; case KEY_DOT: c = '.'; break; case KEY_E: c = 24; break; case KEY_DIV: c = 0; break; case KEY_MUL: c = 1; break; case KEY_SUB: c = '-'; break; case KEY_ADD: c = '+'; break; default: goto nocharkey2; } } else { switch (key) { case KEY_RCL: c = '%'; break; case KEY_RDN: c = 7; break; default: goto nocharkey2; } } if (flags.f.prgm_mode) { if (!mode_alpha_entry) start_alpha_prgm_line(); if (entered_string_length < 15) entered_string[entered_string_length++] = c; } else { if (!mode_alpha_entry) { reg_alpha_length = 0; mode_alpha_entry = true; flags.f.alpha_data_input = 1; } append_alpha_char(c); if (reg_alpha_length == 44) squeak(); } if (core_settings.auto_repeat) { repeating = 2; repeating_key = key; repeating_shift = shift; } redisplay(); return; nocharkey2: /* End of handling keys that represent characters */ if (!shift && (key == KEY_UP || key == KEY_DOWN)) { const menu_spec *m = menus + mode_alphamenu; int nextmenu = key == KEY_UP ? m->prev : m->next; if (nextmenu != MENU_NONE) { set_menu(MENULEVEL_ALPHA, nextmenu); redisplay(); } return; } if (key == KEY_EXIT) { const menu_spec *m = menus + mode_alphamenu; set_menu(MENULEVEL_ALPHA, m->parent); if (mode_alphamenu == MENU_NONE) { if (mode_alpha_entry) { if (flags.f.prgm_mode) finish_alpha_prgm_line(); else if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) docmd_pra(NULL); mode_alpha_entry = false; } flags.f.alpha_mode = 0; pending_command = CMD_CANCELLED; } else redisplay(); return; } if (!shift && key == KEY_BSP) { if (flags.f.prgm_mode) { if (mode_alpha_entry) { if (entered_string_length > 0) { if (core_settings.auto_repeat) { repeating = 2; repeating_key = key; repeating_shift = shift; } entered_string_length--; } else finish_alpha_prgm_line(); } else { int4 line = pc2line(pc); if (line != 0 && (current_prgm != prgms_count - 1 || prgms[current_prgm].text[pc] != CMD_END)) { delete_command(pc); pc = line2pc(line - 1); } prgm_highlight_row = 0; if (mode_alphamenu != MENU_ALPHA1 && mode_alphamenu != MENU_ALPHA2) set_menu(MENULEVEL_ALPHA, menus[mode_alphamenu].parent); } redisplay(); return; } else { if (mode_alpha_entry && reg_alpha_length > 0) { if (core_settings.auto_repeat) { repeating = 2; repeating_key = key; repeating_shift = shift; } reg_alpha_length--; redisplay(); } else pending_command = CMD_CLA; return; } } if (key == KEY_ENTER) { if (flags.f.prgm_mode) { if (mode_alpha_entry) { finish_alpha_prgm_line(); flags.f.alpha_mode = 0; set_menu(MENULEVEL_ALPHA, MENU_NONE); } else if (shift) { flags.f.alpha_mode = 0; set_menu(MENULEVEL_ALPHA, MENU_NONE); } else { start_alpha_prgm_line(); entered_string[0] = 127; entered_string_length = 1; } } else { if (shift || mode_alpha_entry) { if (mode_alpha_entry && (flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) docmd_pra(NULL); flags.f.alpha_mode = 0; mode_alpha_entry = false; set_menu(MENULEVEL_ALPHA, MENU_NONE); } else mode_alpha_entry = true; } redisplay(); return; } command = CMD_CANCELLED; if (!shift) { switch (key) { case KEY_STO: command = CMD_ASTO; break; case KEY_RCL: command = CMD_ARCL; break; case KEY_RUN: command = CMD_RUN; break; default: command = CMD_NONE; break; } } else { switch (key) { case KEY_CHS: set_plainmenu(MENU_MODES1); break; case KEY_E: set_plainmenu(MENU_DISP); break; case KEY_BSP: set_plainmenu(MENU_CLEAR1); break; case KEY_7: set_solve_integ(1); break; case KEY_8: set_solve_integ(0); break; case KEY_9: set_menu(MENULEVEL_APP, MENU_MATRIX1); break; case KEY_DIV: set_menu(MENULEVEL_APP, MENU_STAT1); break; case KEY_DOWN: command = CMD_SST; break; case KEY_4: set_menu(MENULEVEL_APP, MENU_BASE); if (mode_appmenu == MENU_BASE) { set_appmenu_exitcallback(2); baseapp = 1; } break; case KEY_5: set_plainmenu(MENU_CONVERT1); break; case KEY_6: set_plainmenu(MENU_FLAGS); break; case KEY_MUL: set_plainmenu(MENU_PROB); break; case KEY_2: set_plainmenu(MENU_CUSTOM1); break; case KEY_3: set_plainmenu(MENU_PGM_FCN1); break; case KEY_SUB: set_plainmenu(MENU_PRINT1); break; case KEY_0: set_plainmenu(MENU_TOP_FCN); break; case KEY_DOT: show(); pending_command = CMD_LINGER1; shell_request_timeout3(2000); return; case KEY_ADD: set_plainmenu(MENU_CATALOG); break; default: command = CMD_NONE; break; } } if (command == CMD_NONE) { squeak(); return; } if (mode_alpha_entry) { if (flags.f.prgm_mode) finish_alpha_prgm_line(); else if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) docmd_pra(NULL); mode_alpha_entry = false; } if (command == CMD_CANCELLED) { /* plainmenu or appmenu switch */ flags.f.alpha_mode = 0; set_menu(MENULEVEL_ALPHA, MENU_NONE); redisplay(); return; } else do_interactive(command); } void keydown_normal_mode(int shift, int key) { int command; if (is_number_key(shift, key)) { /* Entering number entry mode */ if (deferred_print) print_command(CMD_NULL, NULL); cmdline_length = 0; if (get_front_menu() != NULL) cmdline_row = 0; else cmdline_row = 1; mode_number_entry = true; if (flags.f.prgm_mode) { if (pc == -1) pc = 0; else if (prgms[current_prgm].text[pc] != CMD_END) pc += get_command_length(current_prgm, pc); prgm_highlight_row = 1; if (cmdline_row == 1) display_prgm_line(0, -1); } else { if (!flags.f.stack_lift_disable) { free_vartype(reg_t); reg_t = reg_z; reg_z = reg_y; reg_y = dup_vartype(reg_x); } else flags.f.stack_lift_disable = 0; flags.f.numeric_data_input = 1; mode_varmenu = false; if (cmdline_row == 1) display_y(0); else /* Force repaint of menu; it could be hidden due to a recent * two-line AVIEW command */ redisplay(); } keydown_number_entry(shift, key); return; } if (flags.f.prgm_mode && !shift && key == KEY_BSP) { int4 line = pc2line(pc); if (line == 0) return; if (current_prgm != prgms_count - 1 || prgms[current_prgm].text[pc] != CMD_END) delete_command(pc); pc = line2pc(line - 1); prgm_highlight_row = 0; redisplay(); return; } if ((mode_appmenu != MENU_NONE || mode_plainmenu != MENU_NONE || mode_transientmenu != MENU_NONE) && mode_alphamenu == MENU_NONE && mode_commandmenu == MENU_NONE) { int menukey = find_menu_key(key); int menu, level; if (mode_transientmenu != MENU_NONE) { menu = mode_transientmenu; level = MENULEVEL_TRANSIENT; } else if (mode_plainmenu != MENU_NONE) { menu = mode_plainmenu; level = MENULEVEL_PLAIN; } else { menu = mode_appmenu; level = MENULEVEL_APP; } if (menu == MENU_PROGRAMMABLE) { int keynum; if (menukey != -1) keynum = menukey + 1; else if (!shift) { switch (key) { case KEY_UP: keynum = 7; break; case KEY_DOWN: keynum = 8; break; case KEY_EXIT: keynum = 9; break; default: goto notprogmenu; } } else goto notprogmenu; do_prgm_menu_key(keynum); return; notprogmenu:; } if (menu == MENU_VARMENU) { if (menukey != -1) { if (varmenu_labellength[menukey] == 0) { pending_command = CMD_NULL; } else if (shift && !flags.f.prgm_mode) { view(varmenu_labeltext[menukey], varmenu_labellength[menukey]); } else { int i; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = varmenu_labellength[menukey]; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = varmenu_labeltext[menukey][i]; if (flags.f.prgm_mode) { pending_command = shift ? CMD_VIEW : CMD_STO; store_command_after(&pc, pending_command, &pending_command_arg); prgm_highlight_row = 1; pending_command = CMD_NONE; redisplay(); } else { switch (varmenu_role) { case 0: /* Plain ol' VARMENU */ pending_command = mode_varmenu ? CMD_VMEXEC : CMD_VMSTO; break; case 1: /* Solver */ pending_command = mode_varmenu ? CMD_VMSOLVE : CMD_VMSTO2; break; case 2: /* Integrator */ if (mode_varmenu) { set_integ_var(varmenu_labeltext[menukey], varmenu_labellength[menukey]); set_menu(MENULEVEL_APP, MENU_INTEG_PARAMS); set_appmenu_exitcallback(5); redisplay(); return; } else pending_command = CMD_VMSTO; } } } return; } if (!shift && key == KEY_UP) { if (varmenu_rows > 1) { if (++varmenu_row >= varmenu_rows) varmenu_row = 0; pending_command = CMD_CANCELLED; } return; } if (!shift && key == KEY_DOWN) { if (varmenu_rows > 1) { if (--varmenu_row < 0) varmenu_row = varmenu_rows - 1; pending_command = CMD_CANCELLED; } return; } if (key == KEY_EXIT) { set_menu(MENULEVEL_APP, MENU_NONE); pending_command = CMD_CANCELLED; return; } } if (menukey != -1) { if (menu == MENU_CUSTOM1 || menu == MENU_CUSTOM2 || menu == MENU_CUSTOM3) { if (flags.f.local_label) { if (menukey == 5) { int cmd = shift ? CMD_GTO : CMD_XEQ; do_interactive(cmd); return; } else { pending_command = CMD_XEQ; pending_command_arg.type = ARGTYPE_LCLBL; if (menu == MENU_CUSTOM1) pending_command_arg.val.lclbl = shift ? 'a' + menukey : 'A' + menukey; else pending_command_arg.val.lclbl = 'F' + menukey; if (flags.f.prgm_mode) { store_command_after(&pc, pending_command, &pending_command_arg); prgm_highlight_row = 1; pending_command = CMD_NONE; set_menu(MENULEVEL_COMMAND, MENU_NONE); redisplay(); } return; } } else { int keynum, length; char name[7]; keynum = menukey + 6 * (menu - MENU_CUSTOM1) + 1; get_custom_key(keynum, name, &length); if (length == 0) squeak(); else { int dummyprgm; int4 dummypc; int i; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = length; for (i = 0; i < length; i++) pending_command_arg.val.text[i] = name[i]; if (find_global_label(&pending_command_arg, &dummyprgm, &dummypc)) pending_command = CMD_XEQ; else if (lookup_var(name, length) != -1) pending_command = CMD_RCL; else { int cmd = find_builtin(name, length); if (cmd == -1) pending_command = CMD_XEQ; else if (cmd == CMD_CLALLa) { mode_clall = true; pending_command = CMD_NONE; redisplay(); return; } else if (cmd == CMD_CLV || cmd == CMD_PRV) { if (!flags.f.prgm_mode && vars_count == 0) { display_error(ERR_NO_VARIABLES, 0); pending_command = CMD_NONE; redisplay(); return; } pending_command = CMD_NONE; do_interactive(cmd); return; } else if (cmd == CMD_SST && flags.f.prgm_mode) { sst(); pending_command = CMD_NONE; redisplay(); repeating = 1; repeating_shift = 1; repeating_key = KEY_DOWN; return; } else if (cmd == CMD_BST) { bst(); if (!flags.f.prgm_mode) { flags.f.prgm_mode = 1; redisplay(); flags.f.prgm_mode = 0; pending_command = CMD_CANCELLED; } else { redisplay(); pending_command = CMD_NONE; } repeating = 1; repeating_shift = 1; repeating_key = KEY_UP; return; } else if (cmdlist(cmd)->argtype == ARG_NONE) { pending_command = cmd; pending_command_arg.type = ARGTYPE_NONE; } else { pending_command = CMD_NONE; do_interactive(cmd); return; } } goto send_it_off; } return; } } else if (menu == MENU_CATALOG) { int catsect; catsect = get_cat_section(); if (catsect == CATSECT_TOP) { switch (menukey) { case 0: set_cat_section(CATSECT_FCN); move_cat_row(0); break; case 1: set_cat_section(CATSECT_PGM); move_cat_row(0); break; case 2: if (vars_exist(1, 0, 0)) { set_cat_section(CATSECT_REAL); move_cat_row(0); } else { display_error(ERR_NO_REAL_VARIABLES, 0); flush_display(); return; } break; case 3: if (vars_exist(0, 1, 0)) { set_cat_section(CATSECT_CPX); move_cat_row(0); } else { display_error(ERR_NO_COMPLEX_VARIABLES, 0); flush_display(); return; } break; case 4: if (vars_exist(0, 0, 1)) { set_cat_section(CATSECT_MAT); move_cat_row(0); } else { display_error(ERR_NO_MATRIX_VARIABLES, 0); flush_display(); return; } break; case 5: display_mem(); pending_command = CMD_LINGER1; shell_request_timeout3(2000); return; } redisplay(); return; } else if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY) { int labelindex = get_cat_item(menukey); if (labelindex == -1) { pending_command = CMD_NULL; return; } if (flags.f.prgm_mode && labels[labelindex].length == 0) { display_error(ERR_RESTRICTED_OPERATION, 0); flush_display(); pending_command = CMD_NONE; return; } pending_command = CMD_XEQ; pending_command_arg.type = ARGTYPE_LBLINDEX; pending_command_arg.val.num = labelindex; xeq_invisible = 1; if (!flags.f.prgm_mode && (level == MENULEVEL_TRANSIENT || !mode_plainmenu_sticky)) { if (level == MENULEVEL_PLAIN) { int row = get_cat_row(); set_menu(MENULEVEL_PLAIN, MENU_NONE); set_menu(MENULEVEL_TRANSIENT, MENU_CATALOG); set_cat_section(catsect); set_cat_row(row); } remove_program_catalog = 1; } } else if (catsect == CATSECT_PGM_SOLVE || catsect == CATSECT_PGM_INTEG) { int labelindex = get_cat_item(menukey); int i; if (labelindex == -1) { pending_command = CMD_NULL; return; } if (catsect == CATSECT_PGM_SOLVE) pending_command = flags.f.prgm_mode ? CMD_PGMSLV : CMD_PGMSLVi; else pending_command = flags.f.prgm_mode ? CMD_PGMINT : CMD_PGMINTi; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = labels[labelindex].length; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = labels[labelindex].name[i]; } else if (catsect == CATSECT_FCN) { int cmd = get_cat_item(menukey); if (cmd == -1) cmd = CMD_NULL; if (level == MENULEVEL_TRANSIENT || !mode_plainmenu_sticky) set_menu(level, MENU_NONE); do_interactive(cmd); return; } else { int varindex = get_cat_item(menukey); int i; if (varindex == -1) { pending_command = CMD_NULL; return; } pending_command = CMD_RCL; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = vars[varindex].length; for (i = 0; i < pending_command_arg.length; i++) pending_command_arg.val.text[i] = vars[varindex].name[i]; if (level == MENULEVEL_TRANSIENT || !mode_plainmenu_sticky) set_menu(level, MENU_NONE); } send_it_off: if (flags.f.prgm_mode && (cmdlist(pending_command)->flags & FLAG_IMMED) == 0) { store_command_after(&pc, pending_command, &pending_command_arg); prgm_highlight_row = 1; pending_command = CMD_NONE; if (level == MENULEVEL_TRANSIENT || (level == MENULEVEL_PLAIN && !mode_plainmenu_sticky)) set_menu(level, MENU_NONE); redisplay(); } return; } else if (menu == MENU_INTEG_PARAMS) { if (menukey <= 2) { const char *name; int length; switch (menukey) { case 0: name = "LLIM"; length = 4; break; case 1: name = "ULIM"; length = 4; break; case 2: name = "ACC"; length = 3; break; } if (shift && !flags.f.prgm_mode) view(name, length); else { int i; pending_command_arg.type = ARGTYPE_STR; pending_command_arg.length = length; for (i = 0; i < length; i++) pending_command_arg.val.text[i] = name[i]; if (flags.f.prgm_mode) { pending_command = shift ? CMD_VIEW : CMD_STO; store_command_after(&pc, pending_command, &pending_command_arg); prgm_highlight_row = 1; pending_command = CMD_NONE; redisplay(); } else { pending_command = CMD_VMSTO; } } } else if (menukey == 5) { int tmp; pending_command_arg.type = ARGTYPE_STR; get_integ_var(pending_command_arg.val.text, &tmp); pending_command_arg.length = tmp; pending_command = CMD_INTEG; } else pending_command = CMD_NULL; return; } else { const menu_item_spec *mi = menus[menu].child + menukey; int cmd_id = mi->menuid; const command_spec *cmd; if ((cmd_id & 0x3000) == 0) { set_menu(level, cmd_id); redisplay(); return; } if (menu == MENU_TOP_FCN && shift) { switch (menukey) { case 0: cmd_id = CMD_SIGMASUB; break; case 1: cmd_id = CMD_Y_POW_X; break; case 2: cmd_id = CMD_SQUARE; break; case 3: cmd_id = CMD_10_POW_X; break; case 4: cmd_id = CMD_E_POW_X; break; case 5: cmd_id = CMD_GTO; break; } } else if (menu == MENU_PGM_FCN1 && menukey == 5 && shift) cmd_id = CMD_GTO; else if (menu == MENU_STAT1 && menukey == 0 && shift) cmd_id = CMD_SIGMASUB; else cmd_id &= 0xfff; cmd = cmdlist(cmd_id); if (level == MENULEVEL_TRANSIENT || (level == MENULEVEL_PLAIN && !mode_plainmenu_sticky)) set_menu(level, MENU_NONE); do_interactive(cmd_id); return; } } if (!shift && (key == KEY_UP || key == KEY_DOWN)) { if (menu == MENU_CATALOG) { move_cat_row(key == KEY_UP ? -1 : 1); redisplay(); } else if (flags.f.local_label && (menu == MENU_CUSTOM1 || menu == MENU_CUSTOM2 || menu == MENU_CUSTOM3)) { set_menu(level, menu == MENU_CUSTOM1 ? MENU_CUSTOM2 : MENU_CUSTOM1); redisplay(); } else { const menu_spec *m = menus + menu; int nextmenu = key == KEY_UP ? m->prev : m->next; if (nextmenu != MENU_NONE) { set_menu(level, nextmenu); redisplay(); } } return; } if (key == KEY_EXIT) { if (menu == MENU_CATALOG) { int catsect = get_cat_section(); if (catsect == CATSECT_FCN || catsect == CATSECT_PGM || catsect == CATSECT_REAL || catsect == CATSECT_CPX || catsect == CATSECT_MAT) set_cat_section(CATSECT_TOP); else set_menu(level, MENU_NONE); } else { const menu_spec *m = menus + menu; set_menu(level, m->parent); } pending_command = CMD_CANCELLED; return; } } if (shift && key == KEY_ENTER) { if (deferred_print) print_command(CMD_NULL, NULL); flags.f.alpha_mode = 1; mode_alpha_entry = false; set_menu(MENULEVEL_ALPHA, MENU_ALPHA1); redisplay(); return; } if (key == KEY_EXIT && flags.f.prgm_mode) { flags.f.prgm_mode = 0; pending_command = CMD_CANCELLED; return; } if (key == KEY_UP) { /* Either shift == 1, or there is no menu; this * means BST. This requires special care because it's * one of the rare cases of auto-repeat. * TODO. */ clear_all_rtns(); squeak(); return; } if (!shift) { switch (key) { case KEY_SIGMA: command = CMD_SIGMAADD; break; case KEY_INV: command = CMD_INV; break; case KEY_SQRT: command = CMD_SQRT; break; case KEY_LOG: command = CMD_LOG; break; case KEY_LN: command = CMD_LN; break; case KEY_XEQ: command = CMD_XEQ; break; case KEY_STO: command = CMD_STO; break; case KEY_RCL: command = CMD_RCL; break; case KEY_RDN: command = CMD_RDN; break; case KEY_SIN: command = CMD_SIN; break; case KEY_COS: command = CMD_COS; break; case KEY_TAN: command = CMD_TAN; break; case KEY_ENTER: command = CMD_ENTER; break; case KEY_SWAP: command = CMD_SWAP; break; case KEY_CHS: command = basekeys() ? CMD_BASECHS : CMD_CHS; break; case KEY_BSP: command = CMD_CLX; break; case KEY_DIV: command = basekeys() ? CMD_BASEDIV : CMD_DIV; break; case KEY_DOWN: command = CMD_SST; break; case KEY_MUL: command = basekeys() ? CMD_BASEMUL : CMD_MUL; break; case KEY_SUB: command = basekeys() ? CMD_BASESUB : CMD_SUB; break; case KEY_EXIT: input_length = 0; pending_command = CMD_CANCELLED; return; case KEY_RUN: command = CMD_RUN; break; case KEY_ADD: command = basekeys() ? CMD_BASEADD : CMD_ADD; break; default: command = CMD_NONE; break; } } else { switch (key) { case KEY_SIGMA: command = CMD_SIGMASUB; break; case KEY_INV: command = CMD_Y_POW_X; break; case KEY_SQRT: command = CMD_SQUARE; break; case KEY_LOG: command = CMD_10_POW_X; break; case KEY_LN: command = CMD_E_POW_X; break; case KEY_XEQ: command = CMD_GTO; break; case KEY_STO: command = CMD_COMPLEX; break; case KEY_RCL: command = CMD_PERCENT; break; case KEY_RDN: command = CMD_PI; break; case KEY_SIN: command = CMD_ASIN; break; case KEY_COS: command = CMD_ACOS; break; case KEY_TAN: command = CMD_ATAN; break; case KEY_SWAP: command = CMD_LASTX; break; case KEY_CHS: set_plainmenu(MENU_MODES1); return; case KEY_E: set_plainmenu(MENU_DISP); return; case KEY_BSP: set_plainmenu(MENU_CLEAR1); return; case KEY_7: set_solve_integ(1); redisplay(); return; case KEY_8: set_solve_integ(0); redisplay(); return; case KEY_9: set_menu(MENULEVEL_APP, MENU_MATRIX1); redisplay(); return; case KEY_DIV: set_menu(MENULEVEL_APP, MENU_STAT1); redisplay(); return; case KEY_DOWN: command = CMD_SST; break; case KEY_4: set_menu(MENULEVEL_APP, MENU_BASE); if (mode_appmenu == MENU_BASE) { set_appmenu_exitcallback(2); baseapp = 1; redisplay(); } return; case KEY_5: set_plainmenu(MENU_CONVERT1); return; case KEY_6: set_plainmenu(MENU_FLAGS); return; case KEY_MUL: set_plainmenu(MENU_PROB); return; case KEY_1: command = CMD_ASSIGNa; break; case KEY_2: set_plainmenu(MENU_CUSTOM1); return; case KEY_3: set_plainmenu(MENU_PGM_FCN1); return; case KEY_SUB: set_plainmenu(MENU_PRINT1); return; case KEY_DOT: show(); pending_command = CMD_LINGER1; shell_request_timeout3(2000); return; case KEY_0: set_plainmenu(MENU_TOP_FCN); return; case KEY_ADD: set_plainmenu(MENU_CATALOG); return; default: command = CMD_NONE; break; } } if (command == CMD_NONE) return; else do_interactive(command); } free42-nologo-1.4.77/common/core_keydown.h000644 000765 000024 00000002146 12110237247 020765 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_KEYDOWN_H #define CORE_KEYDOWN_H 1 #include "free42.h" void keydown(int shift, int key); void keydown_number_entry(int shift, int key); void keydown_command_entry(int shift, int key); void keydown_alpha_mode(int shift, int key); void keydown_normal_mode(int shift, int key); #endif free42-nologo-1.4.77/common/core_linalg1.cc000644 000765 000024 00000104734 12110237247 021000 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_linalg1.h" #include "core_linalg2.h" #include "core_main.h" #include "core_variables.h" /**********************************/ /***** Matrix-matrix division *****/ /**********************************/ static void (*linalg_div_completion)(int, vartype *); static const vartype *linalg_div_left; static vartype *linalg_div_result; static int div_rr_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det); static void div_rr_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b); static int div_rc_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im); static void div_rc_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b); static int div_cr_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det); static void div_cr_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_complexmatrix *b); static int div_cc_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im); static void div_cc_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b); int linalg_div(const vartype *left, const vartype *right, void (*completion)(int, vartype *)) { if (left->type == TYPE_REALMATRIX) { if (right->type == TYPE_REALMATRIX) { vartype_realmatrix *num = (vartype_realmatrix *) left; vartype_realmatrix *denom = (vartype_realmatrix *) right; vartype *lu, *res; int4 rows = num->rows; int4 columns = num->columns; int4 *perm; if (denom->rows != rows || denom->columns != rows) { completion(ERR_DIMENSION_ERROR, NULL); return ERR_DIMENSION_ERROR; } perm = (int4 *) malloc(rows * sizeof(int4)); if (perm == NULL) { completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } lu = new_realmatrix(rows, rows); if (lu == NULL) { free(perm); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } res = new_realmatrix(rows, columns); if (res == NULL) { free(perm); free_vartype(lu); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, right); linalg_div_completion = completion; linalg_div_left = left; linalg_div_result = res; return lu_decomp_r((vartype_realmatrix *) lu, perm, div_rr_completion1); } else { vartype_realmatrix *num = (vartype_realmatrix *) left; vartype_complexmatrix *denom = (vartype_complexmatrix *) right; vartype *lu, *res; int4 rows = num->rows; int4 columns = num->columns; int4 *perm; if (denom->rows != rows || denom->columns != rows) { completion(ERR_DIMENSION_ERROR, NULL); return ERR_DIMENSION_ERROR; } perm = (int4 *) malloc(rows * sizeof(int4)); if (perm == NULL) { completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } lu = new_complexmatrix(rows, rows); if (lu == NULL) { free(perm); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } res = new_complexmatrix(rows, columns); if (res == NULL) { free(perm); free_vartype(lu); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, right); linalg_div_completion = completion; linalg_div_left = left; linalg_div_result = res; return lu_decomp_c((vartype_complexmatrix *) lu, perm, div_rc_completion1); } } else { if (right->type == TYPE_REALMATRIX) { vartype_complexmatrix *num = (vartype_complexmatrix *) left; vartype_realmatrix *denom = (vartype_realmatrix *) right; vartype *lu, *res; int4 rows = num->rows; int4 columns = num->columns; int4 *perm; if (denom->rows != rows || denom->columns != rows) { completion(ERR_DIMENSION_ERROR, 0); return ERR_DIMENSION_ERROR; } perm = (int4 *) malloc(rows * sizeof(int4)); if (perm == NULL) { completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } lu = new_realmatrix(rows, rows); if (lu == NULL) { free(perm); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } res = new_complexmatrix(rows, columns); if (res == NULL) { free(perm); free_vartype(lu); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, right); linalg_div_completion = completion; linalg_div_left = left; linalg_div_result = res; return lu_decomp_r((vartype_realmatrix *) lu, perm, div_cr_completion1); } else { vartype_complexmatrix *num = (vartype_complexmatrix *) left; vartype_complexmatrix *denom = (vartype_complexmatrix *) right; vartype *lu, *res; int4 rows = num->rows; int4 columns = num->columns; int4 *perm; if (denom->rows != rows || denom->columns != rows) { completion(ERR_DIMENSION_ERROR, NULL); return ERR_DIMENSION_ERROR; } perm = (int4 *) malloc(rows * sizeof(int4)); if (perm == NULL) { completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } lu = new_complexmatrix(rows, rows); if (lu == NULL) { free(perm); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } res = new_complexmatrix(rows, columns); if (res == NULL) { free(perm); free_vartype(lu); completion(ERR_INSUFFICIENT_MEMORY, NULL); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, right); linalg_div_completion = completion; linalg_div_left = left; linalg_div_result = res; return lu_decomp_c((vartype_complexmatrix *) lu, perm, div_cc_completion1); } } } static int div_rr_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det) { if (error != ERR_NONE) { free_vartype((vartype *) a); free(perm); free_vartype(linalg_div_result); return error; } else { matrix_copy(linalg_div_result, linalg_div_left); return lu_backsubst_rr(a, perm, (vartype_realmatrix *) linalg_div_result, div_rr_completion2); } } static void div_rr_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_div_result); /* Note: linalg_div_result == b */ free_vartype((vartype *) a); free(perm); linalg_div_completion(error, linalg_div_result); } static int div_rc_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im) { if (error != ERR_NONE) { free_vartype((vartype *) a); free(perm); free_vartype(linalg_div_result); return error; } else { matrix_copy(linalg_div_result, linalg_div_left); return lu_backsubst_cc(a, perm, (vartype_complexmatrix *) linalg_div_result, div_rc_completion2); } } static void div_rc_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_div_result); /* Note: linalg_div_result == b */ free_vartype((vartype *) a); free(perm); linalg_div_completion(error, linalg_div_result); } static int div_cr_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det) { if (error != ERR_NONE) { free_vartype((vartype *) a); free(perm); free_vartype(linalg_div_result); return error; } else { matrix_copy(linalg_div_result, linalg_div_left); return lu_backsubst_rc(a, perm, (vartype_complexmatrix *) linalg_div_result, div_cr_completion2); } } static void div_cr_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_complexmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_div_result); /* Note: linalg_div_result == b */ free_vartype((vartype *) a); free(perm); linalg_div_completion(error, linalg_div_result); } static int div_cc_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im) { if (error != ERR_NONE) { free_vartype((vartype *) a); free(perm); free_vartype(linalg_div_result); return error; } else { matrix_copy(linalg_div_result, linalg_div_left); return lu_backsubst_cc(a, perm, (vartype_complexmatrix *) linalg_div_result, div_cc_completion2); } } static void div_cc_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_div_result); /* Note: linalg_div_result == b */ free_vartype((vartype *) a); free(perm); linalg_div_completion(error, linalg_div_result); } /****************************************/ /***** Matrix-matrix multiplication *****/ /****************************************/ typedef struct { vartype_realmatrix *left; vartype_realmatrix *right; vartype *result; int4 i, j, k; phloat sum; void (*completion)(int error, vartype *result); } mul_rr_data_struct; static mul_rr_data_struct *mul_rr_data; static int matrix_mul_rr_worker(int interrupted); static int matrix_mul_rr(vartype_realmatrix *left, vartype_realmatrix *right, void (*completion)(int, vartype *)) { mul_rr_data_struct *dat; int error; if (left->columns != right->rows) { error = ERR_DIMENSION_ERROR; goto finished; } if (!contains_no_strings(left) || !contains_no_strings(right)) { error = ERR_ALPHA_DATA_IS_INVALID; goto finished; } dat = (mul_rr_data_struct *) malloc(sizeof(mul_rr_data_struct)); if (dat == NULL) { error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->result = new_realmatrix(left->rows, right->columns); if (dat->result == NULL) { free(dat); error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->left = left; dat->right = right; dat->i = 0; dat->j = 0; dat->k = 0; dat->sum = 0; dat->completion = completion; mul_rr_data = dat; mode_interruptible = matrix_mul_rr_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; finished: completion(error, NULL); return error; } static int matrix_mul_rr_worker(int interrupted) { mul_rr_data_struct *dat = mul_rr_data; int count = 0; int inf; phloat *l = dat->left->array->data; phloat *r = dat->right->array->data; phloat *p = ((vartype_realmatrix *) dat->result)->array->data; int4 i = dat->i; int4 j = dat->j; int4 k = dat->k; int4 m = dat->left->rows; int4 n = dat->right->columns; int4 q = dat->left->columns; phloat sum = dat->sum; if (interrupted) { dat->completion(ERR_INTERRUPTED, NULL); free_vartype(dat->result); free(dat); return ERR_INTERRUPTED; } while (count++ < 1000) { sum += l[i * q + k] * r[k * n + j]; if (++k < q) continue; k = 0; if ((inf = p_isinf(sum)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[i * n + j] = sum; sum = 0; if (++j < n) continue; j = 0; if (++i < m) continue; else { dat->completion(ERR_NONE, dat->result); free(dat); return ERR_NONE; } } dat->i = i; dat->j = j; dat->k = k; dat->sum = sum; return ERR_INTERRUPTIBLE; } #if 0 /* TODO: Blocked matrix multiplication * This commented-out function implements a working blocked matrix * multiplication algorithm. It works by breaking up the multiplication into * multiplications of submatrices of the multiplicands. If the submatrices are * sufficiently small, they can be made to fit entirely in the CPU's L1 cache. * With block sizes of around 45, I have observed a speed-up factor of circa * 1.7 on a Pentium MMX, and with block sizes of about 90, I have observed a * speed-up factor of circa 3 on a Duron. (Those were the optimum block sizes * observed for each processor.) These numbers suggest the Pentium MMX has a 32 * kilobyte L1 cache, and the Duron has a 128 kilobyte L1 cache. * For production use, the block size should be a user-configurable parameter, * with a built-in tool to automatically determine the optimum value. Since the * UI for this kind of advanced configuration does not exist yet, and sice the * determination of optimum block size is a very slow process, and since I * expect most HP-42S users will not be very interested in using a calculator * to work with order-100+ matrices, and since even in the optimum case the * speed-up is not staggering, I'm putting full implementation of this feature * off until some later date. */ static int matrix_mul_rr(vartype_realmatrix *left, vartype_realmatrix *right, vartype **result) { phloat *cache = NULL; phloat *leftcache, *rightcache; phloat *l = left->array->data; phloat *r = right->array->data; phloat *p; int inf; int4 m, n, q; int4 i, j, k; q = left->columns; if (q != right->rows) return ERR_DIMENSION_ERROR; m = left->rows; n = right->columns; if (!contains_no_strings(left) || !contains_no_strings(right)) return ERR_ALPHA_DATA_IS_INVALID; *result = new_realmatrix(m, n); if (*result == NULL) return ERR_INSUFFICIENT_MEMORY; p = ((vartype_realmatrix *) *result)->array->data; if (BLOCK_SIZE > 0) { int4 cachesize = 2 * BLOCK_SIZE * BLOCK_SIZE; int4 alignment = CACHE_ALIGNMENT; int4 misalgn; if (alignment == 0) alignment = 1; misalgn = alignment % sizeof(phloat); if (misalgn != 0) alignment += sizeof(phloat) - misalgn; cache = (phloat *) malloc(cachesize * sizeof(phloat) + alignment); if (cache != NULL) { misalgn = ((unsigned long) cache) % alignment; if (misalgn == 0) leftcache = cache; else leftcache = cache + (alignment - misalgn) / sizeof(phloat); rightcache = leftcache + BLOCK_SIZE * BLOCK_SIZE; } } if (cache == NULL) { /* Falling back on basic i,j,k algorithm */ for (i = 0; i < m; i++) for (j = 0; j < n; j++) { phloat sum = 0; for (k = 0; k < q; k++) sum += l[i * q + k] * r[k * n + j]; if ((inf = p_isinf(sum)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else sum = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[i * n + j] = sum; } return ERR_NONE; } else { /* Blocked i,j,k algorithm for optimal cache utilization */ int4 i, j, k, ii, jj, kk; for (i = 0; i < m; i += BLOCK_SIZE) { int4 iimax = m - i; if (iimax > BLOCK_SIZE) iimax = BLOCK_SIZE; for (j = 0; j < n; j += BLOCK_SIZE) { int4 jjmax = n - j; if (jjmax > BLOCK_SIZE) jjmax = BLOCK_SIZE; for (k = 0; k < q; k += BLOCK_SIZE) { int4 kkmax = q - k; if (kkmax > BLOCK_SIZE) kkmax = BLOCK_SIZE; for (ii = 0; ii < iimax; ii++) for (kk = 0; kk < kkmax; kk++) leftcache[ii * BLOCK_SIZE + kk] = l[(ii + i) * q + (kk + k)]; for (kk = 0; kk < kkmax; kk++) for (jj = 0; jj < jjmax; jj++) rightcache[kk * BLOCK_SIZE + jj] = r[(kk + k) * n + (jj + j)]; for (ii = 0; ii < iimax; ii++) for (jj = 0; jj < jjmax; jj++) { phloat sum = p[(ii + i) * n + (jj + j)]; for (kk = 0; kk < kkmax; kk++) sum += leftcache[ii * BLOCK_SIZE + kk] * rightcache[kk * BLOCK_SIZE + jj]; if ((inf = p_isinf(sum)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) { free(cache); return ERR_OUT_OF_RANGE; } else sum = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[(ii + i) * n + (jj + j)] = sum; } } } } free(cache); return ERR_NONE; } } #endif typedef struct { vartype_realmatrix *left; vartype_complexmatrix *right; vartype *result; int4 i, j, k; phloat sum_re, sum_im; void (*completion)(int error, vartype *result); } mul_rc_data_struct; static mul_rc_data_struct *mul_rc_data; static int matrix_mul_rc_worker(int interrupted); static int matrix_mul_rc(vartype_realmatrix *left, vartype_complexmatrix *right, void (*completion)(int, vartype *)) { mul_rc_data_struct *dat; int error; if (left->columns != right->rows) { error = ERR_DIMENSION_ERROR; goto finished; } if (!contains_no_strings(left)) { error = ERR_ALPHA_DATA_IS_INVALID; goto finished; } dat = (mul_rc_data_struct *) malloc(sizeof(mul_rc_data_struct)); if (dat == NULL) { error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->result = new_complexmatrix(left->rows, right->columns); if (dat->result == NULL) { free(dat); error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->left = left; dat->right = right; dat->i = 0; dat->j = 0; dat->k = 0; dat->sum_re = 0; dat->sum_im = 0; dat->completion = completion; mul_rc_data = dat; mode_interruptible = matrix_mul_rc_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; finished: completion(error, NULL); return error; } static int matrix_mul_rc_worker(int interrupted) { mul_rc_data_struct *dat = mul_rc_data; int count = 0; int inf; phloat *l = dat->left->array->data; phloat *r = dat->right->array->data; phloat *p = ((vartype_complexmatrix *) dat->result)->array->data; int4 i = dat->i; int4 j = dat->j; int4 k = dat->k; int4 m = dat->left->rows; int4 n = dat->right->columns; int4 q = dat->left->columns; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; if (interrupted) { dat->completion(ERR_INTERRUPTED, NULL); free_vartype(dat->result); free(dat); return ERR_INTERRUPTED; } while (count++ < 1000) { phloat tmp = l[i * q + k]; sum_re += tmp * r[2 * (k * n + j)]; sum_im += tmp * r[2 * (k * n + j) + 1]; if (++k < q) continue; k = 0; if ((inf = p_isinf(sum_re)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if ((inf = p_isinf(sum_im)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[2 * (i * n + j)] = sum_re; p[2 * (i * n + j) + 1] = sum_im; sum_re = 0; sum_im = 0; if (++j < n) continue; j = 0; if (++i < m) continue; else { dat->completion(ERR_NONE, dat->result); free(dat); return ERR_NONE; } } dat->i = i; dat->j = j; dat->k = k; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } typedef struct { vartype_complexmatrix *left; vartype_realmatrix *right; vartype *result; int4 i, j, k; phloat sum_re, sum_im; void (*completion)(int error, vartype *result); } mul_cr_data_struct; static mul_cr_data_struct *mul_cr_data; static int matrix_mul_cr_worker(int interrupted); static int matrix_mul_cr(vartype_complexmatrix *left, vartype_realmatrix *right, void (*completion)(int, vartype *)) { mul_cr_data_struct *dat; int error; if (left->columns != right->rows) { error = ERR_DIMENSION_ERROR; goto finished; } if (!contains_no_strings(right)) { error = ERR_ALPHA_DATA_IS_INVALID; goto finished; } dat = (mul_cr_data_struct *) malloc(sizeof(mul_cr_data_struct)); if (dat == NULL) { error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->result = new_complexmatrix(left->rows, right->columns); if (dat->result == NULL) { free(dat); error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->left = left; dat->right = right; dat->i = 0; dat->j = 0; dat->k = 0; dat->sum_re = 0; dat->sum_im = 0; dat->completion = completion; mul_cr_data = dat; mode_interruptible = matrix_mul_cr_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; finished: completion(error, NULL); return error; } static int matrix_mul_cr_worker(int interrupted) { mul_cr_data_struct *dat = mul_cr_data; int count = 0; int inf; phloat *l = dat->left->array->data; phloat *r = dat->right->array->data; phloat *p = ((vartype_complexmatrix *) dat->result)->array->data; int4 i = dat->i; int4 j = dat->j; int4 k = dat->k; int4 m = dat->left->rows; int4 n = dat->right->columns; int4 q = dat->left->columns; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; if (interrupted) { dat->completion(ERR_INTERRUPTED, NULL); free_vartype(dat->result); free(dat); return ERR_INTERRUPTED; } while (count++ < 1000) { phloat tmp = r[k * n + j]; sum_re += tmp * l[2 * (i * q + k)]; sum_im += tmp * l[2 * (i * q + k) + 1]; if (++k < q) continue; k = 0; if ((inf = p_isinf(sum_re)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if ((inf = p_isinf(sum_im)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[2 * (i * n + j)] = sum_re; p[2 * (i * n + j) + 1] = sum_im; sum_re = 0; sum_im = 0; if (++j < n) continue; j = 0; if (++i < m) continue; else { dat->completion(ERR_NONE, dat->result); free(dat); return ERR_NONE; } } dat->i = i; dat->j = j; dat->k = k; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } typedef struct { vartype_complexmatrix *left; vartype_complexmatrix *right; vartype *result; int4 i, j, k; phloat sum_re, sum_im; void (*completion)(int error, vartype *result); } mul_cc_data_struct; static mul_cc_data_struct *mul_cc_data; static int matrix_mul_cc_worker(int interrupted); static int matrix_mul_cc(vartype_complexmatrix *left, vartype_complexmatrix *right, void (*completion)(int, vartype *)) { mul_cc_data_struct *dat; int error; if (left->columns != right->rows) { error = ERR_DIMENSION_ERROR; goto finished; } dat = (mul_cc_data_struct *) malloc(sizeof(mul_cc_data_struct)); if (dat == NULL) { error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->result = new_complexmatrix(left->rows, right->columns); if (dat->result == NULL) { free(dat); error = ERR_INSUFFICIENT_MEMORY; goto finished; } dat->left = left; dat->right = right; dat->i = 0; dat->j = 0; dat->k = 0; dat->sum_re = 0; dat->sum_im = 0; dat->completion = completion; mul_cc_data = dat; mode_interruptible = matrix_mul_cc_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; finished: completion(error, NULL); return error; } static int matrix_mul_cc_worker(int interrupted) { mul_cc_data_struct *dat = mul_cc_data; int count = 0; int inf; phloat *l = dat->left->array->data; phloat *r = dat->right->array->data; phloat *p = ((vartype_complexmatrix *) dat->result)->array->data; int4 i = dat->i; int4 j = dat->j; int4 k = dat->k; int4 m = dat->left->rows; int4 n = dat->right->columns; int4 q = dat->left->columns; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; if (interrupted) { dat->completion(ERR_INTERRUPTED, NULL); free_vartype(dat->result); free(dat); return ERR_INTERRUPTED; } while (count++ < 1000) { phloat l_re = l[2 * (i * q + k)]; phloat l_im = l[2 * (i * q + k) + 1]; phloat r_re = r[2 * (k * n + j)]; phloat r_im = r[2 * (k * n + j) + 1]; sum_re += l_re * r_re - l_im * r_im; sum_im += l_im * r_re + l_re * r_im; if (++k < q) continue; k = 0; if ((inf = p_isinf(sum_re)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if ((inf = p_isinf(sum_im)) != 0) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore){ dat->completion(ERR_OUT_OF_RANGE, NULL); free_vartype(dat->result); free(dat); return ERR_OUT_OF_RANGE; } else sum_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } p[2 * (i * n + j)] = sum_re; p[2 * (i * n + j) + 1] = sum_im; sum_re = 0; sum_im = 0; if (++j < n) continue; j = 0; if (++i < m) continue; else { dat->completion(ERR_NONE, dat->result); free(dat); return ERR_NONE; } } dat->i = i; dat->j = j; dat->k = k; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } int linalg_mul(const vartype *left, const vartype *right, void (*completion)(int, vartype *)) { if (left->type == TYPE_REALMATRIX) { if (right->type == TYPE_REALMATRIX) return matrix_mul_rr((vartype_realmatrix *) left, (vartype_realmatrix *) right, completion); else return matrix_mul_rc((vartype_realmatrix *) left, (vartype_complexmatrix *) right, completion); } else { if (right->type == TYPE_REALMATRIX) return matrix_mul_cr((vartype_complexmatrix *) left, (vartype_realmatrix *) right, completion); else return matrix_mul_cc((vartype_complexmatrix *) left, (vartype_complexmatrix *) right, completion); } } /**************************/ /***** Matrix inverse *****/ /**************************/ static void (*linalg_inv_completion)(int error, vartype *det); static vartype *linalg_inv_result; static int inv_r_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det); static void inv_r_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b); static int inv_c_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im); static void inv_c_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b); int linalg_inv(const vartype *src, void (*completion)(int, vartype *)) { int4 n; int4 *perm; if (src->type == TYPE_REALMATRIX) { vartype_realmatrix *ma = (vartype_realmatrix *) src; vartype *lu, *inv; n = ma->rows; if (n != ma->columns) return ERR_DIMENSION_ERROR; if (!contains_no_strings(ma)) return ERR_ALPHA_DATA_IS_INVALID; lu = new_realmatrix(n, n); if (lu == NULL) return ERR_INSUFFICIENT_MEMORY; inv = new_realmatrix(n, n); if (inv == NULL) { free_vartype(lu); return ERR_INSUFFICIENT_MEMORY; } perm = (int4 *) malloc(n * sizeof(int4)); if (perm == NULL) { free_vartype(lu); free_vartype(inv); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, src); linalg_inv_completion = completion; linalg_inv_result = inv; return lu_decomp_r((vartype_realmatrix *) lu, perm, inv_r_completion1); } else { vartype_complexmatrix *ma = (vartype_complexmatrix *) src; vartype *lu, *inv; n = ma->rows; if (n != ma->columns) return ERR_DIMENSION_ERROR; lu = new_complexmatrix(n, n); if (lu == NULL) return ERR_INSUFFICIENT_MEMORY; inv = new_complexmatrix(n, n); if (inv == NULL) { free_vartype(lu); return ERR_INSUFFICIENT_MEMORY; } perm = (int4 *) malloc(n * sizeof(int4)); if (perm == NULL) { free_vartype(lu); free_vartype(inv); return ERR_INSUFFICIENT_MEMORY; } matrix_copy(lu, src); linalg_inv_completion = completion; linalg_inv_result = inv; return lu_decomp_c((vartype_complexmatrix *) lu, perm, inv_c_completion1); } } static int inv_r_completion1(int error, vartype_realmatrix *a, int4 *perm, phloat det) { if (error != ERR_NONE) { free_vartype(linalg_inv_result); free_vartype((vartype *) a); free(perm); linalg_inv_completion(error, NULL); return error; } else { int4 i, n = a->rows; vartype_realmatrix *inv = (vartype_realmatrix *) linalg_inv_result; for (i = 0; i < n; i++) inv->array->data[i * (n + 1)] = 1; return lu_backsubst_rr(a, perm, inv, inv_r_completion2); } } static void inv_r_completion2(int error, vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_inv_result); /* Note: linalg_inv_result == b */ free_vartype((vartype *) a); free(perm); linalg_inv_completion(error, linalg_inv_result); } static int inv_c_completion1(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im) { if (error != ERR_NONE) { free_vartype(linalg_inv_result); free_vartype((vartype *) a); free(perm); linalg_inv_completion(error, NULL); return error; } else { int4 i, n = a->rows; vartype_complexmatrix *inv = (vartype_complexmatrix *) linalg_inv_result; for (i = 0; i < n; i++) inv->array->data[2 * (i * (n + 1))] = 1; return lu_backsubst_cc(a, perm, inv, inv_c_completion2); } } static void inv_c_completion2(int error, vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b) { if (error != ERR_NONE) free_vartype(linalg_inv_result); /* Note: linalg_inv_result == b */ free_vartype((vartype *) a); free(perm); linalg_inv_completion(error, linalg_inv_result); } /******************************/ /***** Matrix determinant *****/ /******************************/ static void (*linalg_det_completion)(int error, vartype *det); static bool linalg_det_prev_sm_err; static int det_r_completion(int error, vartype_realmatrix *a, int4 *perm, phloat det); static int det_c_completion(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im); int linalg_det(const vartype *src, void (*completion)(int, vartype *)) { int4 n; int4 *perm; if (src->type == TYPE_REALMATRIX) { vartype_realmatrix *ma = (vartype_realmatrix *) src; n = ma->rows; if (n != ma->columns) { completion(ERR_DIMENSION_ERROR, 0); return ERR_DIMENSION_ERROR; } if (!contains_no_strings(ma)) { completion(ERR_ALPHA_DATA_IS_INVALID, 0); return ERR_ALPHA_DATA_IS_INVALID; } ma = (vartype_realmatrix *) dup_vartype(src); if (ma == NULL) { completion(ERR_INSUFFICIENT_MEMORY, 0); return ERR_INSUFFICIENT_MEMORY; } if (!disentangle((vartype *) ma)) { free_vartype((vartype *) ma); completion(ERR_INSUFFICIENT_MEMORY, 0); return ERR_INSUFFICIENT_MEMORY; } perm = (int4 *) malloc(n * sizeof(int4)); if (perm == NULL) { free_vartype((vartype *) ma); completion(ERR_INSUFFICIENT_MEMORY, 0); return ERR_INSUFFICIENT_MEMORY; } /* Before calling lu_decomp_r, make sure the 'singular matrix' * error reporting mode is on; we don't want the HP-42S compatible * zero-pivot-fudging to take place when all we're doing is computing * the determinant. * The completion routine will restore the 'singular matrix' error * mode to its original value. */ linalg_det_prev_sm_err = core_settings.matrix_singularmatrix; core_settings.matrix_singularmatrix = true; linalg_det_completion = completion; return lu_decomp_r(ma, perm, det_r_completion); } else /* src->type == TYPE_COMPLEXMATRIX */ { vartype_complexmatrix *ma = (vartype_complexmatrix *) src; n = ma->rows; if (n != ma->columns) return ERR_DIMENSION_ERROR; ma = (vartype_complexmatrix *) dup_vartype(src); if (ma == NULL) return ERR_INSUFFICIENT_MEMORY; if (!disentangle((vartype *) ma)) { free_vartype((vartype *) ma); return ERR_INSUFFICIENT_MEMORY; } n = ma->rows; perm = (int4 *) malloc(n * sizeof(int4)); if (perm == NULL) { free_vartype((vartype *) ma); return ERR_INSUFFICIENT_MEMORY; } /* Before calling lu_decomp_c, make sure the 'singular matrix' * error reporting mode is on; we don't want the HP-42S compatible * zero-pivot-fudging to take place when all we're doing is computing * the determinant. * The completion routine will restore the 'singular matrix' error * mode to its original value. */ linalg_det_prev_sm_err = core_settings.matrix_singularmatrix; core_settings.matrix_singularmatrix = true; linalg_det_completion = completion; return lu_decomp_c(ma, perm, det_c_completion); } } static int det_r_completion(int error, vartype_realmatrix *a, int4 *perm, phloat det) { vartype *det_v; core_settings.matrix_singularmatrix = linalg_det_prev_sm_err; free_vartype((vartype *) a); free(perm); if (error == ERR_SINGULAR_MATRIX) { det = 0; error = ERR_NONE; } if (error == ERR_NONE) { int inf = p_isinf(det); if (inf != 0) { if (flags.f.range_error_ignore) det = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else error = ERR_OUT_OF_RANGE; } } if (error == ERR_NONE) { det_v = new_real(det); if (det_v == NULL) error = ERR_INSUFFICIENT_MEMORY; } linalg_det_completion(error, det_v); return error; } static int det_c_completion(int error, vartype_complexmatrix *a, int4 *perm, phloat det_re, phloat det_im) { vartype *det_v; core_settings.matrix_singularmatrix = linalg_det_prev_sm_err; free_vartype((vartype *) a); free(perm); if (error == ERR_SINGULAR_MATRIX) { det_re = 0; det_im = 0; error = ERR_NONE; } if (error == ERR_NONE) { int inf; if ((inf = p_isinf(det_re)) != 0) { if (flags.f.range_error_ignore) det_re = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else error = ERR_OUT_OF_RANGE; } if ((inf = p_isinf(det_im)) != 0) { if (flags.f.range_error_ignore) det_im = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else error = ERR_OUT_OF_RANGE; } } if (error == ERR_NONE) { det_v = new_complex(det_re, det_im); if (det_v == NULL) error = ERR_INSUFFICIENT_MEMORY; } linalg_det_completion(error, det_v); return error; } free42-nologo-1.4.77/common/core_linalg1.h000644 000765 000024 00000002356 12110237247 020637 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_LINALG1_H #define CORE_LINALG1_H 1 #include "core_globals.h" int linalg_div(const vartype *left, const vartype *right, void (*completion)(int, vartype *)); int linalg_mul(const vartype *left, const vartype *right, void (*completion)(int, vartype *)); int linalg_inv(const vartype *src, void (*completion)(int, vartype *)); int linalg_det(const vartype *src, void (*completion)(int, vartype *)); #endif free42-nologo-1.4.77/common/core_linalg2.cc000644 000765 000024 00000047604 12110237247 021003 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_linalg2.h" #include "core_globals.h" #include "core_main.h" #define STATE(s) \ if (--count <= 0) { \ dat->state = s; \ goto suspend; \ } \ state##s: \ ; /****************************/ /***** LU decomposition *****/ /****************************/ typedef struct { vartype_realmatrix *a; int4 *perm; phloat det; int4 i, imax, j, k; phloat max, tmp, sum, *scale; int state; int (*completion)(int, vartype_realmatrix *, int4 *, phloat); } lu_r_data_struct; lu_r_data_struct *lu_r_data; static int lu_decomp_r_worker(int interrupted); int lu_decomp_r(vartype_realmatrix *a, int4 *perm, int (*completion)(int, vartype_realmatrix *, int4 *, phloat)) { lu_r_data_struct *dat = (lu_r_data_struct *) malloc(sizeof(lu_r_data_struct)); if (dat == NULL) return completion(ERR_INSUFFICIENT_MEMORY, a, perm, 0); dat->scale = (phloat *) malloc(a->rows * sizeof(phloat)); if (dat->scale == NULL) { free(dat); return completion(ERR_INSUFFICIENT_MEMORY, a, perm, 0); } dat->a = a; dat->perm = perm; dat->completion = completion; dat->state = 0; lu_r_data = dat; mode_interruptible = lu_decomp_r_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; } static int lu_decomp_r_worker(int interrupted) { lu_r_data_struct *dat = lu_r_data; phloat *a = dat->a->array->data; int4 n = dat->a->rows; phloat *scale = dat->scale; int4 *perm = dat->perm; int count = 1000; int err; int4 i = dat->i; int4 imax = dat->imax; int4 j = dat->j; int4 k = dat->k; phloat max = dat->max; phloat tmp = dat->tmp; phloat sum = dat->sum; if (interrupted) { free(scale); err = dat->completion(ERR_INTERRUPTED, dat->a, perm, 0); free(dat); return err; } switch (dat->state) { case 0: break; case 1: goto state1; case 2: goto state2; case 3: goto state3; case 4: goto state4; case 5: goto state5; } dat->det = 1; for (i = 0; i < n; i++) { max = 0; for (j = 0; j < n; j++) { tmp = a[i * n + j]; if (tmp < 0) tmp = -tmp; if (tmp > max) max = tmp; STATE(1); } scale[i] = max; } for (j = 0; j < n; j++) { for (i = 0; i < j; i++) { sum = a[i * n + j]; for (k = 0; k < i; k++) { sum -= a[i * n + k] * a[k * n + j]; STATE(2); } a[i * n + j] = sum; } max = 0; imax = j; for (i = j; i < n; i++) { sum = a[i * n + j]; for (k = 0; k < j; k++) { sum -= a[i * n + k] * a[k * n + j]; STATE(3); } a[i * n + j] = sum; if (scale[i] == 0) { imax = i; break; } tmp = (sum < 0 ? -sum : sum) / scale[i]; if (tmp > max) { imax = i; max = tmp; } } if (j != imax) { for (k = 0; k < n; k++) { tmp = a[imax * n + k]; a[imax * n + k] = a[j * n + k]; a[j * n + k] = tmp; STATE(4); } dat->det = -dat->det; scale[imax] = scale[j]; } perm[j] = imax; if (a[j * n + j] == 0) { if (core_settings.matrix_singularmatrix) { free(scale); err = dat->completion(ERR_SINGULAR_MATRIX, dat->a, perm, 0); free(dat); return err; } else { /* For a zero pivot, substitute a small positive number. * I use a number that's about 10^-20 times the size of * the maximum of the original column, with a minimum of * 10^20 / POS_HUGE_PHLOAT. */ phloat tiniest = 1e20 / POS_HUGE_PHLOAT; phloat tiny; if (scale[j] == 0) tiny = tiniest; else { tiny = pow(10, floor(log10(scale[j])) - 20); if (tiny < tiniest) tiny = tiniest; } a[j * n + j] = tiny; } } dat->det *= a[j * n + j]; if (j != n - 1) { tmp = 1 / a[j * n + j]; for (i = j + 1; i < n; i++) { a[i * n + j] *= tmp; STATE(5); } } } free(scale); err = dat->completion(ERR_NONE, dat->a, perm, dat->det); free(dat); return err; suspend: dat->i = i; dat->imax = imax; dat->j = j; dat->k = k; dat->max = max; dat->tmp = tmp; dat->sum = sum; return ERR_INTERRUPTIBLE; } typedef struct { vartype_complexmatrix *a; int4 *perm; phloat det_re, det_im; int4 i, imax, j, k; phloat max, tmp, tmp_re, tmp_im, sum_re, sum_im, *scale; int state; int (*completion)(int, vartype_complexmatrix *, int4 *, phloat, phloat); } lu_c_data_struct; lu_c_data_struct *lu_c_data; static int lu_decomp_c_worker(int interrupted); int lu_decomp_c(vartype_complexmatrix *a, int4 *perm, int (*completion)(int, vartype_complexmatrix *, int4 *, phloat, phloat)) { lu_c_data_struct *dat = (lu_c_data_struct *) malloc(sizeof(lu_c_data_struct)); if (dat == NULL) return completion(ERR_INSUFFICIENT_MEMORY, a, perm, 0, 0); dat->scale = (phloat *) malloc(a->rows * sizeof(phloat)); if (dat->scale == NULL) { free(dat); return completion(ERR_INSUFFICIENT_MEMORY, a, perm, 0, 0); } dat->a = a; dat->perm = perm; dat->completion = completion; dat->state = 0; lu_c_data = dat; mode_interruptible = lu_decomp_c_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; } static int lu_decomp_c_worker(int interrupted) { lu_c_data_struct *dat = lu_c_data; phloat *a = dat->a->array->data; int4 n = dat->a->rows; phloat *scale = dat->scale; int4 *perm = dat->perm; int count = 1000; int err; int4 i = dat->i; int4 imax = dat->imax; int4 j = dat->j; int4 k = dat->k; phloat max = dat->max; phloat tmp = dat->tmp; phloat tmp_re = dat->tmp_re; phloat tmp_im = dat->tmp_im; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; phloat xre, xim, yre, yim; phloat tiniest = 1e20 / POS_HUGE_PHLOAT; phloat tiny; phloat s_re, s_im; if (interrupted) { free(scale); err = dat->completion(ERR_INTERRUPTED, dat->a, perm, 0, 0); free(dat); return err; } switch (dat->state) { case 0: break; case 1: goto state1; case 2: goto state2; case 3: goto state3; case 4: goto state4; case 5: goto state5; } dat->det_re = 1; dat->det_im = 0; for (i = 0; i < n; i++) { max = 0; for (j = 0; j < n; j++) { tmp = hypot(a[2 * (i * n + j)], a[2 * (i * n + j) + 1]); if (tmp > max) max = tmp; STATE(1); } scale[i] = max; } for (j = 0; j < n; j++) { for (i = 0; i < j; i++) { sum_re = a[2 * (i * n + j)]; sum_im = a[2 * (i * n + j) + 1]; for (k = 0; k < i; k++) { xre = a[2 * (i * n + k)]; xim = a[2 * (i * n + k) + 1]; yre = a[2 * (k * n + j)]; yim = a[2 * (k * n + j) + 1]; sum_re -= xre * yre - xim * yim; sum_im -= xim * yre + xre * yim; STATE(2); } a[2 * (i * n + j)] = sum_re; a[2 * (i * n + j) + 1] = sum_im; } max = 0; for (i = j; i < n; i++) { sum_re = a[2 * (i * n + j)]; sum_im = a[2 * (i * n + j) + 1]; for (k = 0; k < j; k++) { xre = a[2 * (i * n + k)]; xim = a[2 * (i * n + k) + 1]; yre = a[2 * (k * n + j)]; yim = a[2 * (k * n + j) + 1]; sum_re -= xre * yre - xim * yim; sum_im -= xim * yre + xre * yim; STATE(3); } a[2 * (i * n + j)] = sum_re; a[2 * (i * n + j) + 1] = sum_im; if (scale[i] == 0) { imax = i; break; } tmp = hypot(sum_re, sum_im) / scale[i]; if (tmp > max) { imax = i; max = tmp; } } if (j != imax) { for (k = 0; k < n; k++) { tmp = a[2 * (imax * n + k)]; a[2 * (imax * n + k)] = a[2 * (j * n + k)]; a[2 * (j * n + k)] = tmp; tmp = a[2 * (imax * n + k) + 1]; a[2 * (imax * n + k) + 1] = a[2 * (j * n + k) + 1]; a[2 * (j * n + k) + 1] = tmp; STATE(4); } dat->det_re = -dat->det_re; dat->det_im = -dat->det_im; scale[imax] = scale[j]; } perm[j] = imax; tmp_re = a[2 * (j * n + j)]; tmp_im = a[2 * (j * n + j) + 1]; if (tmp_re == 0 && tmp_im == 0) { if (core_settings.matrix_singularmatrix) { free(scale); err = dat->completion(ERR_NONE, dat->a, perm, 0, 0); free(dat); return err; } else { /* For a zero pivot, substitute a small positive number. * I use a number that's about 10^-20 times the size of * the maximum of the original column, with a minimum of * 10^20 / POS_HUGE_PHLOAT. */ if (scale[j] == 0) tiny = tiniest; else { tiny = pow(10, floor(log10(scale[j])) - 20); if (tiny < tiniest) tiny = tiniest; } a[2 * (j * n + j)] = tmp_re = tiny; a[2 * (j * n + j) + 1] = tmp_im = 0; } } tmp = dat->det_re * tmp_re - dat->det_im * tmp_im; dat->det_im = dat->det_im * tmp_re + dat->det_re * tmp_im; dat->det_re = tmp; if (j != n - 1) { tmp = hypot(tmp_re, tmp_im); s_re = tmp_re / tmp / tmp; s_im = -tmp_im / tmp / tmp; for (i = j + 1; i < n; i++) { tmp_re = a[2 * (i * n + j)]; tmp_im = a[2 * (i * n + j) + 1]; a[2 * (i * n + j)] = tmp_re * s_re - tmp_im * s_im; a[2 * (i * n + j) + 1] = tmp_im * s_re + tmp_re * s_im; STATE(5); } } } free(scale); err = dat->completion(ERR_NONE, dat->a, perm, dat->det_re, dat->det_im); free(dat); return err; suspend: dat->i = i; dat->imax = imax; dat->j = j; dat->k = k; dat->max = max; dat->tmp = tmp; dat->tmp_re = tmp_re; dat->tmp_im = tmp_im; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } /*****************************/ /***** Back-substitution *****/ /*****************************/ typedef struct { vartype_realmatrix *a; int4 *perm; vartype_realmatrix *b; int4 i, ii, j, ll, k; phloat sum; int state; void (*completion)(int, vartype_realmatrix *, int4 *, vartype_realmatrix *); } backsub_rr_data_struct; static backsub_rr_data_struct *backsub_rr_data; static int lu_backsubst_rr_worker(int interrupted); int lu_backsubst_rr(vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b, void (*completion)(int, vartype_realmatrix *, int4 *, vartype_realmatrix *)) { backsub_rr_data_struct *dat = (backsub_rr_data_struct *) malloc(sizeof(backsub_rr_data_struct)); if (dat == NULL) { completion(ERR_INSUFFICIENT_MEMORY, a, perm, b); return ERR_INSUFFICIENT_MEMORY; } dat->a = a; dat->perm = perm; dat->b = b; dat->completion = completion; dat->state = 0; backsub_rr_data = dat; mode_interruptible = lu_backsubst_rr_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; } static int lu_backsubst_rr_worker(int interrupted) { backsub_rr_data_struct *dat = backsub_rr_data; phloat *a = dat->a->array->data; int4 n = dat->a->rows; phloat *b = dat->b->array->data; int4 q = dat->b->columns; int4 *perm = dat->perm; int count = 1000; int4 i = dat->i; int4 ii = dat->ii; int4 j = dat->j; int4 ll = dat->ll; int4 k = dat->k; phloat sum = dat->sum; phloat t; if (interrupted) { dat->completion(ERR_INTERRUPTED, dat->a, perm, dat->b); free(dat); return ERR_INTERRUPTED; } switch (dat->state) { case 0: break; case 1: goto state1; case 2: goto state2; } for (k = 0; k < q; k++) { ii = -1; for (i = 0; i < n; i++) { ll = perm[i]; sum = b[ll * q + k]; b[ll * q + k] = b[i * q + k]; if (ii != -1) { for (j = ii; j < i; j++) { sum -= a[i * n + j] * b[j * q + k]; STATE(1); } } else if (sum != 0) ii = i; b[i * q + k] = sum; } for (i = n - 1; i >= 0; i--) { sum = b[i * q + k]; for (j = i + 1; j < n; j++) { sum -= a[i * n + j] * b[j * q + k]; STATE(2); } t = sum / a[i * n + i]; if (p_isinf(t) || p_isnan(t)) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else t = p_isinf(t) < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } b[i * q + k] = t; } } dat->completion(ERR_NONE, dat->a, perm, dat->b); free(dat); return ERR_NONE; suspend: dat->i = i; dat->ii = ii; dat->j = j; dat->ll = ll; dat->k = k; dat->sum = sum; return ERR_INTERRUPTIBLE; } typedef struct { vartype_realmatrix *a; int4 *perm; vartype_complexmatrix *b; int4 i, ii, j, ll, k; phloat sum_re, sum_im; int state; void (*completion)(int, vartype_realmatrix *, int4 *, vartype_complexmatrix *); } backsub_rc_data_struct; static backsub_rc_data_struct *backsub_rc_data; static int lu_backsubst_rc_worker(int interrupted); int lu_backsubst_rc(vartype_realmatrix *a, int4 *perm, vartype_complexmatrix *b, void (*completion)(int, vartype_realmatrix *, int4 *, vartype_complexmatrix *)) { backsub_rc_data_struct *dat = (backsub_rc_data_struct *) malloc(sizeof(backsub_rc_data_struct)); if (dat == NULL) { completion(ERR_INSUFFICIENT_MEMORY, a, perm, b); return ERR_INSUFFICIENT_MEMORY; } dat->a = a; dat->perm = perm; dat->b = b; dat->completion = completion; dat->state = 0; backsub_rc_data = dat; mode_interruptible = lu_backsubst_rc_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; } static int lu_backsubst_rc_worker(int interrupted) { backsub_rc_data_struct *dat = backsub_rc_data; phloat *a = dat->a->array->data; int4 n = dat->a->rows; phloat *b = dat->b->array->data; int4 q = dat->b->columns; int4 *perm = dat->perm; int count = 1000; int4 i = dat->i; int4 ii = dat->ii; int4 j = dat->j; int4 ll = dat->ll; int4 k = dat->k; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; phloat tmp; phloat t_re, t_im; if (interrupted) { dat->completion(ERR_INTERRUPTED, dat->a, perm, dat->b); free(dat); return ERR_INTERRUPTED; } switch (dat->state) { case 0: break; case 1: goto state1; case 2: goto state2; } for (k = 0; k < q; k++) { ii = -1; for (i = 0; i < n; i++) { ll = perm[i]; sum_re = b[2 * (ll * q + k)]; sum_im = b[2 * (ll * q + k) + 1]; b[2 * (ll * q + k)] = b[2 * (i * q + k)]; b[2 * (ll * q + k) + 1] = b[2 * (i * q + k) + 1]; if (ii != -1) { for (j = ii; j < i; j++) { tmp = a[i * n + j]; sum_re -= tmp * b[2 * (j * q + k)]; sum_im -= tmp * b[2 * (j * q + k) + 1]; STATE(1); } } else if (sum_re != 0 || sum_im != 0) ii = i; b[2 * (i * q + k)] = sum_re; b[2 * (i * q + k) + 1] = sum_im; } for (i = n - 1; i >= 0; i--) { sum_re = b[2 * (i * q + k)]; sum_im = b[2 * (i * q + k) + 1]; for (j = i + 1; j < n; j++) { tmp = a[i * n + j]; sum_re -= tmp * b[2 * (j * q + k)]; sum_im -= tmp * b[2 * (j * q + k) + 1]; STATE(2); } tmp = a[i * n + i]; t_re = sum_re / tmp; t_im = sum_im / tmp; if (p_isinf(t_re) || p_isnan(t_re)) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else t_re = p_isinf(t_re) < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if (p_isinf(t_im) || p_isnan(t_im)) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else t_im = p_isinf(t_im) < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } b[2 * (i * q + k)] = t_re; b[2 * (i * q + k) + 1] = t_im; } } dat->completion(ERR_NONE, dat->a, perm, dat->b); free(dat); return ERR_NONE; suspend: dat->i = i; dat->ii = ii; dat->j = j; dat->ll = ll; dat->k = k; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } typedef struct { vartype_complexmatrix *a; int4 *perm; vartype_complexmatrix *b; int4 i, ii, j, ll, k; phloat sum_re, sum_im; int state; void (*completion)(int, vartype_complexmatrix *, int4 *, vartype_complexmatrix *); } backsub_cc_data_struct; static backsub_cc_data_struct *backsub_cc_data; static int lu_backsubst_cc_worker(int interrupted); int lu_backsubst_cc(vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b, void (*completion)(int, vartype_complexmatrix *, int4 *, vartype_complexmatrix *)) { backsub_cc_data_struct *dat = (backsub_cc_data_struct *) malloc(sizeof(backsub_cc_data_struct)); if (dat == NULL) { completion(ERR_INSUFFICIENT_MEMORY, a, perm, b); return ERR_INSUFFICIENT_MEMORY; } dat->a = a; dat->perm = perm; dat->b = b; dat->completion = completion; dat->state = 0; backsub_cc_data = dat; mode_interruptible = lu_backsubst_cc_worker; mode_stoppable = false; return ERR_INTERRUPTIBLE; } static int lu_backsubst_cc_worker(int interrupted) { backsub_cc_data_struct *dat = backsub_cc_data; phloat *a = dat->a->array->data; int4 n = dat->a->rows; phloat *b = dat->b->array->data; int4 q = dat->b->columns; int4 *perm = dat->perm; int count = 1000; int4 i = dat->i; int4 ii = dat->ii; int4 j = dat->j; int4 ll = dat->ll; int4 k = dat->k; phloat sum_re = dat->sum_re; phloat sum_im = dat->sum_im; phloat tmp, tmp_re, tmp_im; phloat bre, bim; phloat t_re, t_im; if (interrupted) { dat->completion(ERR_INTERRUPTED, dat->a, perm, dat->b); free(dat); return ERR_INTERRUPTED; } switch (dat->state) { case 0: break; case 1: goto state1; case 2: goto state2; } for (k = 0; k < q; k++) { ii = -1; for (i = 0; i < n; i++) { ll = perm[i]; sum_re = b[2 * (ll * q + k)]; sum_im = b[2 * (ll * q + k) + 1]; b[2 * (ll * q + k)] = b[2 * (i * q + k)]; b[2 * (ll * q + k) + 1] = b[2 * (i * q + k) + 1]; if (ii != -1) { for (j = ii; j < i; j++) { bre = b[2 * (j * q + k)]; bim = b[2 * (j * q + k) + 1]; tmp_re = a[2 * (i * n + j)]; tmp_im = a[2 * (i * n + j) + 1]; sum_re -= bre * tmp_re - bim * tmp_im; sum_im -= bim * tmp_re + bre * tmp_im; STATE(1); } } else if (sum_re != 0 || sum_im != 0) ii = i; b[2 * (i * q + k)] = sum_re; b[2 * (i * q + k) + 1] = sum_im; } for (i = n - 1; i >= 0; i--) { sum_re = b[2 * (i * q + k)]; sum_im = b[2 * (i * q + k) + 1]; for (j = i + 1; j < n; j++) { bre = b[2 * (j * q + k)]; bim = b[2 * (j * q + k) + 1]; tmp_re = a[2 * (i * n + j)]; tmp_im = a[2 * (i * n + j) + 1]; sum_re -= bre * tmp_re - bim * tmp_im; sum_im -= bim * tmp_re + bre * tmp_im; STATE(2); } tmp_re = a[2 * (i * n + i)]; tmp_im = a[2 * (i * n + i) + 1]; tmp = hypot(tmp_re, tmp_im); tmp_re = tmp_re / tmp / tmp; tmp_im = -tmp_im / tmp / tmp; t_re = sum_re * tmp_re - sum_im * tmp_im; t_im = sum_im * tmp_re + sum_re * tmp_im; if (p_isinf(t_re) || p_isnan(t_re)) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else t_re = p_isinf(t_re) < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } if (p_isinf(t_im) || p_isnan(t_im)) { if (core_settings.matrix_outofrange && !flags.f.range_error_ignore) return ERR_OUT_OF_RANGE; else t_im = p_isinf(t_im) < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; } b[2 * (i * q + k)] = t_re; b[2 * (i * q + k) + 1] = t_im; } } dat->completion(ERR_NONE, dat->a, perm, dat->b); free(dat); return ERR_NONE; suspend: dat->i = i; dat->ii = ii; dat->j = j; dat->ll = ll; dat->k = k; dat->sum_re = sum_re; dat->sum_im = sum_im; return ERR_INTERRUPTIBLE; } free42-nologo-1.4.77/common/core_linalg2.h000644 000765 000024 00000003333 12110237247 020634 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_LINALG2_H #define CORE_LINALG2_H 1 #include "core_globals.h" int lu_decomp_r(vartype_realmatrix *a, int4 *perm, int (*completion)(int, vartype_realmatrix *, int4 *, phloat)); int lu_decomp_c(vartype_complexmatrix *a, int4 *perm, int (*completion)(int, vartype_complexmatrix *, int4 *, phloat, phloat)); int lu_backsubst_rr(vartype_realmatrix *a, int4 *perm, vartype_realmatrix *b, void (*completion)(int, vartype_realmatrix *, int4 *, vartype_realmatrix *)); int lu_backsubst_rc(vartype_realmatrix *a, int4 *perm, vartype_complexmatrix *b, void (*completion)(int, vartype_realmatrix *, int4 *, vartype_complexmatrix *)); int lu_backsubst_cc(vartype_complexmatrix *a, int4 *perm, vartype_complexmatrix *b, void (*completion)(int, vartype_complexmatrix *, int4 *, vartype_complexmatrix *)); #endif free42-nologo-1.4.77/common/core_main.cc000644 000765 000024 00000220352 12110237247 020370 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_main.h" #include "core_commands2.h" #include "core_commands4.h" #include "core_display.h" #include "core_helpers.h" #include "core_keydown.h" #include "core_math1.h" #include "core_sto_rcl.h" #include "core_tables.h" #include "core_variables.h" #include "shell.h" #include "shell_spool.h" static void set_shift(bool state) { if (mode_shift != state) { mode_shift = state; shell_annunciators(-1, state, -1, -1, -1, -1); } } static void continue_running(); static void stop_interruptible(); static int handle_error(int error); int repeating = 0; int repeating_shift; int repeating_key; static int4 oldpc; core_settings_struct core_settings; void core_init(int read_saved_state, int4 version) { /* Possible values for read_saved_state: * 0: state file not present (Memory Clear) * 1: state file present and looks OK so far * 2: state file present but not OK (State File Corrupt) */ phloat_init(); if (read_saved_state != 1 || !load_state(version)) hard_reset(read_saved_state != 0); repaint_display(); shell_annunciators(mode_updown, mode_shift, 0 /*print*/, mode_running, flags.f.grad, flags.f.rad || flags.f.grad); } #if defined(IPHONE) || defined(ANDROID) void core_enter_background() { if (mode_interruptible != NULL) stop_interruptible(); set_running(false); save_state(); } #endif void core_quit() { #ifndef ANDROID // In Android, core_enter_background() is always called // before core_quit(). // TODO: Does that apply to the iPhone verson as well? if (mode_interruptible != NULL) stop_interruptible(); save_state(); #endif free_vartype(reg_x); free_vartype(reg_y); free_vartype(reg_z); free_vartype(reg_t); free_vartype(reg_lastx); purge_all_vars(); clear_all_prgms(); if (vars != NULL) free(vars); clean_vartype_pools(); phloat_cleanup(); #ifdef ANDROID reinitialize_globals(); #endif } void core_repaint_display() { repaint_display(); } int core_menu() { return mode_clall || get_front_menu() != NULL; } int core_alpha_menu() { int *menu = get_front_menu(); return menu != NULL && *menu >= MENU_ALPHA1 && *menu <= MENU_ALPHA_MISC2; } int core_hex_menu() { int *menu = get_front_menu(); return menu != NULL && *menu == MENU_BASE_A_THRU_F; } int core_keydown(int key, int *enqueued, int *repeat) { *enqueued = 0; *repeat = 0; if (key != 0) no_keystrokes_yet = false; if (key == KEY_SHIFT) { set_shift(!mode_shift); return (mode_running && !mode_getkey && !mode_pause) || keybuf_head != keybuf_tail; } if (mode_pause) { mode_pause = false; set_running(false); if (!mode_shift && (key == KEY_RUN || key == KEY_EXIT)) { redisplay(); return 0; } } if (mode_interruptible != NULL) { /* We're in the middle of an interruptible function * (e.g., INVRT, PRP); queue up any keystrokes and invoke * the appropriate callback to keep the funtion moving along */ int error, keep_running; if (key != 0) { /* Enqueue... */ *enqueued = 1; if (key == KEY_EXIT || (mode_stoppable && !mode_shift && key == KEY_RUN)) { keybuf_tail = keybuf_head; stop_interruptible(); return 0; } else { if (((keybuf_head + 1) & 15) != keybuf_tail) { if (mode_shift) key = -key; keybuf[keybuf_head] = key; keybuf_head = (keybuf_head + 1) & 15; } } set_shift(false); } error = mode_interruptible(0); if (error == ERR_INTERRUPTIBLE) /* Still not done */ return 1; mode_interruptible = NULL; keep_running = handle_error(error); if (mode_running) { if (!keep_running) set_running(false); } else { shell_annunciators(-1, -1, -1, 0, -1, -1); pending_command = CMD_NONE; } if (mode_running || keybuf_tail != keybuf_head) return 1; else { redisplay(); return 0; } } if (mode_running && !mode_getkey) { /* We're running; queue up any keystrokes and invoke * continue_running() to keep the program moving along */ if (key != 0) { if (key == KEY_EXIT) { keybuf_tail = keybuf_head; set_shift(false); set_running(false); pending_command = CMD_CANCELLED; return 0; } /* Enqueue... */ *enqueued = 1; if (!mode_shift && key == KEY_RUN) { keybuf_tail = keybuf_head; set_running(false); redisplay(); return 0; } if (((keybuf_head + 1) & 15) != keybuf_tail) { if (mode_shift) key = -key; keybuf[keybuf_head] = key; keybuf_head = (keybuf_head + 1) & 15; } set_shift(false); } continue_running(); if ((mode_running && !mode_getkey && !mode_pause) || keybuf_tail != keybuf_head) return 1; else { if (mode_getkey) /* Technically, the program is still running, but we turn * off the 'running' annunciator so that the user has some * cue that they may now type. (Actually, I'm doing it * purely because the HP-42S does it, too!) */ shell_annunciators(-1, -1, -1, 0, -1, -1); else if (!mode_pause) redisplay(); return 0; } } /* If we get here, mode_running must be false. * or a program is running but hanging in GETKEY; */ if (keybuf_tail != keybuf_head) { /* We're not running, or a program is waiting in GETKEY; * feed queued-up keystroke to keydown() */ int oldshift = 0; int oldkey = keybuf[keybuf_tail]; if (oldkey < 0) { oldkey = -oldkey; oldshift = 1; } keybuf_tail = (keybuf_tail + 1) & 15; /* If we're in GETKEY mode, the 'running' annunciator is off; * see the code circa 30 lines back. * We now turn it back on since program execution resumes. */ if (mode_getkey && mode_running) shell_annunciators(-1, -1, -1, 1, -1, -1); /* Feed the dequeued key to the usual suspects */ keydown(oldshift, oldkey); core_keyup(); /* We've just de-queued a key; may have to enqueue * one as well, if the user is actually managing to * type while we're unwinding the keyboard buffer */ if (key != 0) { if (((keybuf_head + 1) & 15) != keybuf_tail) { if (mode_shift) key = -key; keybuf[keybuf_head] = key; keybuf_head = (keybuf_head + 1) & 15; } set_shift(false); } return (mode_running && !mode_getkey) || keybuf_head != keybuf_tail; } /* No program is running, or it is running but waiting for a * keystroke (GETKEY); handle any new keystroke that has just come in */ if (key != 0) { int shift = mode_shift; set_shift(false); if (mode_getkey && mode_running) shell_annunciators(-1, -1, -1, 1, -1, -1); keydown(shift, key); if (repeating != 0) { *repeat = repeating; repeating = 0; } return mode_running && !mode_getkey; } /* Nothing going on at all! */ return 0; } int core_repeat() { keydown(repeating_shift, repeating_key); int rpt = repeating; repeating = 0; return rpt; } void core_keytimeout1() { if (pending_command == CMD_LINGER1 || pending_command == CMD_LINGER2) return; if (pending_command == CMD_RUN || pending_command == CMD_SST) { int saved_pending_command = pending_command; if (pc == -1) pc = 0; prgm_highlight_row = 1; flags.f.prgm_mode = 2; /* HACK - magic value to tell redisplay() */ /* not to suppress option menu highlights */ pending_command = CMD_NONE; redisplay(); flags.f.prgm_mode = 0; pending_command = saved_pending_command; } else if (pending_command != CMD_NONE && pending_command != CMD_CANCELLED && (cmdlist(pending_command)->flags & FLAG_NO_SHOW) == 0) { display_command(0); /* If the program catalog was left up by GTO or XEQ, * don't paint over it */ if (mode_transientmenu == MENU_NONE || pending_command == CMD_NULL) display_x(1); flush_display(); } } void core_keytimeout2() { if (pending_command == CMD_LINGER1 || pending_command == CMD_LINGER2) return; remove_program_catalog = 0; if (pending_command != CMD_NONE && pending_command != CMD_CANCELLED && (cmdlist(pending_command)->flags & FLAG_NO_SHOW) == 0) { clear_row(0); draw_string(0, 0, "NULL", 4); display_x(1); flush_display(); pending_command = CMD_CANCELLED; } } bool core_timeout3(int repaint) { if (mode_pause) { if (repaint) { /* The PSE ended normally */ mode_pause = false; if (mode_goose >= 0) mode_goose = -1 - mode_goose; } return true; } /* Remove the output of SHOW, MEM, or shift-VARMENU from the display */ if (pending_command == CMD_LINGER1) pending_command = CMD_CANCELLED; else if (pending_command == CMD_LINGER2) { flags.f.message = 0; flags.f.two_line_message = 0; pending_command = CMD_NONE; if (repaint) redisplay(); } return false; } int core_keyup() { if (mode_pause) { /* The only way this can happen is if they key in question was Shift */ return 0; } int error = ERR_NONE; if (pending_command == CMD_LINGER1 || pending_command == CMD_LINGER2) { pending_command = CMD_LINGER2; return mode_running || keybuf_head != keybuf_tail; } if (pending_command == CMD_SILENT_OFF) { #ifdef IPHONE if (off_enabled()) shell_powerdown(); else { set_running(false); squeak(); } #else shell_powerdown(); #endif pending_command = CMD_NONE; return 0; } if (pending_command == CMD_NONE) return mode_running || keybuf_head != keybuf_tail; if (remove_program_catalog) { if (mode_transientmenu == MENU_CATALOG) set_menu(MENULEVEL_TRANSIENT, MENU_NONE); else if (mode_plainmenu == MENU_CATALOG) set_menu(MENULEVEL_PLAIN, MENU_NONE); remove_program_catalog = 0; } if (pending_command == CMD_CANCELLED || pending_command == CMD_NULL) { pending_command = CMD_NONE; redisplay(); return mode_running || keybuf_head != keybuf_tail; } mode_varmenu = pending_command == CMD_VMSTO || pending_command == CMD_VMSTO2 || pending_command == CMD_VMSOLVE || pending_command == CMD_VMEXEC; if (input_length > 0) { /* INPUT active */ if (pending_command == CMD_RUN || pending_command == CMD_SST) { int err = generic_sto(&input_arg, 0); if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) { char lbuf[12], rbuf[100]; int llen, rlen; string_copy(lbuf, &llen, input_name, input_length); lbuf[llen++] = '='; rlen = vartype2string(reg_x, rbuf, 100); print_wide(lbuf, llen, rbuf, rlen); } input_length = 0; if (err != ERR_NONE) { pending_command = CMD_NONE; display_error(err, 1); redisplay(); return mode_running || keybuf_head != keybuf_tail; } } else if (pending_command == CMD_GTO || pending_command == CMD_GTODOT || pending_command == CMD_GTODOTDOT || pending_command == CMD_RTN) /* NOTE: set_running(true) also ends INPUT mode, so commands that * cause program execution to start do not have to be handled here. */ input_length = 0; } if (pending_command == CMD_VMEXEC) { string_copy(reg_alpha, ®_alpha_length, pending_command_arg.val.text, pending_command_arg.length); goto do_run; } if (pending_command == CMD_RUN) { do_run: if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) print_command(pending_command, &pending_command_arg); pending_command = CMD_NONE; if (pc == -1) pc = 0; set_running(true); return 1; } if (pending_command == CMD_SST) { int cmd; arg_struct arg; oldpc = pc; if (pc == -1) pc = 0; get_next_command(&pc, &cmd, &arg, 1); if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) print_program_line(current_prgm, oldpc); mode_disable_stack_lift = false; set_running(true); error = cmdlist(cmd)->handler(&arg); set_running(false); mode_pause = false; } else { if ((flags.f.trace_print || flags.f.normal_print) && flags.f.printer_exists) print_command(pending_command, &pending_command_arg); mode_disable_stack_lift = false; error = cmdlist(pending_command)->handler(&pending_command_arg); mode_pause = false; } if (error == ERR_INTERRUPTIBLE) { shell_annunciators(-1, -1, -1, 1, -1, -1); return 1; } handle_error(error); pending_command = CMD_NONE; if (!mode_getkey && !mode_pause) redisplay(); return (mode_running && !mode_getkey && !mode_pause) || keybuf_head != keybuf_tail; } int core_allows_powerdown(int *want_cpu) { int allow = shell_low_battery() || !(mode_running || mode_getkey || flags.f.continuous_on || mode_interruptible != NULL); *want_cpu = 0; if (!allow && mode_getkey) { /* We're being asked to power down but we're refusing. * If this happens in the middle of a GETKEY, it should return 70, * the code for OFF, but without stopping program execution. */ vartype *seventy = new_real(70); if (seventy != NULL) { recall_result(seventy); flags.f.stack_lift_disable = 0; if (mode_running) { shell_annunciators(-1, -1, -1, 1, -1, -1); *want_cpu = 1; } else redisplay(); } else { /* Memory allocation failure... The program will stop now, * anyway, so we change our mind and allow the powerdown. */ display_error(ERR_INSUFFICIENT_MEMORY, 1); set_running(false); redisplay(); allow = 1; } mode_getkey = 0; } return allow; } int core_powercycle() { bool need_redisplay = false; if (mode_interruptible != NULL) stop_interruptible(); no_keystrokes_yet = true; keybuf_tail = keybuf_head; set_shift(false); flags.f.continuous_on = 0; pending_command = CMD_NONE; if (mode_getkey) { /* A real HP-42S can't be switched off while GETKEY is active: pressing * OFF on the keyboard returns code 70 and stops program execution; and * when the auto-poweroff timeout expires, code 70 is returned but * program execution continues. * Since Free42 can be shut down in ways the HP-42S can't (exiting the * application, or turning off power on a Palm), I have to fake it a * bit; I put 70 in X as if the user had done OFF twice on a real 42S. * Note that, as on a real 42S, we don't allow auto-powerdown while * GETKEY is waiting; if an auto-powerdown request happens during * GETKEY, it returns 70 but program execution is not stopped, and the * power stays on (see core_allows_powerdown(), above). */ vartype *seventy = new_real(70); if (seventy != NULL) { recall_result(seventy); flags.f.stack_lift_disable = 0; } else { display_error(ERR_INSUFFICIENT_MEMORY, 1); flags.f.auto_exec = 0; } if (!flags.f.auto_exec) need_redisplay = true; mode_getkey = false; } if (flags.f.auto_exec) { if (mode_command_entry) finish_command_entry(false); if (flags.f.prgm_mode) { if (mode_alpha_entry) { pc = incomplete_saved_pc; prgm_highlight_row = incomplete_saved_highlight_row; } else if (mode_number_entry) { arg_struct arg; arg.type = ARGTYPE_DOUBLE; arg.val_d = entered_number; store_command(pc, CMD_NUMBER, &arg); prgm_highlight_row = 1; } flags.f.prgm_mode = false; } mode_alpha_entry = false; mode_number_entry = false; set_menu(MENULEVEL_ALPHA, MENU_NONE); flags.f.alpha_mode = 0; set_running(true); flags.f.auto_exec = 0; need_redisplay = false; } else { if (mode_running) { set_running(false); need_redisplay = true; } } if (need_redisplay || bin_dec_mode_switch || state_file_has_old_bcd) { bin_dec_mode_switch = false; state_file_has_old_bcd = false; redisplay(); } return mode_running; } int core_list_programs(char *buf, int bufsize) { int lastidx = -1; int bufptr = 0; int label; int count = 0; for (label = 0; label < labels_count; label++) { int len = labels[label].length; char name[51]; int namelen = 0; int end = 0; int i; if (len == 0) { if (labels[label].prgm == lastidx) continue; if (label == labels_count - 1) { string2buf(name, 21, &namelen, ".END.", 5); namelen = 5; } else { string2buf(name, 21, &namelen, "END", 3); namelen = 3; } end = 1; } else { name[namelen++] = '"'; namelen += hp2ascii(name + namelen, labels[label].name, len); name[namelen++] = '"'; end = labels[label + 1].length == 0; } lastidx = labels[label].prgm; if (bufptr + namelen + 1 >= bufsize) { if (bufptr > 0 && buf[bufptr - 1] != 0) { buf[bufptr - 1] = 0; count++; } return count; } for (i = 0; i < namelen; i++) buf[bufptr++] = name[i]; if (end) { buf[bufptr++] = 0; count++; } else { buf[bufptr++] = ' '; } } return count; } static int export_hp42s(int index, int (*progress_report)(const char *)) { int4 pc = 0; int cmd; arg_struct arg; int saved_prgm = current_prgm; uint4 hp42s_code; unsigned char code_flags, code_name, code_std_1, code_std_2; char cmdbuf[25]; int cmdlen; char buf[1000]; int buflen = 0; int i; int cancel = 0; current_prgm = index; do { get_next_command(&pc, &cmd, &arg, 0); hp42s_code = cmdlist(cmd)->hp42s_code; code_flags = hp42s_code >> 24; code_name = hp42s_code >> 16; code_std_1 = hp42s_code >> 8; code_std_2 = hp42s_code; cmdlen = 0; switch (code_flags) { case 1: /* A command that requires some special attention */ if (cmd == CMD_STO) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 15) cmdbuf[cmdlen++] = 0x30 + arg.val.num; else goto normal; } else if (cmd == CMD_RCL) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 15) cmdbuf[cmdlen++] = 0x20 + arg.val.num; else goto normal; } else if (cmd == CMD_FIX || cmd == CMD_SCI || cmd == CMD_ENG) { char byte2; if (arg.type != ARGTYPE_NUM || arg.val.num <= 9) goto normal; cmdbuf[cmdlen++] = (char) 0xF1; if (arg.val.num == 10) { switch (cmd) { case CMD_FIX: byte2 = (char) 0xD5; break; case CMD_SCI: byte2 = (char) 0xD6; break; case CMD_ENG: byte2 = (char) 0xD7; break; } } else { switch (cmd) { case CMD_FIX: byte2 = (char) 0xE5; break; case CMD_SCI: byte2 = (char) 0xE6; break; case CMD_ENG: byte2 = (char) 0xE7; break; } } cmdbuf[cmdlen++] = byte2; } else if (cmd == CMD_SIZE) { cmdbuf[cmdlen++] = (char) 0xF3; cmdbuf[cmdlen++] = (char) 0xF7; cmdbuf[cmdlen++] = arg.val.num >> 8; cmdbuf[cmdlen++] = arg.val.num; } else if (cmd == CMD_LBL) { if (arg.type == ARGTYPE_NUM) { if (arg.val.num <= 14) cmdbuf[cmdlen++] = 0x01 + arg.val.num; else goto normal; } else if (arg.type == ARGTYPE_STR) { if (progress_report != NULL) { char s[52]; int sl = hp2ascii(s + 1, arg.val.text, arg.length); s[0] = s[sl + 1] = '"'; s[sl + 2] = 0; if (progress_report(s)) { cancel = 1; goto done; } } cmdbuf[cmdlen++] = (char) 0xC0; cmdbuf[cmdlen++] = 0x00; cmdbuf[cmdlen++] = 0xF1 + arg.length; cmdbuf[cmdlen++] = 0x00; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else goto normal; } else if (cmd == CMD_INPUT) { if (arg.type == ARGTYPE_IND_NUM || arg.type == ARGTYPE_IND_STK) code_std_2 = 0xEE; goto normal; } else if (cmd == CMD_XEQ) { if (arg.type == ARGTYPE_NUM || arg.type == ARGTYPE_LCLBL) { code_std_1 = 0xE0; code_std_2 = 0x00; goto normal; } else if (arg.type == ARGTYPE_STR) { cmdbuf[cmdlen++] = 0x1E; cmdbuf[cmdlen++] = 0xF0 + arg.length; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else goto normal; } else if (cmd == CMD_GTO) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 14) { cmdbuf[cmdlen++] = 0xB1 + arg.val.num; cmdbuf[cmdlen++] = 0x00; } else if (arg.type == ARGTYPE_NUM || arg.type == ARGTYPE_LCLBL) { code_std_1 = 0xD0; code_std_2 = 0x00; goto normal; } else if (arg.type == ARGTYPE_IND_NUM || arg.type == ARGTYPE_IND_STK) { cmdbuf[cmdlen++] = (char) 0xAE; if (arg.type == ARGTYPE_IND_NUM) arg.type = ARGTYPE_NUM; else arg.type = ARGTYPE_STK; goto non_string_suffix; } else if (arg.type == ARGTYPE_STR) { cmdbuf[cmdlen++] = 0x1D; cmdbuf[cmdlen++] = 0xF0 + arg.length; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else goto normal; } else if (cmd == CMD_END) { if (progress_report != NULL && progress_report("END")) { cancel = 1; goto done; } cmdbuf[cmdlen++] = (char) 0xC0; cmdbuf[cmdlen++] = 0x00; cmdbuf[cmdlen++] = 0x0D; } else if (cmd == CMD_NUMBER) { char *p = phloat2program(arg.val_d); char dot = flags.f.decimal_point ? '.' : ','; char c; while ((c = *p++) != 0) { if (c >= '0' && c <= '9') cmdbuf[cmdlen++] = 0x10 + c - '0'; else if (c == dot) cmdbuf[cmdlen++] = 0x1A; else if (c == 24) cmdbuf[cmdlen++] = 0x1B; else if (c == '-') cmdbuf[cmdlen++] = 0x1C; else /* Should not happen */ continue; } cmdbuf[cmdlen++] = 0x00; } else if (cmd == CMD_STRING) { cmdbuf[cmdlen++] = 0xF0 + arg.length; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else if (cmd >= CMD_ASGN01 && cmd <= CMD_ASGN18) { if (arg.type == ARGTYPE_STR) { cmdbuf[cmdlen++] = 0xF2 + arg.length; cmdbuf[cmdlen++] = (char) 0xC0; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else { /* arg.type == ARGTYPE_COMMAND; we don't use that * any more, but just to be safe (in case anyone ever * actually used this in a program), we handle it * anyway. */ const command_spec *cs = cmdlist(arg.val.cmd); cmdbuf[cmdlen++] = 0xF2 + cs->name_length; cmdbuf[cmdlen++] = (char) 0xC0; for (i = 0; i < cs->name_length; i++) cmdbuf[cmdlen++] = cs->name[i]; } cmdbuf[cmdlen++] = cmd - CMD_ASGN01; } else if ((cmd >= CMD_KEY1G && cmd <= CMD_KEY9G) || (cmd >= CMD_KEY1X && cmd <= CMD_KEY9X)) { int keyg = cmd <= CMD_KEY9G; int keynum = cmd - (keyg ? CMD_KEY1G : CMD_KEY1X) + 1; if (arg.type == ARGTYPE_STR || arg.type == ARGTYPE_IND_STR){ cmdbuf[cmdlen++] = 0xF2 + arg.length; cmdbuf[cmdlen++] = keyg ? 0xC3 : 0xC2; if (arg.type == ARGTYPE_IND_STR) cmdbuf[cmdlen - 1] += 8; cmdbuf[cmdlen++] = keynum; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else { cmdbuf[cmdlen++] = (char) 0xF3; cmdbuf[cmdlen++] = keyg ? 0xE3 : 0xE2; cmdbuf[cmdlen++] = keynum; goto non_string_suffix; } } else if (cmd == CMD_XROM) { cmdbuf[cmdlen++] = (char) (0xA0 + ((arg.val.num >> 8) & 7)); cmdbuf[cmdlen++] = (char) arg.val.num; } else { /* Shouldn't happen */ continue; } break; case 0: normal: if (arg.type == ARGTYPE_STR || arg.type == ARGTYPE_IND_STR) { int i; cmdbuf[cmdlen++] = 0xF0 + arg.length + 1; cmdbuf[cmdlen++] = arg.type == ARGTYPE_STR ? code_name : code_name + 8; for (i = 0; i < arg.length; i++) cmdbuf[cmdlen++] = arg.val.text[i]; } else { unsigned char suffix; if (code_std_1 != 0) cmdbuf[cmdlen++] = code_std_1; cmdbuf[cmdlen++] = code_std_2; non_string_suffix: suffix = 0; switch (arg.type) { case ARGTYPE_NONE: goto no_suffix; case ARGTYPE_IND_NUM: suffix = 0x80; case ARGTYPE_NUM: suffix += arg.val.num; break; case ARGTYPE_IND_STK: suffix = 0x80; case ARGTYPE_STK: switch (arg.val.stk) { case 'X': suffix += 0x73; break; case 'Y': suffix += 0x72; break; case 'Z': suffix += 0x71; break; case 'T': suffix += 0x70; break; case 'L': suffix += 0x74; break; default: /* Shouldn't happen */ continue; } break; case ARGTYPE_LCLBL: if (arg.val.lclbl >= 'A' && arg.val.lclbl <= 'J') suffix = arg.val.lclbl - 'A' + 0x66; else if (arg.val.lclbl >= 'a' && arg.val.lclbl <= 'e') suffix = arg.val.lclbl - 'a' + 0x7B; else /* Shouldn't happen */ continue; break; default: /* Shouldn't happen */ /* Values not handled above are ARGTYPE_NEG_NUM, * which is converted to ARGTYPE_NUM by * get_next_command(); ARGTYPE_DOUBLE, which only * occurs with CMD_NUMBER, which is handled in the * special-case section, above; ARGTYPE_COMMAND, * which is handled in the special-case section; * and ARGTYPE_LBLINDEX, which is converted to * ARGTYPE_STR before being stored in a program. */ continue; } cmdbuf[cmdlen++] = suffix; no_suffix: ; } break; case 2: default: /* Illegal command */ continue; } if (buflen + cmdlen > 1000) { if (!shell_write(buf, buflen)) goto done; buflen = 0; } for (i = 0; i < cmdlen; i++) buf[buflen++] = cmdbuf[i]; } while (cmd != CMD_END && pc < prgms[index].size); if (buflen > 0) shell_write(buf, buflen); done: current_prgm = saved_prgm; return cancel; } int4 core_program_size(int prgm_index) { int4 pc = 0; int cmd; arg_struct arg; int saved_prgm = current_prgm; uint4 hp42s_code; unsigned char code_flags, code_name, code_std_1, code_std_2; int4 size = 0; current_prgm = prgm_index; do { get_next_command(&pc, &cmd, &arg, 0); hp42s_code = cmdlist(cmd)->hp42s_code; code_flags = hp42s_code >> 24; code_name = hp42s_code >> 16; code_std_1 = hp42s_code >> 8; code_std_2 = hp42s_code; switch (code_flags) { case 1: /* A command that requires some special attention */ if (cmd == CMD_STO) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 15) size += 1; else goto normal; } else if (cmd == CMD_RCL) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 15) size += 1; else goto normal; } else if (cmd == CMD_FIX || cmd == CMD_SCI || cmd == CMD_ENG) { goto normal; } else if (cmd == CMD_SIZE) { size += 4; } else if (cmd == CMD_LBL) { if (arg.type == ARGTYPE_NUM) { if (arg.val.num <= 14) size += 1; else goto normal; } else if (arg.type == ARGTYPE_STR) { size += arg.length + 4; } else goto normal; } else if (cmd == CMD_INPUT) { goto normal; } else if (cmd == CMD_XEQ) { if (arg.type == ARGTYPE_NUM || arg.type == ARGTYPE_LCLBL) { size += 3; } else if (arg.type == ARGTYPE_STR) { size += arg.length + 2; } else goto normal; } else if (cmd == CMD_GTO) { if (arg.type == ARGTYPE_NUM && arg.val.num <= 14) { size += 2; } else if (arg.type == ARGTYPE_NUM || arg.type == ARGTYPE_LCLBL) { size += 3; } else if (arg.type == ARGTYPE_STR) { size += arg.length + 2; } else goto normal; } else if (cmd == CMD_END) { /* Not counted for the line 00 total */ } else if (cmd == CMD_NUMBER) { char *p = phloat2program(arg.val_d); while (*p++ != 0) size += 1; size += 1; } else if (cmd == CMD_STRING) { size += arg.length + 1; } else if (cmd >= CMD_ASGN01 && cmd <= CMD_ASGN18) { if (arg.type == ARGTYPE_STR) size += arg.length + 3; else /* arg.type == ARGTYPE_COMMAND; we don't use that * any more, but just to be safe (in case anyone ever * actually used this in a program), we handle it * anyway. */ size += cmdlist(arg.val.cmd)->name_length + 3; } else if ((cmd >= CMD_KEY1G && cmd <= CMD_KEY9G) || (cmd >= CMD_KEY1X && cmd <= CMD_KEY9X)) { if (arg.type == ARGTYPE_STR || arg.type == ARGTYPE_IND_STR) size += arg.length + 3; else size += 4; } else if (cmd == CMD_XROM) { size += 2; } else { /* Shouldn't happen */ continue; } break; case 0: normal: if (arg.type == ARGTYPE_STR || arg.type == ARGTYPE_IND_STR) { size += arg.length + 2; } else { size += code_std_1 == 0 ? 1 : 2; if (arg.type != ARGTYPE_NONE) size += 1; } break; case 2: default: /* Illegal command */ continue; } } while (cmd != CMD_END && pc < prgms[prgm_index].size); current_prgm = saved_prgm; return size; } int core_export_programs(int count, const int *indexes, int (*progress_report)(const char *)) { int i; for (i = 0; i < count; i++) { int p = indexes[i]; if (export_hp42s(p, progress_report)) return 1; } return 0; } static int hp42tofree42[] = { /* Flag values: 0 = simple 1-byte command; 1 = 1-byte command with * embedded argument; 2 = 2-byte command, argument follows; * 3 = everything else, special case handling required. */ /* 00-0F */ /* NULL, LBL 00-14 */ CMD_NULL | 0x3000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, CMD_LBL | 0x1000, /* 10-1F */ /* 0-9, ., E, -, GTO "", XEQ "", W "" */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, /* 20-2F */ /* RCL 00-15 */ CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, CMD_RCL | 0x1000, /* 30-3F */ /* STO 00-15 */ CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, CMD_STO | 0x1000, /* 40-4F */ CMD_ADD | 0x0000, CMD_SUB | 0x0000, CMD_MUL | 0x0000, CMD_DIV | 0x0000, CMD_X_LT_Y | 0x0000, CMD_X_GT_Y | 0x0000, CMD_X_LE_Y | 0x0000, CMD_SIGMAADD | 0x0000, CMD_SIGMASUB | 0x0000, CMD_HMSADD | 0x0000, CMD_HMSSUB | 0x0000, CMD_MOD | 0x0000, CMD_PERCENT | 0x0000, CMD_PERCENT_CH | 0x0000, CMD_TO_REC | 0x0000, CMD_TO_POL | 0x0000, /* 50-5F */ CMD_LN | 0x0000, CMD_SQUARE | 0x0000, CMD_SQRT | 0x0000, CMD_Y_POW_X | 0x0000, CMD_CHS | 0x0000, CMD_E_POW_X | 0x0000, CMD_LOG | 0x0000, CMD_10_POW_X | 0x0000, CMD_E_POW_X_1 | 0x0000, CMD_SIN | 0x0000, CMD_COS | 0x0000, CMD_TAN | 0x0000, CMD_ASIN | 0x0000, CMD_ACOS | 0x0000, CMD_ATAN | 0x0000, CMD_TO_DEC | 0x0000, /* 60-6F */ CMD_INV | 0x0000, CMD_ABS | 0x0000, CMD_FACT | 0x0000, CMD_X_NE_0 | 0x0000, CMD_X_GT_0 | 0x0000, CMD_LN_1_X | 0x0000, CMD_X_LT_0 | 0x0000, CMD_X_EQ_0 | 0x0000, CMD_IP | 0x0000, CMD_FP | 0x0000, CMD_TO_RAD | 0x0000, CMD_TO_DEG | 0x0000, CMD_TO_HMS | 0x0000, CMD_TO_HR | 0x0000, CMD_RND | 0x0000, CMD_TO_OCT | 0x0000, /* 70-7F */ CMD_CLSIGMA | 0x0000, CMD_SWAP | 0x0000, CMD_PI | 0x0000, CMD_CLST | 0x0000, CMD_RUP | 0x0000, CMD_RDN | 0x0000, CMD_LASTX | 0x0000, CMD_CLX | 0x0000, CMD_X_EQ_Y | 0x0000, CMD_X_NE_Y | 0x0000, CMD_SIGN | 0x0000, CMD_X_LE_0 | 0x0000, CMD_MEAN | 0x0000, CMD_SDEV | 0x0000, CMD_AVIEW | 0x0000, CMD_CLD | 0x0000, /* 80-8F */ CMD_DEG | 0x0000, CMD_RAD | 0x0000, CMD_GRAD | 0x0000, CMD_ENTER | 0x0000, CMD_STOP | 0x0000, CMD_RTN | 0x0000, CMD_BEEP | 0x0000, CMD_CLA | 0x0000, CMD_ASHF | 0x0000, CMD_PSE | 0x0000, CMD_CLRG | 0x0000, CMD_AOFF | 0x0000, CMD_AON | 0x0000, CMD_OFF | 0x0000, CMD_PROMPT | 0x0000, CMD_ADV | 0x0000, /* 90-9F */ CMD_RCL | 0x2000, CMD_STO | 0x2000, CMD_STO_ADD | 0x2000, CMD_STO_SUB | 0x2000, CMD_STO_MUL | 0x2000, CMD_STO_DIV | 0x2000, CMD_ISG | 0x2000, CMD_DSE | 0x2000, CMD_VIEW | 0x2000, CMD_SIGMAREG | 0x2000, CMD_ASTO | 0x2000, CMD_ARCL | 0x2000, CMD_FIX | 0x2000, CMD_SCI | 0x2000, CMD_ENG | 0x2000, CMD_TONE | 0x2000, /* A0-AF */ /* XROM (+ 42S ext), GTO/XEQ IND, SPARE1 */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_SF | 0x2000, CMD_CF | 0x2000, CMD_FSC_T | 0x2000, CMD_FCC_T | 0x2000, CMD_FS_T | 0x2000, CMD_FC_T | 0x2000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, /* B0-BF */ /* SPARE2, GTO 00-14 */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, /* C0-CF */ /* GLOBAL */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_X_SWAP | 0x2000, CMD_LBL | 0x2000, /* D0-DF */ /* 3-byte GTO */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, /* E0-EF */ /* 3-byte XEQ */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, /* F0-FF */ /* Strings + 42S ext */ CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000, CMD_NULL | 0x3000 }; static int hp42ext[] = { /* Flag values: 0 = string, 1 = IND string, 2 = suffix, 3 = special, * 4 = illegal */ /* 80-8F */ CMD_VIEW | 0x0000, CMD_STO | 0x0000, CMD_STO_ADD | 0x0000, CMD_STO_SUB | 0x0000, CMD_STO_MUL | 0x0000, CMD_STO_DIV | 0x0000, CMD_X_SWAP | 0x0000, CMD_INDEX | 0x0000, CMD_VIEW | 0x1000, CMD_STO | 0x1000, CMD_STO_ADD | 0x1000, CMD_STO_SUB | 0x1000, CMD_STO_MUL | 0x1000, CMD_STO_DIV | 0x1000, CMD_X_SWAP | 0x1000, CMD_INDEX | 0x1000, /* 90-9F */ CMD_MVAR | 0x0000, CMD_RCL | 0x0000, CMD_RCL_ADD | 0x0000, CMD_RCL_SUB | 0x0000, CMD_RCL_MUL | 0x0000, CMD_RCL_DIV | 0x0000, CMD_ISG | 0x0000, CMD_DSE | 0x0000, CMD_MVAR | 0x1000, CMD_RCL | 0x1000, CMD_RCL_ADD | 0x1000, CMD_RCL_SUB | 0x1000, CMD_RCL_MUL | 0x1000, CMD_RCL_DIV | 0x1000, CMD_ISG | 0x1000, CMD_DSE | 0x1000, /* A0-AF */ CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_SF | 0x1000, CMD_CF | 0x1000, CMD_FSC_T | 0x1000, CMD_FCC_T | 0x1000, CMD_FS_T | 0x1000, CMD_FC_T | 0x1000, CMD_GTO | 0x1000, CMD_XEQ | 0x1000, /* B0-BF */ CMD_CLV | 0x0000, CMD_PRV | 0x0000, CMD_ASTO | 0x0000, CMD_ARCL | 0x0000, CMD_PGMINT | 0x0000, CMD_PGMSLV | 0x0000, CMD_INTEG | 0x0000, CMD_SOLVE | 0x0000, CMD_CLV | 0x1000, CMD_PRV | 0x1000, CMD_ASTO | 0x1000, CMD_ARCL | 0x1000, CMD_PGMINT | 0x1000, CMD_PGMSLV | 0x1000, CMD_INTEG | 0x1000, CMD_SOLVE | 0x1000, /* CO-CF */ CMD_NULL | 0x3000, /* ASSIGN */ CMD_VARMENU | 0x0000, CMD_NULL | 0x3000, /* KEYX name */ CMD_NULL | 0x3000, /* KEYG name */ CMD_DIM | 0x0000, CMD_INPUT | 0x0000, CMD_EDITN | 0x0000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_VARMENU | 0x1000, CMD_NULL | 0x3000, /* KEYX IND name */ CMD_NULL | 0x3000, /* KEYG IND name */ CMD_DIM | 0x1000, CMD_INPUT | 0x1000, CMD_EDITN | 0x1000, CMD_NULL | 0x4000, /* DO-DF */ CMD_INPUT | 0x2000, CMD_RCL_ADD | 0x2000, CMD_RCL_SUB | 0x2000, CMD_RCL_MUL | 0x2000, CMD_RCL_DIV | 0x2000, CMD_NULL | 0x4000, /* FIX 10 */ CMD_NULL | 0x4000, /* SCI 10 */ CMD_NULL | 0x4000, /* ENG 10 */ CMD_CLV | 0x2000, CMD_PRV | 0x2000, CMD_INDEX | 0x2000, CMD_SIGMAREG | 0x1000, CMD_FIX | 0x1000, CMD_SCI | 0x1000, CMD_ENG | 0x1000, CMD_TONE | 0x1000, /* E0-EF */ CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x3000, /* KEYX suffix */ CMD_NULL | 0x3000, /* KEYG suffix */ CMD_NULL | 0x4000, CMD_NULL | 0x4000, /* FIX 11 */ CMD_NULL | 0x4000, /* SCI 11 */ CMD_NULL | 0x4000, /* ENG 11 */ CMD_PGMINT | 0x2000, CMD_PGMSLV | 0x2000, CMD_INTEG | 0x2000, CMD_SOLVE | 0x2000, CMD_DIM | 0x2000, CMD_NULL | 0x4000, CMD_INPUT | 0x2000, CMD_EDITN | 0x2000, /* F0-FF */ CMD_CLP | 0x0000, CMD_NULL | 0x4000, /* XFCN */ CMD_NULL | 0x4000, /* GTO . nnnn */ CMD_NULL | 0x4000, /* GTO .. */ CMD_NULL | 0x4000, /* GTO . "name" */ CMD_NULL | 0x4000, CMD_NULL | 0x4000, /* DEL */ CMD_NULL | 0x3000, /* SIZE */ CMD_VARMENU | 0x2000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000, CMD_NULL | 0x4000 }; static int getbyte(char *buf, int *bufptr, int *buflen, int maxlen) { if (*bufptr == *buflen) { *buflen = shell_read(buf, maxlen); if (*buflen <= 0) return -1; *bufptr = 0; } return (unsigned char) buf[(*bufptr)++]; } void core_import_programs(int (*progress_report)(const char *)) { char buf[1000]; int i, nread = 0; int need_to_rebuild_label_table = 0; int pos = 0; int byte1, byte2, suffix; int cmd, flag, str_len; int done_flag = 0; arg_struct arg; int assign = 0; int at_end = 1; int instrcount = -1; int report_label = 0; set_running(false); /* Set print mode to MAN during the import, to prevent store_command() * from printing programs as they load */ int saved_trace = flags.f.trace_print; int saved_normal = flags.f.normal_print; flags.f.trace_print = 0; flags.f.normal_print = 0; while (!done_flag) { skip: if (progress_report != NULL) { /* Poll the progress dialog every 100 instructions, in case * we're reading a bogus file: normally, we only update the * dialog when we encounter a GLOBAL, and that should be often * enough when reading legitimate HP-42S programs, but if we're * reading garbage, it may not happen at all, and that would * mean the user would get no chance to intervene. */ if (++instrcount == 100) { if (progress_report(NULL)) goto done; instrcount = 0; } } byte1 = getbyte(buf, &pos, &nread, 1000); if (byte1 == -1) goto done; cmd = hp42tofree42[byte1]; flag = cmd >> 12; cmd &= 0x0FFF; if (flag == 0) { arg.type = ARGTYPE_NONE; goto store; } else if (flag == 1) { arg.type = ARGTYPE_NUM; arg.val.num = byte1 & 15; if (cmd == CMD_LBL) arg.val.num--; goto store; } else if (flag == 2) { suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; goto do_suffix; } else /* flag == 3 */ { if (byte1 == 0x00) /* NULL */ goto skip; else if (byte1 >= 0x10 && byte1 <= 0x1C) { /* Number */ char numbuf[19]; int numlen = 0; int s2d_err; do { if (byte1 == 0x1A) byte1 = flags.f.decimal_point ? '.' : ','; else if (byte1 == 0x1B) byte1 = 24; else if (byte1 == 0x1C) byte1 = '-'; else byte1 += '0' - 0x10; numbuf[numlen++] = byte1; byte1 = getbyte(buf, &pos, &nread, 1000); } while (byte1 >= 0x10 && byte1 <= 0x1C); if (byte1 == -1) done_flag = 1; else if (byte1 != 0x00) pos--; s2d_err = string2phloat(numbuf, numlen, &arg.val_d); switch (s2d_err) { case 0: /* OK */ break; case 1: /* +overflow */ arg.val_d = POS_HUGE_PHLOAT; break; case 2: /* -overflow */ arg.val_d = NEG_HUGE_PHLOAT; break; case 3: /* +underflow */ arg.val_d = POS_TINY_PHLOAT; break; case 4: /* -underflow */ arg.val_d = NEG_TINY_PHLOAT; break; case 5: /* error */ arg.val_d = 0; break; } cmd = CMD_NUMBER; arg.type = ARGTYPE_DOUBLE; } else if (byte1 == 0x1D || byte1 == 0x1E) { cmd = byte1 == 0x1D ? CMD_GTO : CMD_XEQ; str_len = getbyte(buf, &pos, &nread, 1000); if (str_len == -1) goto done; else if (str_len < 0x0F1) { pos--; goto skip; } else str_len -= 0x0F0; arg.type = ARGTYPE_STR; goto do_string; } else if (byte1 == 0x1F) { /* "W" function (see HP-41C instruction table */ goto skip; } else if (byte1 >= 0x0A0 && byte1 <= 0x0A7) { /* XROM & parameterless HP-42S extensions. * I don't want to build a reverse look-up table * for these, so instead I just do a linear search * on the cmdlist table. */ uint4 code; byte2 = getbyte(buf, &pos, &nread, 1000); if (byte2 == -1) goto done; code = (((unsigned int) byte1) << 8) | byte2; for (i = 0; i < CMD_SENTINEL; i++) if (cmdlist(i)->hp42s_code == code) { if ((cmdlist(i)->flags & FLAG_HIDDEN) != 0) break; cmd = i; arg.type = ARGTYPE_NONE; goto store; } /* Not found; insert XROM instruction */ cmd = CMD_XROM; arg.type = ARGTYPE_NUM; arg.val.num = code & 0x07FF; goto store; } else if (byte1 == 0x0AE) { /* GTO/XEQ IND */ suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; if ((suffix & 0x80) != 0) cmd = CMD_XEQ; else { cmd = CMD_GTO; suffix |= 0x080; } goto do_suffix; } else if (byte1 == 0x0AF || byte1 == 0x0B0) { /* SPARE functions (see HP-41C instruction table */ goto skip; } else if (byte1 >= 0x0B1 && byte1 <= 0x0BF) { /* 2-byte GTO */ byte2 = getbyte(buf, &pos, &nread, 1000); if (byte2 == -1) goto done; cmd = CMD_GTO; arg.type = ARGTYPE_NUM; arg.val.num = (byte1 & 15) - 1; goto store; } else if (byte1 >= 0x0C0 && byte1 <= 0x0CD) { /* GLOBAL */ byte2 = getbyte(buf, &pos, &nread, 1000); if (byte2 == -1) goto done; str_len = getbyte(buf, &pos, &nread, 1000); if (str_len == -1) goto done; if (str_len < 0x0F1) { /* END */ if (progress_report != NULL && progress_report("END")) goto done; at_end = 1; goto skip; } else { /* LBL "" */ str_len -= 0x0F1; byte2 = getbyte(buf, &pos, &nread, 1000); if (byte2 == -1) goto done; cmd = CMD_LBL; arg.type = ARGTYPE_STR; if (progress_report != NULL) report_label = 1; goto do_string; } } else if (byte1 >= 0x0D0 && byte1 <= 0x0EF) { /* 3-byte GTO & XEQ */ byte2 = getbyte(buf, &pos, &nread, 1000); if (byte2 == -1) goto done; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; cmd = byte1 <= 0x0DF ? CMD_GTO : CMD_XEQ; suffix &= 0x7F; goto do_suffix; } else /* byte1 >= 0xF0 && byte1 <= 0xFF */ { /* Strings and parameterized HP-42S extensions */ if (byte1 == 0x0F0) { /* Zero-length strings can only be entered synthetically * on the HP-41; they act as NOPs and are sometimes used * right after ISG or DSE. * I would be within my rights to drop these instructions * on the floor -- the real 42S doesn't deal with them * all that gracefully either -- but I'm just too nice, * so I convert them to |-"", which is also a NOP. */ cmd = CMD_STRING; arg.type = ARGTYPE_STR; arg.length = 1; arg.val.text[0] = 127; goto store; } byte2 = getbyte(buf, &pos, &nread, 1000); if (byte1 == 0x0F1) { switch (byte2) { case 0x0D5: cmd = CMD_FIX; arg.val.num = 10; break; case 0x0D6: cmd = CMD_SCI; arg.val.num = 10; break; case 0x0D7: cmd = CMD_ENG; arg.val.num = 10; break; case 0x0E5: cmd = CMD_FIX; arg.val.num = 11; break; case 0x0E6: cmd = CMD_SCI; arg.val.num = 11; break; case 0x0E7: cmd = CMD_ENG; arg.val.num = 11; break; default: goto plain_string; } arg.type = ARGTYPE_NUM; goto store; } if ((byte2 & 0x080) == 0 || byte1 < 0x0F2) { /* String */ int i; plain_string: str_len = byte1 - 0x0F0; pos--; cmd = CMD_STRING; arg.type = ARGTYPE_STR; do_string: for (i = 0; i < str_len; i++) { suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; arg.val.text[i] = suffix; } arg.length = str_len; if (assign) { assign = 0; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; if (suffix > 17) { /* Bad assign... Fix the command to the string * it would have been if I had known this * earlier. */ for (i = arg.length; i > 0; i--) arg.val.text[i] = arg.val.text[i - 1]; arg.val.text[0] = (char) 0xC0; arg.val.text[arg.length + 1] = suffix; arg.length += 2; } else { cmd += suffix; } } if (report_label) { char s[52]; int sl = hp2ascii(s + 1, arg.val.text, arg.length); s[0] = s[sl + 1] = '"'; s[sl + 2] = 0; report_label = 0; if (progress_report(s)) goto done; } goto store; } else { /* Parameterized HP-42S extension */ cmd = hp42ext[byte2 - 0x080]; flag = cmd >> 12; cmd &= 0x0FFF; if (flag == 0 || flag == 1) { arg.type = flag == 0 ? ARGTYPE_STR : ARGTYPE_IND_STR; str_len = byte1 - 0x0F1; goto do_string; } else if (flag == 2) { int ind; if (byte1 != 0x0F2) goto plain_string; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; do_suffix: ind = (suffix & 0x080) != 0; suffix &= 0x7F; if (!ind && suffix >= 102 && suffix <= 111) { arg.type = ARGTYPE_LCLBL; arg.val.lclbl = 'A' + (suffix - 102); } else if (!ind && suffix >= 123) { arg.type = ARGTYPE_LCLBL; arg.val.lclbl = 'a' + (suffix - 123); } else if (suffix >= 112 && suffix <= 116) { arg.type = ind ? ARGTYPE_IND_STK : ARGTYPE_STK; switch (suffix) { case 112: arg.val.stk = 'T'; break; case 113: arg.val.stk = 'Z'; break; case 114: arg.val.stk = 'Y'; break; case 115: arg.val.stk = 'X'; break; case 116: arg.val.stk = 'L'; break; } } else { arg.type = ind ? ARGTYPE_IND_NUM : ARGTYPE_NUM; arg.val.num = suffix; } goto store; } else if (flag == 3) { if (byte2 == 0x0C0) { /* ASSIGN */ str_len = byte1 - 0x0F2; if (str_len == 0) goto plain_string; assign = 1; cmd = CMD_ASGN01; arg.type = ARGTYPE_STR; goto do_string; } else if (byte2 == 0x0C2 || byte2 == 0x0C3 || byte2 == 0x0CA || byte2 == 0x0CB) { /* KEYG/KEYX name, KEYG/KEYX IND name */ str_len = byte1 - 0x0F2; if (str_len == 0) goto plain_string; cmd = byte2 == 0x0C2 || byte2 == 0x0CA ? CMD_KEY1X : CMD_KEY1G; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; if (suffix < 1 || suffix > 9) { /* Treat as plain string. Alas, it is * not safe to back up 2 positions, so * I do the whole thing here. */ int i; bad_keyg_keyx: cmd = CMD_STRING; arg.type = ARGTYPE_STR; arg.length = str_len + 2; arg.val.text[0] = byte2; arg.val.text[1] = suffix; for (i = 2; i < arg.length; i++) { int c = getbyte(buf, &pos, &nread,1000); if (c == -1) goto done; arg.val.text[i] = c; } goto store; } cmd += suffix - 1; arg.type = byte2 == 0x0C2 || byte2 == 0x0C3 ? ARGTYPE_STR : ARGTYPE_IND_STR; goto do_string; } else if (byte2 == 0x0E2 || byte2 == 0x0E3) { /* KEYG/KEYX suffix */ if (byte1 != 0x0F3) goto plain_string; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; if (suffix < 1 || suffix > 9) goto bad_keyg_keyx; cmd = byte2 == 0x0E2 ? CMD_KEY1X : CMD_KEY1G; cmd += suffix - 1; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; goto do_suffix; } else /* byte2 == 0x0F7 */ { /* SIZE */ int sz; if (byte1 != 0x0F3) goto plain_string; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; sz = suffix << 8; suffix = getbyte(buf, &pos, &nread, 1000); if (suffix == -1) goto done; sz += suffix; cmd = CMD_SIZE; arg.type = ARGTYPE_NUM; arg.val.num = sz; goto store; } } else /* flag == 4 */ { /* Illegal value; treat as plain string */ goto plain_string; } } } } store: if (at_end) { goto_dot_dot(); at_end = 0; } store_command_after(&pc, cmd, &arg); need_to_rebuild_label_table = 1; } done: if (need_to_rebuild_label_table) { rebuild_label_table(); update_catalog(); } flags.f.trace_print = saved_trace; flags.f.normal_print = saved_normal; } void core_copy(char *buf, int buflen) { int len = vartype2string(reg_x, buf, buflen - 1); buf[len] = 0; if (reg_x->type == TYPE_REAL || reg_x->type == TYPE_COMPLEX) { /* Convert small-caps 'E' to regular 'e' */ while (--len >= 0) if (buf[len] == 24) buf[len] = 'e'; } } static bool is_number_char(char c) { return (c >= '0' && c <= '9') || c == '.' || c == ',' || c == '+' || c == '-' || c == 'e' || c == 'E' || c == 24; } static bool parse_phloat(const char *p, int len, phloat *res) { // We can't pass the string on to string2phloat() unchanged, because // that function is picky: it does not allow '+' signs, and it does // not allow the mantissa to be more than 12 digits long (including // leading zeroes). So, we massage the string a bit to make it // comply with those restrictions. char buf[100]; bool in_mant = true; int mant_digits = 0; int i = 0, j = 0; while (i < 100 && j < len) { char c = p[j++]; if (c == 0) break; if (c == '+') continue; else if (c == 'e' || c == 'E' || c == 24) { in_mant = false; buf[i++] = 24; } else if (c >= '0' && c <= '9') { if (!in_mant || mant_digits++ < 12) buf[i++] = c; } else buf[i++] = c; } int err = string2phloat(buf, i, res); if (err == 0) return true; else if (err == 1) { *res = POS_HUGE_PHLOAT; return true; } else if (err == 2) { *res = NEG_HUGE_PHLOAT; return true; } else if (err == 3 || err == 4) { *res = 0; return true; } else return false; } void core_paste(const char *buf) { phloat re, im; int i, s1, e1, s2, e2; vartype *v; int base = get_base(); if (base != 10) { int bpd = base == 2 ? 1 : base == 8 ? 3 : 4; int bits = 0; bool neg = false; int8 n = 0; i = 0; while (buf[i] == ' ') i++; if (buf[i] == '-') { neg = true; i++; } while (bits < 36) { char c = buf[i++]; if (c == 0) break; int d; if (base == 16) { if (c >= '0' && c <= '9') d = c - '0'; else if (c >= 'A' && c <= 'F') d = c - 'A' + 10; else if (c >= 'a' && c <= 'f') d = c - 'a' + 10; else break; } else { if (c >= 0 && c < '0' + base) d = c - '0'; else break; } n = n << bpd | d; bits += bpd; } if (bits == 0) goto paste_string; if (neg) n = -n; if ((n & LL(0x800000000)) == 0) n &= LL(0x7ffffffff); else n |= LL(0xfffffff000000000); v = new_real((phloat) n); goto paste; } /* Try matching " %g i %g " */ i = 0; while (buf[i] == ' ') i++; s1 = i; while (is_number_char(buf[i])) i++; e1 = i; if (e1 == s1) goto attempt_2; while (buf[i] == ' ') i++; if (buf[i] == 'i') i++; else goto attempt_2; while (buf[i] == ' ') i++; s2 = i; while (is_number_char(buf[i])) i++; e2 = i; if (e2 == s2) goto attempt_2; goto finish_complex; /* Try matching " %g + %g i " */ attempt_2: i = 0; while (buf[i] == ' ') i++; s1 = i; while (is_number_char(buf[i])) i++; e1 = i; if (e1 == s1) goto attempt_3; while (buf[i] == ' ') i++; if (buf[i] == '+') i++; else goto attempt_3; while (buf[i] == ' ') i++; s2 = i; while (is_number_char(buf[i])) i++; e2 = i; if (e2 == s2) goto attempt_3; goto finish_complex; /* Try matching " ( %g , %g ) " */ /* To avoid the ambiguity with the comma, a colon or semicolon is * also accepted; if those are used, you don't need to surround them * with spaces to distinguish them from 'number' chars */ attempt_3: i = 0; while (buf[i] == ' ') i++; if (buf[i] == '(') i++; else goto attempt_4; while (buf[i] == ' ') i++; s1 = i; while (is_number_char(buf[i])) i++; e1 = i; if (e1 == s1) goto attempt_4; while (buf[i] == ' ') i++; if (buf[i] == ',' || buf[i] == ':' || buf[i] == ';') i++; else goto attempt_4; while (buf[i] == ' ') i++; s2 = i; while (is_number_char(buf[i])) i++; e2 = i; if (e2 == s2) goto attempt_4; finish_complex: if (!parse_phloat(buf + s1, e1 - s1, &re)) goto attempt_4; if (!parse_phloat(buf + s2, e2 - s2, &im)) goto attempt_4; v = new_complex(re, im); goto paste; /* Try matching " %g " */ attempt_4: i = 0; while (buf[i] == ' ') i++; s1 = i; while (is_number_char(buf[i])) i++; e1 = i; if (e1 != s1 && parse_phloat(buf + s1, e1 - s1, &re)) v = new_real(re); else { paste_string: int len = 0; while (len < 6 && buf[len] != 0) len++; v = new_string(buf, len); } paste: if (v == NULL) { squeak(); return; } else { if (!flags.f.prgm_mode) mode_number_entry = false; recall_result(v); flags.f.stack_lift_disable = 0; } redisplay(); } void set_alpha_entry(bool state) { mode_alpha_entry = state; } void set_running(bool state) { if (mode_running != state) { mode_running = state; shell_annunciators(-1, -1, -1, state, -1, -1); } if (state) { /* Cancel any pending INPUT command */ input_length = 0; mode_goose = -2; prgm_highlight_row = 1; } } bool program_running() { return mode_running; } void do_interactive(int command) { int err; if ((cmdlist(command)->flags & (flags.f.prgm_mode ? FLAG_NO_PRGM : FLAG_PRGM_ONLY)) != 0) { display_error(ERR_RESTRICTED_OPERATION, 0); redisplay(); return; } if (command == CMD_GOTOROW) { err = docmd_stoel(NULL); if (err != ERR_NONE) { display_error(err, 1); redisplay(); return; } } else if (command == CMD_A_THRU_F) { set_base(16); set_menu(MENULEVEL_APP, MENU_BASE_A_THRU_F); redisplay(); return; } else if (command == CMD_CLALLa) { mode_clall = true; redisplay(); return; } else if (command == CMD_CLV || command == CMD_PRV) { if (!flags.f.prgm_mode && vars_count == 0) { display_error(ERR_NO_VARIABLES, 0); redisplay(); return; } } else if (command == CMD_SST && flags.f.prgm_mode) { sst(); redisplay(); repeating = 1; repeating_shift = 1; repeating_key = KEY_DOWN; return; } else if (command == CMD_BST) { bst(); if (!flags.f.prgm_mode) { flags.f.prgm_mode = 1; redisplay(); flags.f.prgm_mode = 0; pending_command = CMD_CANCELLED; } else redisplay(); repeating = 1; repeating_shift = 1; repeating_key = KEY_UP; return; } if (flags.f.prgm_mode && (cmdlist(command)->flags & FLAG_IMMED) == 0) { if (command == CMD_RUN) command = CMD_STOP; if (cmdlist(command)->argtype == ARG_NONE) { arg_struct arg; arg.type = ARGTYPE_NONE; store_command_after(&pc, command, &arg); if (command == CMD_END) pc = 0; prgm_highlight_row = 1; redisplay(); } else { incomplete_saved_pc = pc; incomplete_saved_highlight_row = prgm_highlight_row; if (pc == -1) pc = 0; else if (prgms[current_prgm].text[pc] != CMD_END) pc += get_command_length(current_prgm, pc); prgm_highlight_row = 1; start_incomplete_command(command); } } else { if (cmdlist(command)->argtype == ARG_NONE) pending_command = command; else { if (flags.f.prgm_mode) { incomplete_saved_pc = pc; incomplete_saved_highlight_row = prgm_highlight_row; } start_incomplete_command(command); } } } static void continue_running() { int error; while (!shell_wants_cpu()) { int cmd; arg_struct arg; oldpc = pc; if (pc == -1) pc = 0; else if (pc >= prgms[current_prgm].size) { pc = -1; set_running(false); return; } get_next_command(&pc, &cmd, &arg, 1); if (flags.f.trace_print && flags.f.printer_exists) print_program_line(current_prgm, oldpc); mode_disable_stack_lift = false; error = cmdlist(cmd)->handler(&arg); if (mode_pause) { shell_request_timeout3(1000); return; } if (error == ERR_INTERRUPTIBLE) return; if (!handle_error(error)) return; if (mode_getkey) return; } } typedef struct { char name[7]; int namelen; int cmd_id; } synonym_spec; static synonym_spec hp41_synonyms[] = { { "/", 1, CMD_DIV }, { "*", 1, CMD_MUL }, { "CHS", 3, CMD_CHS }, { "DEC", 3, CMD_TO_DEC }, { "D-R", 3, CMD_TO_RAD }, { "ENTER^", 6, CMD_ENTER }, { "FACT", 4, CMD_FACT }, { "FRC", 3, CMD_FP }, { "HMS", 3, CMD_TO_HMS }, { "HR", 2, CMD_TO_HR }, { "INT", 3, CMD_IP }, { "OCT", 3, CMD_TO_OCT }, { "P-R", 3, CMD_TO_REC }, { "R-D", 3, CMD_TO_DEG }, { "RDN", 3, CMD_RDN }, { "R-P", 3, CMD_TO_POL }, { "ST+", 3, CMD_STO_ADD }, { "ST/", 3, CMD_STO_DIV }, { "ST*", 3, CMD_STO_MUL }, { "ST-", 3, CMD_STO_SUB }, { "X<=0?", 5, CMD_X_LE_0 }, { "X<=Y?", 5, CMD_X_LE_Y }, { "", 0, CMD_NONE } }; int find_builtin(const char *name, int namelen) { int i, j; for (i = 0; hp41_synonyms[i].cmd_id != CMD_NONE; i++) { if (namelen != hp41_synonyms[i].namelen) continue; for (j = 0; j < namelen; j++) if (name[j] != hp41_synonyms[i].name[j]) goto nomatch1; return hp41_synonyms[i].cmd_id; nomatch1:; } for (i = 0; true; i++) { if (i == CMD_OPENF && !core_settings.enable_ext_copan) i += 14; if (i == CMD_DROP && !core_settings.enable_ext_bigstack) i++; if (i == CMD_ACCEL && !core_settings.enable_ext_accel) i++; if (i == CMD_LOCAT && !core_settings.enable_ext_locat) i++; if (i == CMD_HEADING && !core_settings.enable_ext_heading) i++; if (i == CMD_ADATE && !core_settings.enable_ext_time) i += 34; if (i == CMD_SENTINEL) break; if ((cmdlist(i)->flags & FLAG_HIDDEN) != 0) continue; if (cmdlist(i)->name_length != namelen) continue; for (j = 0; j < namelen; j++) { unsigned char c1, c2; c1 = name[j]; if (c1 >= 130 && c1 != 138) c1 &= 127; c2 = cmdlist(i)->name[j]; if (c2 >= 130 && c2 != 138) c2 &= 127; if (c1 != c2) goto nomatch2; } return i; nomatch2:; } return CMD_NONE; } void sst() { if (pc >= prgms[current_prgm].size - 2) { pc = -1; prgm_highlight_row = 0; } else { if (pc == -1) pc = 0; else pc += get_command_length(current_prgm, pc); prgm_highlight_row = 1; } } void bst() { int4 line = pc2line(pc); if (line == 0) { pc = prgms[current_prgm].size - 2; prgm_highlight_row = 1; } else { pc = line2pc(line - 1); prgm_highlight_row = 0; } } void fix_thousands_separators(char *buf, int *bufptr) { /* First, remove the old separators... */ int i, j = 0; char dot = flags.f.decimal_point ? '.' : ','; char sep = flags.f.decimal_point ? ',' : '.'; int intdigits = 0; int counting_intdigits = 1; int nsep; for (i = 0; i < *bufptr; i++) { char c = buf[i]; if (c != sep) buf[j++] = c; if (c == dot || c == 24) counting_intdigits = 0; else if (counting_intdigits && c >= '0' && c <= '9') intdigits++; } /* Now, put 'em back... */ if (!flags.f.thousands_separators) { *bufptr = j; return; } nsep = (intdigits - 1) / 3; if (nsep == 0) { *bufptr = j; return; } for (i = j - 1; i >= 0; i--) buf[i + nsep] = buf[i]; j += nsep; for (i = 0; i < j; i++) { char c = buf[i + nsep]; buf[i] = c; if (nsep > 0 && c >= '0' && c <= '9') { if (intdigits % 3 == 1) { buf[++i] = sep; nsep--; } intdigits--; } } *bufptr = j; } int find_menu_key(int key) { switch (key) { case KEY_SIGMA: return 0; case KEY_INV: return 1; case KEY_SQRT: return 2; case KEY_LOG: return 3; case KEY_LN: return 4; case KEY_XEQ: return 5; default: return -1; } } void start_incomplete_command(int cmd_id) { int argtype = cmdlist(cmd_id)->argtype; if (!flags.f.prgm_mode && (cmdlist(cmd_id)->flags & FLAG_PRGM_ONLY) != 0) { display_error(ERR_RESTRICTED_OPERATION, 0); redisplay(); return; } incomplete_command = cmd_id; incomplete_ind = 0; if (argtype == ARG_NAMED || argtype == ARG_PRGM || argtype == ARG_RVAR || argtype == ARG_MAT) incomplete_alpha = 1; else incomplete_alpha = 0; incomplete_length = 0; incomplete_num = 0; if (argtype == ARG_NUM9) incomplete_maxdigits = 1; else if (argtype == ARG_COUNT) incomplete_maxdigits = cmd_id == CMD_SIMQ ? 2 : 4; else incomplete_maxdigits = 2; incomplete_argtype = argtype; mode_command_entry = true; if (incomplete_command == CMD_ASSIGNa) { set_catalog_menu(CATSECT_TOP); flags.f.local_label = 0; } else if (argtype == ARG_CKEY) set_menu(MENULEVEL_COMMAND, MENU_CUSTOM1); else if (argtype == ARG_MKEY) set_menu(MENULEVEL_COMMAND, MENU_BLANK); else if (argtype == ARG_VAR) { if (mode_appmenu == MENU_VARMENU) mode_commandmenu = MENU_VARMENU; else if (mode_appmenu == MENU_INTEG_PARAMS) mode_commandmenu = MENU_INTEG_PARAMS; else set_catalog_menu(CATSECT_VARS_ONLY); } else if (argtype == ARG_NAMED) set_catalog_menu(CATSECT_VARS_ONLY); else if (argtype == ARG_REAL) { if (mode_appmenu == MENU_VARMENU) mode_commandmenu = MENU_VARMENU; else if (mode_appmenu == MENU_INTEG_PARAMS) mode_commandmenu = MENU_INTEG_PARAMS; else set_catalog_menu(CATSECT_REAL_ONLY); } else if (argtype == ARG_RVAR) { if (vars_exist(1, 0, 0)) set_catalog_menu(CATSECT_REAL_ONLY); else if (flags.f.prgm_mode) { if (incomplete_command == CMD_MVAR) mode_commandmenu = MENU_ALPHA1; } else { mode_command_entry = false; display_error(ERR_NO_REAL_VARIABLES, 0); } } else if (argtype == ARG_MAT) { if (flags.f.prgm_mode || vars_exist(0, 0, 1)) set_catalog_menu(CATSECT_MAT_ONLY); else if (cmd_id != CMD_DIM) { mode_command_entry = false; display_error(ERR_NO_MATRIX_VARIABLES, 0); } } else if (argtype == ARG_LBL || argtype == ARG_PRGM) set_catalog_menu(CATSECT_PGM_ONLY); else if (cmd_id == CMD_LBL) set_menu(MENULEVEL_COMMAND, MENU_ALPHA1); redisplay(); } void finish_command_entry(bool refresh) { if (pending_command == CMD_ASSIGNa) { pending_command = CMD_NONE; start_incomplete_command(CMD_ASSIGNb); return; } mode_command_entry = false; if (flags.f.prgm_mode) { set_menu(MENULEVEL_COMMAND, MENU_NONE); if (pending_command == CMD_NULL || pending_command == CMD_CANCELLED) { pc = incomplete_saved_pc; prgm_highlight_row = incomplete_saved_highlight_row; } else if (pending_command == CMD_SST || pending_command == CMD_BST) { pc = incomplete_saved_pc; prgm_highlight_row = incomplete_saved_highlight_row; if (pending_command == CMD_SST) sst(); else bst(); repeating = 1; repeating_shift = 1; repeating_key = pending_command == CMD_SST ? KEY_DOWN : KEY_UP; pending_command = CMD_NONE; redisplay(); } else { int inserting_an_end = pending_command == CMD_END; if ((cmdlist(pending_command)->flags & FLAG_IMMED) != 0) goto do_it_now; store_command(pc, pending_command, &pending_command_arg); if (inserting_an_end) /* current_prgm was already incremented by store_command() */ pc = 0; prgm_highlight_row = 1; pending_command = CMD_NONE; redisplay(); } } else { do_it_now: if (refresh) redisplay(); if (mode_commandmenu == MENU_CATALOG && (pending_command == CMD_GTO || pending_command == CMD_XEQ)) { /* TODO: also applies to CLP, PRP, and maybe more. * Does *not* apply to the program menus displayed by VARMENU, * PGMINT, PGMSLV, and... (?) */ int catsect = get_cat_section(); if (catsect == CATSECT_PGM || catsect == CATSECT_PGM_ONLY) { int row = get_cat_row(); set_menu(MENULEVEL_TRANSIENT, mode_commandmenu); set_cat_section(catsect); set_cat_row(row); remove_program_catalog = 1; } } if (pending_command == CMD_ASSIGNb || (pending_command >= CMD_ASGN01 && pending_command <= CMD_ASGN18)) { set_menu(MENULEVEL_PLAIN, mode_commandmenu); mode_plainmenu_sticky = true; } set_menu(MENULEVEL_COMMAND, MENU_NONE); if (pending_command == CMD_BST) { bst(); flags.f.prgm_mode = 1; redisplay(); flags.f.prgm_mode = 0; pending_command = CMD_CANCELLED; repeating = 1; repeating_shift = 1; repeating_key = KEY_UP; } } } void finish_xeq() { int prgm; int4 pc; int cmd; if (find_global_label(&pending_command_arg, &prgm, &pc)) cmd = CMD_NONE; else cmd = find_builtin(pending_command_arg.val.text, pending_command_arg.length); if (cmd == CMD_CLALLa) { mode_clall = true; mode_command_entry = false; pending_command = CMD_NONE; redisplay(); return; } if (cmd == CMD_NONE) finish_command_entry(true); else { /* Show the entered command in its XEQ "FOO" * form, briefly, just to confirm to the user * what they've entered; this will be replaced * by the matching built-in command after the * usual brief delay (timeout1() or the * explicit delay, below). */ mode_command_entry = false; redisplay(); if (cmdlist(cmd)->argtype == ARG_NONE) { pending_command = cmd; pending_command_arg.type = ARGTYPE_NONE; finish_command_entry(false); return; } else { shell_delay(250); pending_command = CMD_NONE; set_menu(MENULEVEL_COMMAND, MENU_NONE); if ((cmd == CMD_CLV || cmd == CMD_PRV) && !flags.f.prgm_mode && vars_count == 0) { display_error(ERR_NO_VARIABLES, 0); pending_command = CMD_NONE; redisplay(); } else start_incomplete_command(cmd); return; } } } void start_alpha_prgm_line() { incomplete_saved_pc = pc; incomplete_saved_highlight_row = prgm_highlight_row; if (pc == -1) pc = 0; else if (prgms[current_prgm].text[pc] != CMD_END) pc += get_command_length(current_prgm, pc); prgm_highlight_row = 1; if (cmdline_row == 1) display_prgm_line(0, -1); entered_string_length = 0; mode_alpha_entry = true; } void finish_alpha_prgm_line() { if (entered_string_length == 0) { pc = incomplete_saved_pc; prgm_highlight_row = incomplete_saved_highlight_row; } else { arg_struct arg; int i; arg.type = ARGTYPE_STR; arg.length = entered_string_length; for (i = 0; i < entered_string_length; i++) arg.val.text[i] = entered_string[i]; store_command(pc, CMD_STRING, &arg); prgm_highlight_row = 1; } mode_alpha_entry = false; } static void stop_interruptible() { int error = mode_interruptible(1); handle_error(error); mode_interruptible = NULL; if (mode_running) set_running(false); else shell_annunciators(-1, -1, -1, false, -1, -1); pending_command = CMD_NONE; redisplay(); } static int handle_error(int error) { if (mode_running) { if (error == ERR_RUN) error = ERR_NONE; if (error == ERR_NONE || error == ERR_NO || error == ERR_YES || error == ERR_STOP) flags.f.stack_lift_disable = mode_disable_stack_lift; if (error == ERR_NO) { if (prgms[current_prgm].text[pc] != CMD_END) pc += get_command_length(current_prgm, pc); } else if (error == ERR_STOP) { if (pc >= prgms[current_prgm].size) pc = -1; set_running(false); return 0; } else if (error != ERR_NONE && error != ERR_YES) { if (flags.f.error_ignore && error != ERR_SUSPICIOUS_OFF) { flags.f.error_ignore = 0; return 1; } if (solve_active() && (error == ERR_OUT_OF_RANGE || error == ERR_DIVIDE_BY_0 || error == ERR_INVALID_DATA || error == ERR_STAT_MATH_ERROR)) { unwind_stack_until_solve(); error = return_to_solve(1); if (error == ERR_STOP) set_running(false); if (error == ERR_NONE || error == ERR_RUN || error == ERR_STOP) return 0; } pc = oldpc; display_error(error, 1); set_running(false); return 0; } return 1; } else if (pending_command == CMD_SST) { if (error == ERR_RUN) error = ERR_NONE; if (error == ERR_NONE || error == ERR_NO || error == ERR_YES || error == ERR_STOP) flags.f.stack_lift_disable = mode_disable_stack_lift; if (error == ERR_NO) { if (prgms[current_prgm].text[pc] != CMD_END) pc += get_command_length(current_prgm, pc); goto noerr; } else if (error == ERR_NONE || error == ERR_YES || error == ERR_STOP) { noerr: error = ERR_NONE; if (pc > prgms[current_prgm].size) pc = -1; } else { if (flags.f.error_ignore) { flags.f.error_ignore = 0; goto noerr; } if (solve_active() && (error == ERR_OUT_OF_RANGE || error == ERR_DIVIDE_BY_0 || error == ERR_INVALID_DATA || error == ERR_STAT_MATH_ERROR)) { unwind_stack_until_solve(); error = return_to_solve(1); if (error == ERR_NONE || error == ERR_RUN || error == ERR_STOP) goto noerr; } pc = oldpc; display_error(error, 1); } return 0; } else { if (error == ERR_RUN) { set_running(true); error = ERR_NONE; } if (error == ERR_NONE || error == ERR_NO || error == ERR_YES || error == ERR_STOP) flags.f.stack_lift_disable = mode_disable_stack_lift; else if (flags.f.error_ignore) { flags.f.error_ignore = 0; error = ERR_NONE; } if (error != ERR_NONE && error != ERR_STOP) display_error(error, 1); return 0; } } free42-nologo-1.4.77/common/core_main.h000644 000765 000024 00000037330 12110237247 020234 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_MAIN_H #define CORE_MAIN_H 1 #include "free42.h" /**********************************/ /* Shell/Core interface functions */ /**********************************/ /* core_init() * * This function initializes the emulator core. If the read_state parameter is * 1, the core should read saved state using shell_read_saved_state(); if * it is 0, or if there is a problem reading the saved state, it should perform * a hard reset. * If the read_state parameter is 1, the 'version' parameter should contain the * state file version number; otherwise its value is not used. * This is guaranteed to be the first function called on the emulator core. */ void core_init(int read_state, int4 version); #if defined(IPHONE) || defined(ANDROID) /* core_enter_background() * * This function is called when the iPhone app has been placed in background * mode. It writes the state, just like core_quit(), but it doesn't perform * any cleanup, so the app can resume instantly if it is brought back to * the foreground. If the app is killed while in the background, nothing is * lost. */ void core_enter_background(); #endif /* core_quit() * * This function shuts down the emulator core. The core should save its state * using shell_write_saved_state(). * This is guaranteed to be the last function called on the emulator core. */ void core_quit(); /* core_repaint_display() * * This function asks the emulator core to repaint the display. The core will * respond by immediately calling shell_blitter() to repaint the entire * display. * The shell uses this function to re-generate the display after switching * skins. */ void core_repaint_display(); /* core_menu() * * The shell uses this function to check if a menu is active. This affects * whether or not clicking in the display, to activate menu keys, is * enabled. */ int core_menu(); /* core_alpha_menu() * * The shell uses this function to check if the core is in "alpha" mode (i.e. * the ALPHA menu or any of its submenus is active). This affects how events * from the keyboard (the real PC keyboard, not the on-screen one emulated by * Free42) or PalmOS Graffiti device are handled: in alpha mode, printable * ASCII characters are sent straight to the core; outside alpha mode, all * key events are translated sequences of HP-42S key events according to the * keymap file. */ int core_alpha_menu(); /* core_hex_menu() * * The shell uses this function to check if the core is in "hex" mode (i.e. * the A..F submenu of the BASE application is active). This affects how events * from the keyboard (the real PC keyboard, not the on-screen one emulated by * Free42) are handled: in hex mode, 'A' through 'F' and 'a' through 'f' are * translated to keycodes 1 through 6, regardless of the keyboard map. */ int core_hex_menu(); /* core_keydown() * * This function informs the emulator core that an HP-42S key was pressed. Keys * are identified using a numeric key code, which corresponds to the key * numbers returned by the HP-42S 'GETKEY' function: 'Sigma+' is 1, '1/x' is 2, * and so on. The shift key is 28 -- 'shift' handling is performed by the * emulator core, so it needs to receive raw key codes, unlike the 'GETKEY' * function, which handles the shift key itself and then returns key codes in * the range 38..74 for shifted keys. The core_keydown() function should only * be called with key codes from 1 to 37, inclusive. * Keys that cause immediate action should be handled immediately when this * function is called, and calls to core_keytimeout1(), core_keytimeout2(), * and core_keyup() should be ignored (until the next core_keydown(), that * is!). Keys that are handled only when *released* should not be handled * immediately when this function is called; the emulator core should store the * key code, handle core_keytimeout1() and core_keytimeout2() as appropriate, * and not perform any action until core_keyup() is called. * RETURNS: a flag indicating whether or not the front end should call this * function again as soon as possible. This will be 1 if the calculator is * running a user program, and is only returning execution to the shell because * it has detected that there is a pending event. * The 'enqueued' pointer is a return parameter that the emulator core uses to * tell the shell if it has enqueued the keystroke. If this is 1, the shell * should not send call timeout1(), timeout2(), or keyup() for this keystroke. * NOTE: a key code of 0 (zero) signifies 'no key'; this is needed if the shell * is calling this function because it asked to be called (by returning 1 the * last time) but no keystrokes are available. It is not necessary to balance * the keydown call with a keyup in this case. * The 'repeat' pointer is a return parameter that the emulator core uses to * ask the shell to auto-repeat the current key. If this is set to 1 or 2, the * shell will not call timeout1() and timeout2(), but will repeatedly call * core_repeat() until the key is released. (1 requests a slow repeat rate, for * SST/BST; 2 requests a fast repeat rate, for number/alpha entry.) */ int core_keydown(int key, int *enqueued, int *repeat); /* core_repeat() * * This function is called by the shell to signal auto-repeating key events. * It is the core's responsibility to keep track of *which* key is repeating. * The function can return 0, to request repeating to stop; 1, which requests * a slow repeat rate, for SST/BST; or 2, which requests a fast repeat rate, * for number/alpha entry. */ int core_repeat(); /* core_keytimeout1() * * This function informs the emulator core that the currently pressed key has * been held down for 1/4 of a second. (If the key is released less than 1/4 * second after being pressed, this function is not called.) * For keys that do not execute immediately, this marks the moment when the * calculator displays the key's function name. */ void core_keytimeout1(); /* core_keytimeout2() * * This function informs the emulator core that 2 seconds have passed since * core_keytimeout1() was called. (If the key is released less than 2 seconds * after core_keytimeout1() is called, this function is not called.) * This marks the moment when the calculator switches from displaying the key's * function name to displaying 'NULL' (informing the user that the key has been * annulled and so no operation will be performed when it is released). */ void core_keytimeout2(); /* core_timeout3() * * A wakeup call that the core can request by calling shell_request_timeout3(). * Used to implement PSE and the brief linger period after MEM, SHOW, and * shift-VARMENU. The 'repaint' parameter says whether the callback is invoked * because the timeout period expired (1), or because a key event is on its * way (0); if a key event is on its way, the emulator core should not repaint * the screen, in order to avoid flashing. * If the function returns 'true', this means that the timeout was used for * PSE, and the shell should resume program execution. */ bool core_timeout3(int repaint); /* core_keyup() * * This function informs the emulator core that the currently pressed key (that * is, the one whose key code was given to it in the most recent call to * core_keydown()) has been released. * This function is always called when a key is released, regardless of how * long it was held down, and regardless of whether or not core_keytimeout1() * and core_keytimeout2() were called following the most recent * core_keydown(). * RETURNS: a flag indicating whether or not the front end should call this * function again as soon as possible. This will be 1 if the calculator is * running a user program, and is only returning execution to the shell because * it has detected that there is a pending event. */ int core_keyup(); /* core_allows_powerdown() * * The shell can call this function to find out if the emulator core does not * mind if the system powers off. This is only really useful on systems where * free42 can actually control the power, and where it makes sense to do so -- * meaning battery-powered, single-tasking environments. In other words, * PalmOS. On other platforms, the shell does not control power (Unix), or * refrains from exercising that control (Windows, where user-level * applications can shut down the PC, but usually shouldn't); shells in those * environments have no use for this function and will never call it. * This function should return 'true' if a program is running and/or if flag 44 * (continuous_on) is set -- but not if flag 49 (low_battery) is set. * The emulator should not assume that a power-down will actually take place if * this function gets called, as other applications could veto the power-down * after we have already agreed to it. Any actions that a real HP-42S performs * at power-down (I can't think of anything except stop program execution) * should be done in response to the power cycle notifier core_powercycle(), * immediately followed by HP-42S power-up activities (again, not much comes to * mind, except resuming program execution if flag 11 was set, clearing flags * 11 and 44, and maybe some other flag stuff). * If the core vetoes a power-down but then wants the CPU immediately, i.e. if * it was waiting in GETKEY and now wants program execution to resume, it * should set *want_cpu to 1; otherwise it should set it to 0. */ int core_allows_powerdown(int *want_cpu); /* core_powercycle() * * This tells the core to pretend that a power cycle has just taken place. * Usually called right after core_init(), but on platforms where a power-down * can take place without shutting down applications, it can be called at any * time between core_init() and core_quit(). * The core should respond by performing power-down activities, immediately * followed by power-up activities. (The emulator core is never expected to * actually emulate the state of *being* off -- the host environment can handle * being off just fine by itself, thank you very much -- it's just the side * effects of the *transitions* of a power cycle we want emulated (stopping * program execution, restarting it if flag 11 is set, clearing flags 11 and * 44, and maybe other stuff once I discover it). * This function returns a flag telling the shell whether or not the core wants * to run again -- this will be true if flag 11 was set, so there's program * execution to be done. */ int core_powercycle(); /* core_list_programs() * * This function is called by the shell when the user activates the Export * Program command. The core returns a list of program names, corresponding to * the programs as they appear in the PGM catalog, but in reverse order (the * .END. is last). Each item corresponds to one program, and consists of the * names of all the global labels in that program. Programs that contain no * global labels are given the name END (or .END.). * The indexes into the list of program names should be used to identify * programs to core_export_programs(). * The function returns the number of items in the list, which is always >= 1. */ int core_list_programs(char *buf, int bufsize); /* core_program_size() * This function returns the size of a program, specified by its index. * The indexes correspond to those returned by core_list_programs(). The caller * should *only* use those indexes; any indexes outside of that range will * cause weirdness and mayhem. * The size returned by this function does not include the 3 bytes for the * END; this matches what the HP-42S displays on line 00, but the caller should * be aware that the actual byte stream produced by core_export_programs() will * be 3 bytes longer. */ int4 core_program_size(int prgm_index); /* core_export_programs() * * This function is called by the shell after the user has selected a nonempty * set of programs (from the list returned by core_list_programs()) and * confirmed the operation (supplied a file name etc.). * The 'count' parameter indicates how many programs are to be exported; the * 'indexes' parameter is an array of program indexes. The core will pass the * raw file data to the shell using the shell_write() function. * The 'progress_report' parameter is an optional callback that will be invoked * to report when each program was written. If the callback returns zero, the * export will abort. * The function returns 0 on success, and 1 if it was aborted by the * progress_report callback (i.e., by the user clicking "Cancel" in the * progress dialog); in the latter case, the shell should delete the unfinished * file. Shells that pass NULL for 'progress_report' can ignore the return * value, since it will always be 0. */ int core_export_programs(int count, const int *indexes, int (*progress_report)(const char *)); /* core_import_programs() * * This function is called by the shell after the user has selected a file to * import. The core will read the file data using the shell_read() function. * The parameter is a pointer to a function that will be called to deliver * progress reports: when importing HP-42S program files, it will be called * for each global label and each END encountered; when importing Free42 * program files, it will be called to report the number of programs loaded. * If the callback returns zero, the import will abort. */ void core_import_programs(int (*progress_report)(const char *)); /* core_copy() * * Returns a string representation of the contents of the X register. * Used by the shell to implement the Copy command. */ void core_copy(char *buf, int buflen); /* core_paste() * * Puts the given value on the stack, using RCL semantics. It tries to parse * the string as a complex or a real number; if that fails, it is pasted as * a plain string. * Used by the shell to implement the Paste command. */ void core_paste(const char *s); /* core_settings * * This is a struct that stores user-configurable core settings. The shell * should provide the appropriate controls in a "Preferences" dialog box to * allow the user to view and change these settings. */ typedef struct { bool matrix_singularmatrix; bool matrix_outofrange; bool raw_text; bool auto_repeat; bool enable_ext_copan; bool enable_ext_bigstack; bool enable_ext_accel; bool enable_ext_locat; bool enable_ext_heading; bool enable_ext_time; } core_settings_struct; extern core_settings_struct core_settings; /*******************/ /* Keyboard repeat */ /*******************/ extern int repeating; extern int repeating_shift; extern int repeating_key; /*******************/ /* Other functions */ /*******************/ void set_alpha_entry(bool state); void set_running(bool state); bool program_running(); int want_to_run_again(); void do_interactive(int command); int find_builtin(const char *name, int namelen); void sst(); void bst(); void fix_thousands_separators(char *buf, int *bufptr); int find_menu_key(int key); void start_incomplete_command(int cmd_id); void finish_command_entry(bool refresh); void finish_xeq(); void start_alpha_prgm_line(); void finish_alpha_prgm_line(); int shiftcharacter(char c); #endif free42-nologo-1.4.77/common/core_math1.cc000644 000765 000024 00000067140 12110237247 020462 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ // to enable debug out //#define WINDOWS_DEBUG #ifdef WINDOWS_DEBUG #include #endif #include #include "core_math1.h" #include "core_commands2.h" #include "core_display.h" #include "core_globals.h" #include "core_helpers.h" #include "core_main.h" #include "core_variables.h" #include "shell.h" #define SOLVE_VERSION 4 #define INTEG_VERSION 2 #define NUM_SHADOWS 10 /* Solver */ typedef struct { int version; char prgm_name[7]; int prgm_length; char active_prgm_name[7]; int active_prgm_length; char var_name[7]; int var_length; int keep_running; int prev_prgm; int4 prev_pc; int state; int which; int toggle; int retry_counter; phloat retry_value; phloat x1, x2, x3; phloat fx1, fx2; phloat prev_x, curr_x, curr_f; phloat xm, fxm; char shadow_name[NUM_SHADOWS][7]; int shadow_length[NUM_SHADOWS]; phloat shadow_value[NUM_SHADOWS]; uint4 last_disp_time; } solve_state; static solve_state solve; #define ROMB_K 5 // 1/2 million evals max! #define ROMB_MAX 20 /* Integrator */ typedef struct { int version; char prgm_name[7]; int prgm_length; char active_prgm_name[7]; int active_prgm_length; char var_name[7]; int var_length; int keep_running; int prev_prgm; int4 prev_pc; int state; phloat llim, ulim, acc; phloat a, b, eps; int n, m, i, k; phloat h, sum; phloat c[ROMB_K]; phloat s[ROMB_K+1]; int nsteps; phloat p; phloat t, u; phloat prev_int; int evalCount; } integ_state; static integ_state integ; static void reset_solve(); static void reset_integ(); bool persist_math() { int size = sizeof(solve_state); solve.version = SOLVE_VERSION; if (!shell_write_saved_state(&size, sizeof(int))) return false; if (!shell_write_saved_state(&solve, sizeof(solve_state))) return false; size = sizeof(integ_state); integ.version = INTEG_VERSION; if (!shell_write_saved_state(&size, sizeof(int))) return false; if (!shell_write_saved_state(&integ, sizeof(integ_state))) return false; return true; } bool unpersist_math(bool discard) { int size; bool success; void *dummy; if (shell_read_saved_state(&size, sizeof(int)) != sizeof(int)) return false; if (!discard && size == sizeof(solve_state)) { if (shell_read_saved_state(&solve, size) != size) return false; if (solve.version != SOLVE_VERSION) reset_solve(); } else { dummy = malloc(size); if (dummy == NULL) return false; success = shell_read_saved_state(dummy, size) == size; free(dummy); if (!success) return false; reset_solve(); } if (shell_read_saved_state(&size, sizeof(int)) != sizeof(int)) return false; if (!discard && size == sizeof(integ_state)) { if (shell_read_saved_state(&integ, size) != size) return false; if (integ.version != INTEG_VERSION) reset_integ(); } else { dummy = malloc(size); if (dummy == NULL) return false; success = shell_read_saved_state(dummy, size) == size; free(dummy); if (!success) return false; reset_integ(); } return true; } void reset_math() { reset_solve(); reset_integ(); } static void reset_solve() { int i; for (i = 0; i < NUM_SHADOWS; i++) solve.shadow_length[i] = 0; solve.prgm_length = 0; solve.active_prgm_length = 0; solve.state = 0; } static int find_shadow(const char *name, int length) { int i; for (i = 0; i < NUM_SHADOWS; i++) if (string_equals(solve.shadow_name[i], solve.shadow_length[i], name, length)) return i; return -1; } void put_shadow(const char *name, int length, phloat value) { int i = find_shadow(name, length); if (i == -1) { for (i = 0; i < NUM_SHADOWS; i++) if (solve.shadow_length[i] == 0) goto do_insert; /* No empty slots available. Remove slot 0 (the oldest) and * move all subsequent ones down, freeing up slot NUM_SHADOWS - 1 */ for (i = 0; i < NUM_SHADOWS - 1; i++) { string_copy(solve.shadow_name[i], &solve.shadow_length[i], solve.shadow_name[i + 1], solve.shadow_length[i + 1]); solve.shadow_value[i] = solve.shadow_value[i + 1]; } } do_insert: string_copy(solve.shadow_name[i], &solve.shadow_length[i], name, length); solve.shadow_value[i] = value; } int get_shadow(const char *name, int length, phloat *value) { int i = find_shadow(name, length); if (i == -1) return 0; *value = solve.shadow_value[i]; return 1; } void remove_shadow(const char *name, int length) { int i = find_shadow(name, length); int j; if (i == -1) return; for (j = i; j < NUM_SHADOWS - 1; j++) { string_copy(solve.shadow_name[i], &solve.shadow_length[i], solve.shadow_name[i + 1], solve.shadow_length[i + 1]); solve.shadow_value[i] = solve.shadow_value[i + 1]; } solve.shadow_length[NUM_SHADOWS - 1] = 0; } void set_solve_prgm(const char *name, int length) { string_copy(solve.prgm_name, &solve.prgm_length, name, length); } static int call_solve_fn(int which, int state) { int err, i; arg_struct arg; vartype *v = recall_var(solve.var_name, solve.var_length); phloat x = which == 1 ? solve.x1 : which == 2 ? solve.x2 : solve.x3; solve.prev_x = solve.curr_x; solve.curr_x = x; if (v == NULL || v->type != TYPE_REAL) { v = new_real(x); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; store_var(solve.var_name, solve.var_length, v); } else ((vartype_real *) v)->x = x; solve.which = which; solve.state = state; arg.type = ARGTYPE_STR; arg.length = solve.active_prgm_length; for (i = 0; i < arg.length; i++) arg.val.text[i] = solve.active_prgm_name[i]; err = docmd_gto(&arg); if (err != ERR_NONE) { free_vartype(v); return err; } push_rtn_addr(-2, 0); return ERR_RUN; } int start_solve(const char *name, int length, phloat x1, phloat x2) { if (solve_active()) return ERR_SOLVE_SOLVE; string_copy(solve.var_name, &solve.var_length, name, length); string_copy(solve.active_prgm_name, &solve.active_prgm_length, solve.prgm_name, solve.prgm_length); solve.prev_prgm = current_prgm; solve.prev_pc = pc; if (x1 == x2) { if (x1 == 0) { x2 = 1; solve.retry_counter = 0; } else { x2 = x1 * 1.000001; if (p_isinf(x2)) x2 = x1 * 0.999999; solve.retry_counter = -10; } } else { solve.retry_counter = 10; solve.retry_value = fabs(x1) < fabs(x2) ? x1 : x2; } if (x1 < x2) { solve.x1 = x1; solve.x2 = x2; } else { solve.x1 = x2; solve.x2 = x1; } solve.last_disp_time = 0; solve.toggle = 1; solve.keep_running = program_running(); return call_solve_fn(1, 1); } typedef struct { const char *text; int length; } message_spec; #define SOLVE_ROOT 0 #define SOLVE_SIGN_REVERSAL 1 #define SOLVE_EXTREMUM 2 #define SOLVE_BAD_GUESSES 3 #define SOLVE_CONSTANT 4 static const message_spec solve_message[] = { { NULL, 0 }, { "Sign Reversal", 13 }, { "Extremum", 8 }, { "Bad Guess(es)", 13 }, { "Constant?", 9 } }; static int finish_solve(int message) { vartype *v, *new_x, *new_y, *new_z, *new_t; arg_struct arg; int dummy, print; solve.state = 0; if (solve.which == -1) { /* Ridders was terminated because it wasn't making progress; this does * not necessarily mean that x3 is the best guess so far. So, to be * sure, select the value with the lowest absulute function value. */ phloat t1 = fabs(solve.fx1); phloat t2 = fabs(solve.fx2); phloat t3 = fabs(solve.curr_f); phloat t; if (t1 < t2) { solve.which = 1; t = t1; } else { solve.which = 2; t = t2; } if (t3 < t) solve.which = 3; } v = recall_var(solve.var_name, solve.var_length); ((vartype_real *) v)->x = solve.which == 1 ? solve.x1 : solve.which == 2 ? solve.x2 : solve.x3; new_x = dup_vartype(v); new_y = new_real(solve.prev_x); new_z = new_real(solve.curr_f); new_t = new_real(message); if (new_x == NULL || new_y == NULL || new_z == NULL || new_t == NULL) { free_vartype(new_x); free_vartype(new_y); free_vartype(new_z); free_vartype(new_t); return ERR_INSUFFICIENT_MEMORY; } free_vartype(reg_x); free_vartype(reg_y); free_vartype(reg_z); free_vartype(reg_t); reg_x = new_x; reg_y = new_y; reg_z = new_z; reg_t = new_t; current_prgm = solve.prev_prgm; pc = solve.prev_pc; arg.type = ARGTYPE_STR; string_copy(arg.val.text, &dummy, solve.var_name, solve.var_length); arg.length = solve.var_length; print = flags.f.trace_print && flags.f.printer_exists; if (!solve.keep_running) { view_helper(&arg, print); if (message != 0) { clear_row(1); draw_string(0, 1, solve_message[message].text, solve_message[message].length); flags.f.message = 1; flags.f.two_line_message = 1; flush_display(); } } else { if (print) { char namebuf[8], valbuf[22]; int namelen = 0, vallen; string2buf(namebuf, 8, &namelen, solve.var_name, solve.var_length); char2buf(namebuf, 8, &namelen, '='); vallen = vartype2string(v, valbuf, 22); print_wide(namebuf, namelen, valbuf, vallen); } } if (print && message != 0) print_lines(solve_message[message].text, solve_message[message].length, 1); return solve.keep_running ? ERR_NONE : ERR_STOP; } #ifdef WINDOWS_DEBUG static void printout(const phloat& p, const char* s) { char buf[256]; buf[0] = 0; if (s) { strcpy(buf, s); strcat(buf, " "); } p.bcd.asString(buf + strlen(buf)); strcat(buf, "\n"); OutputDebugString(buf); } #endif int return_to_solve(int failure) { phloat f, slope, s, xnew, prev_f = solve.curr_f; uint4 now_time; if (solve.state == 0) return ERR_INTERNAL_ERROR; if (!failure) { if (reg_x->type == TYPE_REAL) { f = ((vartype_real *) reg_x)->x; solve.curr_f = f; if (f == 0) return finish_solve(SOLVE_ROOT); } else { solve.curr_f = POS_HUGE_PHLOAT; failure = 1; } } else solve.curr_f = POS_HUGE_PHLOAT; if (!failure && solve.retry_counter != 0) { if (solve.retry_counter > 0) solve.retry_counter--; else solve.retry_counter++; } now_time = shell_milliseconds(); if (!solve.keep_running && solve.state > 1 && now_time >= solve.last_disp_time + 250) { /* Put on a show so the user won't think we're just drinking beer * while they're waiting anxiously for the solver to converge... */ char buf[22]; int bufptr = 0, i; solve.last_disp_time = now_time; clear_display(); bufptr = phloat2string(solve.curr_x, buf, 22, 0, 0, 3, flags.f.thousands_separators); for (i = bufptr; i < 21; i++) buf[i] = ' '; buf[21] = failure ? '?' : solve.curr_f > 0 ? '+' : '-'; draw_string(0, 0, buf, 22); bufptr = phloat2string(solve.prev_x, buf, 22, 0, 0, 3, flags.f.thousands_separators); for (i = bufptr; i < 21; i++) buf[i] = ' '; buf[21] = prev_f == POS_HUGE_PHLOAT ? '?' : prev_f > 0 ? '+' : '-'; draw_string(0, 1, buf, 22); flush_display(); flags.f.message = 1; flags.f.two_line_message = 1; } switch (solve.state) { case 1: /* first evaluation of x1 */ if (failure) { if (solve.retry_counter > 0) solve.retry_counter = -solve.retry_counter; return call_solve_fn(2, 2); } else { solve.fx1 = f; return call_solve_fn(2, 3); } case 2: /* first evaluation of x2 after x1 was unsuccessful */ if (failure) return finish_solve(SOLVE_BAD_GUESSES); solve.fx2 = f; solve.x1 = (solve.x1 + solve.x2) / 2; if (solve.x1 == solve.x2) return finish_solve(SOLVE_BAD_GUESSES); return call_solve_fn(1, 3); case 3: /* make sure f(x1) != f(x2) */ if (failure) { if (solve.which == 1) solve.x1 = (solve.x1 + solve.x2) / 2; else solve.x2 = (solve.x1 + solve.x2) / 2; if (solve.x1 == solve.x2) return finish_solve(SOLVE_BAD_GUESSES); return call_solve_fn(solve.which, 3); } if (solve.which == 1) solve.fx1 = f; else solve.fx2 = f; if (solve.fx1 == solve.fx2) { /* If f(x1) == f(x2), we assume we're in a local flat spot. * We extend the interval exponentially until we have two * values of x, both of which are evaluated successfully, * and yielding different values; from that moment on, we can * apply the secant method. */ int which; phloat x; if (solve.toggle) { x = solve.x2 + 100 * (solve.x2 - solve.x1); if (p_isinf(x)) { if (solve.retry_counter != 0) goto retry_solve; return finish_solve(SOLVE_CONSTANT); } which = 2; solve.x2 = x; } else { x = solve.x1 - 100 * (solve.x2 - solve.x1); if (p_isinf(x)) { if (solve.retry_counter != 0) goto retry_solve; return finish_solve(SOLVE_CONSTANT); } which = 1; solve.x1 = x; } solve.toggle = !solve.toggle; return call_solve_fn(which, 3); } /* When we get here, f(x1) != f(x2), and we can start * applying the secant method. */ goto do_secant; case 4: /* secant method, evaluated x3 */ case 5: /* just performed bisection */ if (failure) { if (solve.x3 > solve.x2) { /* Failure outside [x1, x2]; approach x2 */ solve.x3 = (solve.x2 + solve.x3) / 2; if (solve.x3 == solve.x2) return finish_solve(SOLVE_EXTREMUM); } else if (solve.x3 < solve.x1) { /* Failure outside [x1, x2]; approach x1 */ solve.x3 = (solve.x1 + solve.x3) / 2; if (solve.x3 == solve.x1) return finish_solve(SOLVE_EXTREMUM); } else { /* Failure inside [x1, x2]; * alternately approach x1 and x2 */ if (solve.toggle) { phloat old_x3 = solve.x3; if (solve.x3 <= (solve.x1 + solve.x2) / 2) solve.x3 = (solve.x1 + solve.x3) / 2; else solve.x3 = (solve.x2 + solve.x3) / 2; if (solve.x3 == old_x3) return finish_solve(SOLVE_SIGN_REVERSAL); } else solve.x3 = solve.x1 + solve.x2 - solve.x3; solve.toggle = !solve.toggle; if (solve.x3 == solve.x1 || solve.x3 == solve.x2) return finish_solve(SOLVE_SIGN_REVERSAL); } return call_solve_fn(3, 4); } else if (solve.fx1 > 0 && solve.fx2 > 0) { if (solve.fx1 > solve.fx2) { if (f >= solve.fx1 && solve.state != 5) goto do_bisection; solve.x1 = solve.x3; solve.fx1 = f; } else { if (f >= solve.fx2 && solve.state != 5) goto do_bisection; solve.x2 = solve.x3; solve.fx2 = f; } } else if (solve.fx1 < 0 && solve.fx2 < 0) { if (solve.fx1 < solve.fx2) { if (f <= solve.fx1 && solve.state != 5) goto do_bisection; solve.x1 = solve.x3; solve.fx1 = f; } else { if (f <= solve.fx2 && solve.state != 5) goto do_bisection; solve.x2 = solve.x3; solve.fx2 = f; } } else { /* f(x1) and f(x2) have opposite signs; assuming f is * continuous on the interval [x1, x2], there is at least * one root. We use x3 to replace x1 or x2 and narrow the * interval, even if f(x3) is actually worse than f(x1) and * f(x2). This way we're guaranteed to home in on the root * (but of course we'll get stuck if we encounter a * discontinuous sign reversal instead, e.g. 1/x at x = 0). * Such is life. */ if ((solve.fx1 > 0 && f > 0) || (solve.fx1 < 0 && f < 0)) { solve.x1 = solve.x3; solve.fx1 = f; } else { solve.x2 = solve.x3; solve.fx2 = f; } } if (solve.x2 < solve.x1) { /* Make sure x1 is always less than x2 */ phloat tmp = solve.x1; solve.x1 = solve.x2; solve.x2 = tmp; tmp = solve.fx1; solve.fx1 = solve.fx2; solve.fx2 = tmp; } do_secant: if (solve.fx1 == solve.fx2) return finish_solve(SOLVE_EXTREMUM); if ((solve.fx1 > 0 && solve.fx2 < 0) || (solve.fx1 < 0 && solve.fx2 > 0)) goto do_ridders; slope = (solve.fx2 - solve.fx1) / (solve.x2 - solve.x1); if (p_isinf(slope)) { solve.x3 = (solve.x1 + solve.x2) / 2; if (solve.x3 == solve.x1 || solve.x3 == solve.x2) return finish_solve(SOLVE_ROOT); else return call_solve_fn(3, 4); } else if (slope == 0) { /* Underflow caused by x2 - x1 being too big. * We're changing the calculation sequence to steer * clear of trouble. */ solve.x3 = solve.x1 - solve.fx1 * (solve.x2 - solve.x1) / (solve.fx2 - solve.fx1); goto finish_secant; } else { int inf; solve.x3 = solve.x1 - solve.fx1 / slope; finish_secant: if ((inf = p_isinf(solve.x3)) != 0) { if (solve.retry_counter != 0) goto retry_solve; return finish_solve(SOLVE_EXTREMUM); } /* The next two 'if' statements deal with the case that the * secant extrapolation returns one of the points we already * had. We assume this means no improvement is possible (TODO: * this is not necessarily true; Kahan's 34C article has an * example of this -- the thing to be careful of is that the * 'bad' value is so bad that the secant becomes excessively * steep). We fudge the 'solve' struct a bit to make sure we * don't return the 'bad' value as the root. */ if (solve.x3 == solve.x1) { solve.which = 1; solve.curr_f = solve.fx1; solve.prev_x = solve.x2; return finish_solve(SOLVE_ROOT); } if (solve.x3 == solve.x2) { solve.which = 2; solve.curr_f = solve.fx2; solve.prev_x = solve.x1; return finish_solve(SOLVE_ROOT); } /* If we're extrapolating, make sure we don't race away from * the current interval too quickly */ if (solve.x3 < solve.x1) { phloat min = solve.x1 - 100 * (solve.x2 - solve.x1); if (solve.x3 < min) solve.x3 = min; } else if (solve.x3 > solve.x2) { phloat max = solve.x2 + 100 * (solve.x2 - solve.x1); if (solve.x3 > max) solve.x3 = max; } else { /* If we're interpolating, make sure we actually make * some progress. Enforce a minimum distance between x3 * and the edges of the interval. */ phloat eps = (solve.x2 - solve.x1) / 10; if (solve.x3 < solve.x1 + eps) solve.x3 = solve.x1 + eps; else if (solve.x3 > solve.x2 - eps) solve.x3 = solve.x2 - eps; } return call_solve_fn(3, 4); } retry_solve: /* We hit infinity without finding two values of x where f(x) has * opposite signs, but we got to infinity suspiciously quickly. * If we started with two guesses, we now retry with only the lower * of the two; if we started with one guess, we now retry with * starting guesses of 0 and 1. */ if (solve.retry_counter > 0) { solve.x1 = solve.retry_value; solve.x2 = solve.x1 * 1.000001; if (p_isinf(solve.x2)) solve.x2 = solve.x1 * 0.999999; if (solve.x1 > solve.x2) { phloat tmp = solve.x1; solve.x1 = solve.x2; solve.x2 = tmp; } solve.retry_counter = -10; } else { solve.x1 = 0; solve.x2 = 1; solve.retry_counter = 0; } return call_solve_fn(1, 1); do_bisection: solve.x3 = (solve.x1 + solve.x2) / 2; return call_solve_fn(3, 5); case 6: /* Ridders' method, evaluated midpoint */ if (failure) goto do_bisection; s = sqrt(f * f - solve.fx1 * solve.fx2); if (s == 0) { /* Mathematically impossible, but numerically possible if * the function is so close to zero that f^2 underflows. * We could handle this better but this seems adequate. */ solve.which = -1; return finish_solve(SOLVE_ROOT); } solve.xm = solve.x3; solve.fxm = f; if (solve.fx1 < solve.fx2) s = -s; xnew = solve.xm + (solve.xm - solve.x1) * (solve.fxm / s); if (xnew == solve.x1 || xnew == solve.x2) { solve.which = -1; return finish_solve(SOLVE_ROOT); } solve.x3 = xnew; return call_solve_fn(3, 7); case 7: /* Ridders' method, evaluated xnew */ if (failure) goto do_bisection; if ((f > 0 && solve.fxm < 0) || (f < 0 && solve.fxm > 0)) { if (solve.xm < solve.x3) { solve.x1 = solve.xm; solve.fx1 = solve.fxm; solve.x2 = solve.x3; solve.fx2 = f; } else { solve.x1 = solve.x3; solve.fx1 = f; solve.x2 = solve.xm; solve.fx2 = solve.fxm; } } else if ((f > 0 && solve.fx1 < 0) || (f < 0 && solve.fx1 > 0)) { solve.x2 = solve.x3; solve.fx2 = f; } else /* f > 0 && solve.fx2 < 0 || f < 0 && solve.fx2 > 0 */ { solve.x1 = solve.x3; solve.fx1 = f; } do_ridders: solve.x3 = (solve.x1 + solve.x2) / 2; // TODO: The following termination condition should really be // // if (solve.x3 == solve.x1 || solve.x3 == solve.x2) // // since it is mathematically impossible for x3 to lie outside the // interval [x1, x2], but due to decimal round-off, it is, in fact, // possible, e.g. consider x1 = 7...2016 and x2 = 7...2018; this // results in x3 = 7...2015. Fixing this will require either // extended-precision math for solver internals, or at least a // slightly smarter implementation of the midpoint calculation, // e.g. fall back on x3 = x1 + (x2 - x1) / 2 if x3 = (x1 + x2) / 2 // returns an incorrect result. if (solve.x3 <= solve.x1 || solve.x3 >= solve.x2) { solve.which = -1; return finish_solve(SOLVE_ROOT); } else return call_solve_fn(3, 6); default: return ERR_INTERNAL_ERROR; } } static void reset_integ() { integ.prgm_length = 0; integ.active_prgm_length = 0; integ.state = 0; } void set_integ_prgm(const char *name, int length) { string_copy(integ.prgm_name, &integ.prgm_length, name, length); } void get_integ_prgm(char *name, int *length) { string_copy(name, length, integ.prgm_name, integ.prgm_length); } void set_integ_var(const char *name, int length) { string_copy(integ.var_name, &integ.var_length, name, length); } void get_integ_var(char *name, int *length) { string_copy(name, length, integ.var_name, integ.var_length); } static int call_integ_fn() { int err, i; arg_struct arg; phloat x = integ.u; vartype *v = recall_var(integ.var_name, integ.var_length); if (v == NULL || v->type != TYPE_REAL) { v = new_real(x); if (v == NULL) return ERR_INSUFFICIENT_MEMORY; store_var(integ.var_name, integ.var_length, v); } else ((vartype_real *) v)->x = x; arg.type = ARGTYPE_STR; arg.length = integ.active_prgm_length; for (i = 0; i < arg.length; i++) arg.val.text[i] = integ.active_prgm_name[i]; err = docmd_gto(&arg); if (err != ERR_NONE) { free_vartype(v); return err; } push_rtn_addr(-3, 0); return ERR_RUN; } int start_integ(const char *name, int length) { vartype *v; if (integ_active()) return ERR_INTEG_INTEG; v = recall_var("LLIM", 4); if (v == NULL) return ERR_NONEXISTENT; else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (v->type != TYPE_REAL) return ERR_INVALID_TYPE; integ.llim = ((vartype_real *) v)->x; v = recall_var("ULIM", 4); if (v == NULL) return ERR_NONEXISTENT; else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (v->type != TYPE_REAL) return ERR_INVALID_TYPE; integ.ulim = ((vartype_real *) v)->x; v = recall_var("ACC", 3); if (v == NULL) integ.acc = 0; else if (v->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (v->type != TYPE_REAL) return ERR_INVALID_TYPE; else integ.acc = ((vartype_real *) v)->x; if (integ.acc < 0) integ.acc = 0; string_copy(integ.var_name, &integ.var_length, name, length); string_copy(integ.active_prgm_name, &integ.active_prgm_length, integ.prgm_name, integ.prgm_length); integ.prev_prgm = current_prgm; integ.prev_pc = pc; integ.a = integ.llim; integ.b = integ.ulim - integ.llim; integ.h = 2; integ.prev_int = 0; integ.nsteps = 1; integ.n = 1; integ.state = 1; integ.s[0] = 0; integ.k = 1; integ.evalCount = 0; integ.keep_running = program_running(); if (!integ.keep_running) { clear_row(0); draw_string(0, 0, "Integrating", 11); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; } return return_to_integ(0); } static int finish_integ() { vartype *x, *y; int saved_trace = flags.f.trace_print; integ.state = 0; #ifdef WINDOWS_DEBUG printout(integ.evalCount, "integ eval count"); #endif x = new_real(integ.sum * integ.b * 0.75); y = new_real(integ.eps); if (x == NULL || y == NULL) { free_vartype(x); free_vartype(y); return ERR_INSUFFICIENT_MEMORY; } flags.f.trace_print = 0; recall_two_results(x, y); flags.f.trace_print = saved_trace; current_prgm = integ.prev_prgm; pc = integ.prev_pc; if (!integ.keep_running) { char buf[22]; int bufptr = 0; string2buf(buf, 22, &bufptr, "\003=", 2); bufptr += vartype2string(x, buf + bufptr, 22 - bufptr); clear_row(0); draw_string(0, 0, buf, bufptr); flush_display(); flags.f.message = 1; flags.f.two_line_message = 0; if (flags.f.trace_print && flags.f.printer_exists) print_wide(buf, 2, buf + 2, bufptr - 2); return ERR_STOP; } else return ERR_NONE; } /* approximate integral of `f' between `a' and `b' subject to a given * error. Use Romberg method with refinement substitution, x = (3u-u^3)/2 * which prevents endpoint evaluation and causes non-uniform sampling. */ int return_to_integ(int failure) { switch (integ.state) { case 0: return ERR_INTERNAL_ERROR; case 1: integ.state = 2; loop1: integ.p = integ.h / 2 - 1; integ.sum = 0.0; integ.i = 0; loop2: integ.t = 1 - integ.p * integ.p; integ.u = integ.p + integ.t * integ.p / 2; integ.u = (integ.u * integ.b + integ.b) / 2 + integ.a; ++integ.evalCount; return call_integ_fn(); case 2: if (!failure && reg_x->type == TYPE_REAL) integ.sum += integ.t * ((vartype_real *) reg_x)->x; integ.p += integ.h; if (++integ.i < integ.nsteps) goto loop2; // update integral moving resuslt integ.prev_int = (integ.prev_int + integ.sum*integ.h)/2; integ.s[integ.k++] = integ.prev_int; if (integ.n >= ROMB_K-1) { int i, m; int ns = ROMB_K-1; phloat dm = 1; for (i = 0; i < ROMB_K; ++i) integ.c[i] = integ.s[i]; integ.sum = integ.s[ns]; for (m = 1; m < ROMB_K; ++m) { dm /= 4; for (i = 0; i < ROMB_K-m; ++i) integ.c[i] = (integ.c[i+1]-integ.c[i]*dm*4)/(1-dm); integ.eps = integ.c[--ns]*dm; integ.sum += integ.eps; } integ.eps = fabs(integ.eps); if (integ.eps <= integ.acc*fabs(integ.sum)) { // done! return finish_integ(); } for (i = 0; i < ROMB_K-1; ++i) integ.s[i] = integ.s[i+1]; integ.k = ROMB_K-1; } integ.nsteps <<= 1; integ.h /= 2.0; if (++integ.n >= ROMB_MAX) return finish_integ(); // too many goto loop1; default: return ERR_INTERNAL_ERROR; } } free42-nologo-1.4.77/common/core_math1.h000644 000765 000024 00000003103 12110237247 020311 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_MATH1_H #define CORE_MATH1_H 1 #include "free42.h" #include "core_phloat.h" bool persist_math(); bool unpersist_math(bool discard); void reset_math(); void put_shadow(const char *name, int length, phloat value); int get_shadow(const char *name, int length, phloat *value); void remove_shadow(const char *name, int length); void set_solve_prgm(const char *name, int length); int start_solve(const char *name, int length, phloat x1, phloat x2); int return_to_solve(int failure); void set_integ_prgm(const char *name, int length); void get_integ_prgm(char *name, int *length); void set_integ_var(const char *name, int length); void get_integ_var(char *name, int *length); int start_integ(const char *name, int length); int return_to_integ(int failure); #endif free42-nologo-1.4.77/common/core_math2.cc000644 000765 000024 00000040671 12110237247 020463 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include "core_globals.h" #include "core_math2.h" phloat math_random() { #ifdef BCD_MATH const Phloat n1(BCDFloat(9821, 0, 0, 0, 0, 0, 0, 1)); const Phloat n2(BCDFloat(2113, 2700, 0, 0, 0, 0, 0, 0)); random_number = random_number * n1 + n2; #else random_number = random_number * 9821 + 0.211327; #endif random_number -= floor(random_number); return random_number; } int math_asinh(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* TODO: review; and deal with overflows in intermediate results */ phloat are, aim, br, bphi; /* If re(x)<0, we calculate asinh(x)=-asinh(-x); this avoids the loss of * significance in x+sqrt(x^2+1) when x is large and sqrt(x^2+1) approaches * -x. */ int neg = xre < 0; if (neg) { xre = -xre; xim = -xim; } /* a = x ^ 2 + 1 */ are = xre * xre - xim * xim + 1; aim = 2 * xre * xim; /* b = sqrt(a) */ br = sqrt(hypot(are, aim)); bphi = atan2(aim, are) / 2; /* a = b + x */ sincos(bphi, &aim, &are); are = are * br + xre; aim = aim * br + xim; /* y = log(a) */ *yre = log(hypot(are, aim)); *yim = atan2(aim, are); if (neg) { *yre = -*yre; *yim = -*yim; } return ERR_NONE; } int math_acosh(phloat xre, phloat xim, phloat *yre, phloat *yim) { /* TODO: review; and deal with overflows in intermediate results */ phloat ar, aphi, are, aim, br, bphi, bre, bim, cre, cim; /* a = sqrt(x + 1) */ ar = sqrt(hypot(xre + 1, xim)); aphi = atan2(xim, xre + 1) / 2; sincos(aphi, &aim, &are); are *= ar; aim *= ar; /* b = sqrt(x - 1) */ br = sqrt(hypot(xre - 1, xim)); bphi = atan2(xim, xre - 1) / 2; sincos(bphi, &bim, &bre); bre *= br; bim *= br; /* c = x + a * b */ cre = xre + are * bre - aim * bim; cim = xim + are * bim + aim * bre; /* y = log(c) */ *yre = log(hypot(cre, cim)); *yim = atan2(cim, cre); return ERR_NONE; } int math_atanh(phloat xre, phloat xim, phloat *yre, phloat *yim) { phloat are, aim, bre, bim, cre, cim, h; /* TODO: review, and deal with overflows in intermediate results */ if (xim == 0 && (xre == 1 || xre == -1)) return ERR_INVALID_DATA; /* a = 1 + x */ are = 1 + xre; aim = xim; /* b = 1 - x */ bre = 1 - xre; bim = - xim; /* c = a / b */ h = hypot(bre, bim); cre = (are * bre + aim * bim) / h / h; cim = (aim * bre - are * bim) / h / h; /* y = log(c) / 2 */ *yre = log(hypot(cre, cim)) / 2; *yim = atan2(cim, cre) / 2; /* Theoretically, you could go out of range, but in practice, * you can't get close enough to the critical values to cause * trouble. */ return ERR_NONE; } #ifdef BCD_MATH int math_gamma(phloat phx, phloat *phgamma) { if (phx == 0 || (phx < 0 && phx == floor(phx))) return ERR_INVALID_DATA; *phgamma = gamma(phx); int inf = p_isinf(*phgamma); if (inf != 0) { if (flags.f.range_error_ignore) *phgamma = inf < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } return ERR_NONE; } #else // BCD_MATH /**************************************************************/ /* The following is code to compute the gamma function, */ /* copied from the GNU C Library, version 2.2.5, and modified */ /* to conform to the coding conventions used in Free42. */ /* Dependencies on the IEEE-754 format have been removed. */ /* It is included here because on PalmOS, MathLib does */ /* not provide gamma at all, and on Linux, I'm not sure */ /* how it works (the lack of documentation does not help!). */ /* Better safe than sorry, and it's not that big anyway. */ /**************************************************************/ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ /* __ieee754_lgamma_r(x, signgamp) * Reentrant version of the logarithm of the Gamma function * with user provide pointer for the sign of Gamma(x). * * Method: * 1. Argument Reduction for 0 < x <= 8 * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may * reduce x to a number in [1.5,2.5] by * lgamma(1+s) = log(s) + lgamma(s) * for example, * lgamma(7.3) = log(6.3) + lgamma(6.3) * = log(6.3*5.3) + lgamma(5.3) * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) * 2. Polynomial approximation of lgamma around its * minimun ymin=1.461632144968362245 to maintain monotonicity. * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use * Let z = x-ymin; * lgamma(x) = -1.214862905358496078218 + z^2*poly(z) * where * poly(z) is a 14 degree polynomial. * 2. Rational approximation in the primary interval [2,3] * We use the following approximation: * s = x-2.0; * lgamma(x) = 0.5*s + s*P(s)/Q(s) * with accuracy * |P/Q - (lgamma(x)-0.5s)| < 2**-61.71 * Our algorithms are based on the following observation * * zeta(2)-1 2 zeta(3)-1 3 * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... * 2 3 * * where Euler = 0.5771... is the Euler constant, which is very * close to 0.5. * * 3. For x>=8, we have * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... * (better formula: * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) * Let z = 1/x, then we approximation * f(z) = lgamma(x) - (x-0.5)(log(x)-1) * by * 3 5 11 * w = w0 + w1*z + w2*z + w3*z + ... + w6*z * where * |w - f(z)| < 2**-58.74 * * 4. For negative x, since (G is gamma function) * -x*G(-x)*G(x) = pi/sin(pi*x), * we have * G(x) = pi/(sin(pi*x)*(-x)*G(-x)) * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 * Hence, for x<0, signgam = sign(sin(pi*x)) and * lgamma(x) = log(|Gamma(x)|) * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); * Note: one should avoid compute pi*(-x) directly in the * computation of sin(pi*(-x)). * * 5. Special Cases * lgamma(2+s) ~ s*(1-Euler) for tiny s * lgamma(1)=lgamma(2)=0 * lgamma(x) ~ -log(x) for tiny x * lgamma(0) = lgamma(inf) = inf * lgamma(-integer) = +-inf * */ /* NOTE (ThO): if the 'const' in this code is not commented out, the * PalmOS build of Free42 malfunctions in GAMMA (wrong results, * memory access weirdness). This isn't the first time I've noticed * m68k-palmos-gcc acting weird in the presence of const globals. * It probably puts them in the wrong section or something. I should debug * this, and add section attributes where needed, but since Free42 doesn't * use that much global space anyway, and the variables below are only * something like 500 bytes altogether, I just work around the issue by * putting them in the read/write global area. */ static /*const*/ double half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ a0 = 7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */ a1 = 3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */ a2 = 6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */ a3 = 2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */ a4 = 7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */ a5 = 2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */ a6 = 1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */ a7 = 5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */ a8 = 2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */ a9 = 1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */ a10 = 2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */ a11 = 4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */ tc = 1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */ tf = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */ /* tt = -(tail of tf) */ tt = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */ t0 = 4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */ t1 = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */ t2 = 6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */ t3 = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */ t4 = 1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */ t5 = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */ t6 = 6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */ t7 = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */ t8 = 2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */ t9 = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */ t10 = 8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */ t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */ t12 = 3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */ t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */ t14 = 3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */ u0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ u1 = 6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */ u2 = 1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */ u3 = 9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */ u4 = 2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */ u5 = 1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */ v1 = 2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */ v2 = 2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */ v3 = 7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */ v4 = 1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */ v5 = 3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */ s0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ s1 = 2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */ s2 = 3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */ s3 = 1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */ s4 = 2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */ s5 = 1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */ s6 = 3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */ r1 = 1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */ r2 = 7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */ r3 = 1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */ r4 = 1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */ r5 = 7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */ r6 = 7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */ w0 = 4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */ w1 = 8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */ w2 = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */ w3 = 7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */ w4 = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */ w5 = 8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */ w6 = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */ static /*const*/ double zero= 0.00000000000000000000e+00; static double sin_pi(double x) { double y,z, absx; int4 n; absx = x < 0 ? -x : x; if(absx < 0.25) return sin(pi*x); y = -x; /* x is assume negative */ /* * argument reduction, make sure inexact flag not raised if input * is an integer */ z = floor(y); if(z!=y) { /* inexact anyway */ y *= 0.5; y = 2.0*(y - floor(y)); /* y = |x| mod 2.0 */ n = (int4) (y*4.0); } else { if(absx + 1 == absx) { y = zero; n = 0; /* y must be even */ } else { n = ((int) y) & 1; y = n; n<<= 2; } } switch (n) { case 0: y = sin(pi*y); break; case 1: case 2: y = cos(pi*(0.5-y)); break; case 3: case 4: y = sin(pi*(one-y)); break; case 5: case 6: y = -cos(pi*(y-1.5)); break; default: y = sin(pi*(y-2.0)); break; } return -y; } static int math_lgamma(double x, double *gam, int *sgngam) { double t,y,z,nadj,p,p1,p2,p3,q,r,w; double absx; int i, neg = 0; /* purge off +-inf, NaN, +-0, and negative arguments */ /* ThO: I removed the code that deals with inf and nan (Free42 does * not allow such values to propagate, so they should never be presented * as arguments to a function; if they are, what needs to be fixed is * the function that *returns* inf or nan, not the way functions deal * with such incoming arguments. */ *sgngam = 1; if (x == 0) return ERR_INVALID_DATA; absx = x < 0 ? -x : x; if (absx < 8.47032947254300339068e-22) { /* |x|<2**-70, return -log(|x|) */ *sgngam = -1; *gam = -log(absx); return ERR_NONE; } if (x < 0) { if (absx == floor(absx)) /* -integer */ return ERR_INVALID_DATA; t = sin_pi(x); if (t == zero) return ERR_INVALID_DATA; if (t < 0) { *sgngam = -1; nadj = log(pi / (t*x)); } else nadj = log(pi / (-t*x)); x = -x; neg = 1; } /* purge off 1 and 2 */ if (x == 1 || x == 2) r = 0; /* for x < 2.0 */ else if (x < 2) { if(x <= 0.900000095367431529603) { /* lgamma(x) = lgamma(x+1)-log(x) */ r = -log(x); if(x >= 0.7315998077392578125) {y = one-x; i= 0;} else if(x >= 0.231639981269836425781) {y= x-(tc-one); i=1;} else {y = x; i=2;} } else { r = zero; if(x >= 1.73163127899169921875) {y=2.0-x;i=0;} /* [1.7316,2] */ else if(x >= 1.231632232666015625) {y=x-tc;i=1;} /* [1.23,1.73] */ else {y=x-one;i=2;} } switch(i) { case 0: z = y*y; p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10)))); p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11))))); p = y*p1+p2; r += (p-0.5*y); break; case 1: z = y*y; w = z*y; p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */ p2 = t1+w*(t4+w*(t7+w*(t10+w*t13))); p3 = t2+w*(t5+w*(t8+w*(t11+w*t14))); p = z*p1-(tt-w*(p2+y*p3)); r += (tf + p); break; case 2: p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5))))); p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5)))); r += (-0.5*y + p1/p2); } } else if(x < 8) { /* x < 8.0 */ i = (int4)x; t = zero; y = x-(double)i; p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))); q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6))))); r = half*y+p/q; z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ switch(i) { case 7: z *= (y+6.0); /* FALLTHRU */ case 6: z *= (y+5.0); /* FALLTHRU */ case 5: z *= (y+4.0); /* FALLTHRU */ case 4: z *= (y+3.0); /* FALLTHRU */ case 3: z *= (y+2.0); /* FALLTHRU */ r += log(z); break; } /* 8.0 <= x < 2**58 */ } else if (x < 288230376151711744.0) { t = log(x); z = one/x; y = z*z; w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6))))); r = (x-half)*(t-one)+w; } else /* 2**58 <= x <= inf */ r = x*(log(x)-one); if (neg) r = nadj - r; *gam = r; return ERR_NONE; } /***************************************************************/ /* Here ends the borrowed GNU C Library code (Gamma function). */ /***************************************************************/ int math_gamma(phloat phx, phloat *phgamma) { double x = to_double(phx); double gam; double lgam; int sign, err; if (x == 0 || (x < 0 && x == floor(x))) return ERR_INVALID_DATA; err = math_lgamma(x, &lgam, &sign); if (err != ERR_NONE) return err; gam = exp(lgam); if (p_isinf(gam)) { if (flags.f.range_error_ignore) *phgamma = sign < 0 ? NEG_HUGE_PHLOAT : POS_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } else { *phgamma = sign < 0 ? -gam : gam; } return ERR_NONE; } #endif // BCD_MATH free42-nologo-1.4.77/common/core_math2.h000644 000765 000024 00000002220 12110237247 020311 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_MATH2_H #define CORE_MATH2_H 1 #include "core_phloat.h" phloat math_random(); int math_asinh(phloat xre, phloat xim, phloat *yre, phloat *yim); int math_acosh(phloat xre, phloat xim, phloat *yre, phloat *yim); int math_atanh(phloat xre, phloat xim, phloat *yre, phloat *yim); int math_gamma(phloat x, phloat *gamma); #endif free42-nologo-1.4.77/common/core_phloat.cc000644 000765 000024 00000134415 12110237247 020737 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include #include "core_phloat.h" #include "core_helpers.h" #include "shell.h" #include "bcd.h" #include "bcdmath.h" phloat POS_HUGE_PHLOAT; phloat NEG_HUGE_PHLOAT; phloat POS_TINY_PHLOAT; phloat NEG_TINY_PHLOAT; #ifdef BCD_MATH void phloat_init() { POS_HUGE_PHLOAT.bcd.d_[0] = 9999; POS_HUGE_PHLOAT.bcd.d_[1] = 9999; POS_HUGE_PHLOAT.bcd.d_[2] = 9999; POS_HUGE_PHLOAT.bcd.d_[3] = 9999; POS_HUGE_PHLOAT.bcd.d_[4] = 9999; POS_HUGE_PHLOAT.bcd.d_[5] = 9999; POS_HUGE_PHLOAT.bcd.d_[6] = 9000; POS_HUGE_PHLOAT.bcd.d_[7] = 2500; NEG_HUGE_PHLOAT = -POS_HUGE_PHLOAT; POS_TINY_PHLOAT.bcd.d_[0] = 1; POS_TINY_PHLOAT.bcd.d_[1] = 0; POS_TINY_PHLOAT.bcd.d_[2] = 0; POS_TINY_PHLOAT.bcd.d_[3] = 0; POS_TINY_PHLOAT.bcd.d_[4] = 0; POS_TINY_PHLOAT.bcd.d_[5] = 0; POS_TINY_PHLOAT.bcd.d_[6] = 0; POS_TINY_PHLOAT.bcd.d_[7] = (unsigned short) -2499; NEG_TINY_PHLOAT = -POS_TINY_PHLOAT; } void phloat_cleanup() { // Nothing to do } int string2phloat(const char *buf, int buflen, phloat *d) { /* Convert string to phloat. * Return values: * 0: no error * 1: positive overflow * 2: negative overflow * 3: positive underflow * 4: negative underflow * 5: other error */ // First, convert from HP-42S format to bcdfloat format: // strip thousands separators, convert comma to dot if // appropriate, and convert char(24) to 'E'. // Also, reject numbers with more than 12 digits in the mantissa. char buf2[100]; int buflen2 = 0; char sep = flags.f.decimal_point ? ',' : '.'; int mantdigits = 0; bool in_mant = true; bool zero = true; for (int i = 0; i < buflen; i++) { char c = buf[i]; if (c == sep) continue; if (c == ',') c = '.'; else if (c == 24) { c = 'E'; in_mant = false; } else if (in_mant && c >= '0' && c <= '9') { if (++mantdigits > 12) return 5; if (c != '0') zero = false; } buf2[buflen2++] = c; } if (!in_mant && mantdigits == 0) { // A number like "E4"; not something the HP-41 or HP42S (or Free42, for // that matter) will actually allow you to enter into a program, but // the real calcs do accept this kind of thing (synthetically), and // supply a mantissa of 1 (just like when you start number entry by // pressing EEX (HP-41) or E (HP-42S). for (int i = buflen2 - 1; i >= 0; i--) buf2[i + 1] = buf2[i]; buf2[0] = '1'; buflen2++; } buf2[buflen2] = 0; BCDFloat b(buf2); if (b.isInf()) return b.neg() ? 2 : 1; if (!zero && b.isZero()) return b.neg() ? 4 : 3; d->bcd = b; return 0; } /* public */ Phloat::Phloat(int numer, int denom) { BCDFloat n(numer); BCDFloat d(denom); BCDFloat::div(&n, &d, &bcd); } /* public */ Phloat::Phloat(int i) { bcd = BCDFloat(i); } /* public */ Phloat::Phloat(int8 i) : bcd(i) { // Nothing else to do } /* public */ Phloat::Phloat(double d) { bcd = double2bcd(d); } /* public */ Phloat::Phloat(const Phloat &p) { bcd = p.bcd; } /* public */ Phloat Phloat::operator=(int i) { bcd = BCDFloat(i); return *this; } /* public */ Phloat Phloat::operator=(int8 i) { bcd = BCDFloat(i); return *this; } /* public */ Phloat Phloat::operator=(double d) { bcd = double2bcd(d); return *this; } /* public */ Phloat Phloat::operator=(Phloat p) { bcd = p.bcd; return *this; } /* public */ bool Phloat::operator==(Phloat p) const { return BCDFloat::equal(&bcd, &p.bcd); } /* public */ bool Phloat::operator!=(Phloat p) const { return !BCDFloat::equal(&bcd, &p.bcd); } /* public */ bool Phloat::operator<(Phloat p) const { return BCDFloat::lt(&bcd, &p.bcd); } /* public */ bool Phloat::operator<=(Phloat p) const { return BCDFloat::le(&bcd, &p.bcd); } /* public */ bool Phloat::operator>(Phloat p) const { return BCDFloat::gt(&bcd, &p.bcd); } /* public */ bool Phloat::operator>=(Phloat p) const { return BCDFloat::ge(&bcd, &p.bcd); } /* public */ Phloat Phloat::operator-() const { Phloat res(*this); res.bcd.negate(); return res; } /* public */ Phloat Phloat::operator*(Phloat p) const { Phloat res; BCDFloat::mul(&bcd, &p.bcd, &res.bcd); return res; } /* public */ Phloat Phloat::operator/(Phloat p) const { Phloat res; BCDFloat::div(&bcd, &p.bcd, &res.bcd); return res; } /* public */ Phloat Phloat::operator+(Phloat p) const { Phloat res; BCDFloat::add(&bcd, &p.bcd, &res.bcd); return res; } /* public */ Phloat Phloat::operator-(Phloat p) const { Phloat res; BCDFloat::sub(&bcd, &p.bcd, &res.bcd); return res; } /* public */ Phloat Phloat::operator*=(Phloat p) { BCDFloat temp; BCDFloat::mul(&bcd, &p.bcd, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator/=(Phloat p) { BCDFloat temp; BCDFloat::div(&bcd, &p.bcd, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator+=(Phloat p) { BCDFloat temp; BCDFloat::add(&bcd, &p.bcd, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator-=(Phloat p) { BCDFloat temp; BCDFloat::sub(&bcd, &p.bcd, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator++() { // prefix const BCDFloat one(1); BCDFloat temp; BCDFloat::add(&bcd, &one, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator++(int) { // postfix Phloat old = *this; const BCDFloat one(1); BCDFloat temp; BCDFloat::add(&bcd, &one, &temp); bcd = temp; return old; } /* public */ Phloat Phloat::operator--() { // prefix const BCDFloat one(1); BCDFloat temp; BCDFloat::sub(&bcd, &one, &temp); bcd = temp; return *this; } /* public */ Phloat Phloat::operator--(int) { // postfix Phloat old = *this; const BCDFloat one(1); BCDFloat temp; BCDFloat::sub(&bcd, &one, &temp); bcd = temp; return old; } int p_isinf(Phloat p) { if (p.bcd.isInf()) return p.bcd.neg() ? -1 : 1; else return 0; } int p_isnan(Phloat p) { return p.bcd.isNan() ? 1 : 0; } int to_digit(Phloat p) { BCD res = trunc(fmod(BCD(p.bcd), 10)); return ifloor(res); } static int8 mant(const BCDFloat &b) { int8 m = 0; int e = b.exp(); if (e > P) e = P; for (int i = 0; i < e; i++) m = m * 10000 + b.d_[i]; if (b.neg()) m = -m; return m; } char to_char(Phloat p) { return (char) mant(p.bcd); } int to_int(Phloat p) { return (int) mant(p.bcd); } int4 to_int4(Phloat p) { return (int4) mant(p.bcd); } int8 to_int8(Phloat p) { return (int8) mant(p.bcd); } double to_double(Phloat p) { return bcd2double(p.bcd, false); } Phloat sin(Phloat p) { Phloat res; res.bcd = sin(BCD(p.bcd))._v; return res; } Phloat cos(Phloat p) { Phloat res; res.bcd = cos(BCD(p.bcd))._v; return res; } Phloat tan(Phloat p) { Phloat res; res.bcd = tan(BCD(p.bcd))._v; return res; } Phloat asin(Phloat p) { Phloat res; res.bcd = asin(BCD(p.bcd))._v; return res; } Phloat acos(Phloat p) { Phloat res; res.bcd = acos(BCD(p.bcd))._v; return res; } Phloat atan(Phloat p) { Phloat res; res.bcd = atan(BCD(p.bcd))._v; return res; } void sincos(Phloat phi, Phloat *s, Phloat *c) { BCD p(phi.bcd); s->bcd = sin(p)._v; c->bcd = cos(p)._v; } Phloat hypot(Phloat x, Phloat y) { Phloat res; res.bcd = hypot(BCD(x.bcd), BCD(y.bcd))._v; return res; } Phloat atan2(Phloat x, Phloat y) { Phloat res; res.bcd = atan2(BCD(x.bcd), BCD(y.bcd))._v; return res; } Phloat sinh(Phloat p) { // (exp(x)-exp(-x))/2 BCDFloat temp1 = exp(BCD(p.bcd))._v; BCDFloat temp2 = exp(BCD((-p).bcd))._v; BCDFloat temp3; BCDFloat::sub(&temp1, &temp2, &temp3); const BCDFloat two(2); Phloat res; BCDFloat::div(&temp3, &two, &res.bcd); return res; } Phloat cosh(Phloat p) { // (exp(x)+exp(-x))/2 BCDFloat temp1 = exp(BCD(p.bcd))._v; BCDFloat temp2 = exp(BCD((-p).bcd))._v; BCDFloat temp3; BCDFloat::add(&temp1, &temp2, &temp3); const BCDFloat two(2); Phloat res; BCDFloat::div(&temp3, &two, &res.bcd); return res; } Phloat tanh(Phloat p) { // (exp(x)-exp(-x))/(exp(x)+exp(-x)) BCDFloat temp1 = exp(BCD(p.bcd))._v; BCDFloat temp2 = exp(BCD((-p).bcd))._v; BCDFloat temp3; BCDFloat::sub(&temp1, &temp2, &temp3); if (temp3.isInf()) return temp3.neg() ? -1 : 1; BCDFloat temp4; BCDFloat::add(&temp1, &temp2, &temp4); Phloat res; BCDFloat::div(&temp3, &temp4, &res.bcd); return res; } Phloat asinh(Phloat p) { // log(sqrt(x^2+1)+x) BCDFloat temp1; BCDFloat::mul(&p.bcd, &p.bcd, &temp1); BCDFloat temp2; const BCDFloat one(1); BCDFloat::add(&temp1, &one, &temp2); temp1 = sqrt(BCD(temp2))._v; BCDFloat::add(&temp1, &p.bcd, &temp2); Phloat res; res.bcd = log(BCD(temp2))._v; return res; } Phloat acosh(Phloat p) { // log(sqrt(x^2-1)+x) BCDFloat temp1; BCDFloat::mul(&p.bcd, &p.bcd, &temp1); BCDFloat temp2; const BCDFloat one(1); BCDFloat::sub(&temp1, &one, &temp2); temp1 = sqrt(BCD(temp2))._v; BCDFloat::add(&temp1, &p.bcd, &temp2); Phloat res; res.bcd = log(BCD(temp2))._v; return res; } Phloat atanh(Phloat p) { // log((1+x)/(1-x))/2 const BCDFloat one(1); BCDFloat temp1, temp2, temp3; BCDFloat::add(&one, &p.bcd, &temp1); BCDFloat::sub(&one, &p.bcd, &temp2); BCDFloat::div(&temp1, &temp2, &temp3); temp1 = log(BCD(temp3))._v; Phloat res; const BCDFloat two(2); BCDFloat::div(&temp1, &two, &res.bcd); return res; } Phloat log(Phloat p) { Phloat res; res.bcd = log(BCD(p.bcd))._v; return res; } Phloat log1p(Phloat p) { Phloat res; res.bcd = ln1p(BCD(p.bcd))._v; return res; } Phloat log10(Phloat p) { Phloat res; res.bcd = log10(BCD(p.bcd))._v; return res; } Phloat exp(Phloat p) { Phloat res; res.bcd = exp(BCD(p.bcd))._v; return res; } Phloat expm1(Phloat p) { Phloat res; res.bcd = expm1(BCD(p.bcd))._v; return res; } Phloat gamma(Phloat p) { --p; Phloat res; res.bcd = gammaFactorial(BCD(p.bcd))._v; return res; } Phloat sqrt(Phloat p) { Phloat res; res.bcd = sqrt(BCD(p.bcd))._v; return res; } Phloat fmod(Phloat x, Phloat y) { Phloat res; res.bcd = fmod(BCD(x.bcd), BCD(y.bcd))._v; return res; } Phloat fabs(Phloat p) { Phloat res(p); if (!res.bcd.isNan() && res.bcd.neg()) res.bcd.negate(); return res; } Phloat pow(Phloat x, Phloat y) { Phloat res; if (!y.bcd.isSpecial()) { int iy = BCDFloat::ifloor(&y.bcd); BCDFloat by(iy); if (BCDFloat::equal(&y.bcd, &by)) { res.bcd = pow(BCD(x.bcd), iy)._v; return res; } } res.bcd = pow(BCD(x.bcd), BCD(y.bcd))._v; return res; } Phloat floor(Phloat p) { Phloat res; BCDFloat::floor(&p.bcd, &res.bcd); return res; } Phloat operator*(int x, Phloat y) { Phloat res; BCDFloat bx(x); BCDFloat::mul(&bx, &y.bcd, &res.bcd); return res; } Phloat operator/(int x, Phloat y) { Phloat res; BCDFloat bx(x); BCDFloat::div(&bx, &y.bcd, &res.bcd); return res; } Phloat operator/(double x, Phloat y) { Phloat res; BCDFloat bx = double2bcd(x); BCDFloat::div(&bx, &y.bcd, &res.bcd); return res; } Phloat operator+(int x, Phloat y) { Phloat res; BCDFloat bx(x); BCDFloat::add(&bx, &y.bcd, &res.bcd); return res; } Phloat operator-(int x, Phloat y) { Phloat res; BCDFloat bx(x); BCDFloat::sub(&bx, &y.bcd, &res.bcd); return res; } bool operator==(int4 x, Phloat y) { BCDFloat bx(x); return BCDFloat::equal(&bx, &y.bcd); } Phloat PI(BCDFloat(3, 1415, 9265, 3589, 7932, 3846, 2643, 1)); BCDFloat double2bcd(double d, bool round /* = false */) { BCDFloat res(d); if (round && !res.isSpecial()) { // This is used when converting programs from a Free42 Binary state // file. Number literals in programs are rounded to 12 digits, so // what you see really is what you get; without this hack, you'd // get stuff like 0.9 turning into 0.8999999+ but still *looking* // like 0.9! int i; for (i = 4; i < P; i++) res.d_[i] = 0; unsigned short s = res.d_[0]; unsigned short d; if (s < 10) d = 10; else if (s < 100) d = 100; else if (s < 1000) d = 1000; else d = 10000; s = res.d_[3]; unsigned short r = s % d; if (r >= d >> 1) s += d; s -= r; bool carry = s >= 10000; if (carry) s -= 10000; res.d_[3] = s; for (i = 2; carry && i >= 0; i--) { s = res.d_[i] + 1; carry = s >= 10000; if (carry) s -= 10000; res.d_[i] = s; } if (carry) { for (i = 3; i >= 0; i--) res.d_[i + 1] = res.d_[i]; res.d_[0] = 1; res.d_[P]++; // No need to check if the exponent is overflowing; the range // of 'double' is too small to cause such problems here. } } return res; } double bcd2double(BCDFloat b, bool old_bcd) { if (old_bcd) bcdfloat_old2new(b.d_); double zero = 0; bool neg = b.neg(); #if defined(WINDOWS) && !defined(__GNUC__) // No support for NaN or infinities if (b.isNan()) return HUGE_VAL; else if (b.isInf()) return neg ? -HUGE_VAL : HUGE_VAL; #else if (b.isNan()) return 0 / zero; else if (b.isInf()) return neg ? -1 / zero : 1 / zero; #endif if (b.d_[0] == 0) return 0; // Get rid of neg, nan, and inf flags in exponent, and extend // sign of the remaining 13 bits short exp = (((short) b.d_[P]) << 3) >> 3; // Make sure we get exact results for integers if (exp > 0 && exp <= 4) { int j; for (j = exp; j < P; j++) if (b.d_[j] != 0) goto noninteger; int8 n = 0; for (j = 0; j < exp; j++) n = n * 10000 + b.d_[j]; return (double) (neg ? -n : n); } // TODO: Using a table-based conversion algorithm, like the one I use in // the binary version of string2phloat, I could get better accuracy. // The current code returns an inexact result for 23.5, for instance // (VC++), while the number *is* exactly representable in a 'double'. noninteger: double res = 0; for (int i = 0; i < P; i++) res = res * 10000 + b.d_[i]; if (neg) res = -res; return res * pow(10000.0, (double) (exp - P)); } #else // BCD_MATH /* binary-to-bcd conversion tables */ static int min_pow2; static int max_pow2; static char *pos_pow2mant; static int *pos_pow2exp; static char *neg_pow2mant; static int *neg_pow2exp; static shell_bcd_table_struct *bcdtab = NULL; /*********************/ /* Private functions */ /*********************/ static double POS_HUGE_DOUBLE; static double NEG_HUGE_DOUBLE; static double POS_TINY_DOUBLE; static double NEG_TINY_DOUBLE; static void bcd_add(char *dst_mant, int *dst_exp, const char *src_mant, int src_exp); static int bcd_cmp(const char *dst_mant, int dst_exp, const char *src_mant, int src_exp); static void bcd_sub(char *dst_mant, int *dst_exp, const char *src_mant, int src_exp); /********************/ /* Public functions */ /********************/ void phloat_init() { double d, pd, nd, tmp; char temp_bcd_mantissa[20]; int temp_bcd_exponent; int e, i; int carry; uint4 n1, n2, n3, n4, size; char *base; /**************************************************************/ /* First, try if the BCD table is available without doing any */ /* hard work. If it isn't, we'll compute it from scratch and */ /* try to save it so we don't have to do this again later. */ /**************************************************************/ shell_bcd_table_struct *bcdtab = shell_get_bcd_table(); if (bcdtab != NULL) { char *base = (char *) (bcdtab + 1); POS_HUGE_DOUBLE = bcdtab->pos_huge_double; NEG_HUGE_DOUBLE = bcdtab->neg_huge_double; POS_TINY_DOUBLE = bcdtab->pos_tiny_double; NEG_TINY_DOUBLE = bcdtab->neg_tiny_double; POS_HUGE_PHLOAT = POS_HUGE_DOUBLE; NEG_HUGE_PHLOAT = NEG_HUGE_DOUBLE; POS_TINY_PHLOAT = POS_TINY_DOUBLE; NEG_TINY_PHLOAT = NEG_TINY_DOUBLE; max_pow2 = bcdtab->max_pow2; min_pow2 = bcdtab->min_pow2; pos_pow2mant = base; pos_pow2exp = (int *) (base + bcdtab->pos_pow2exp_offset); neg_pow2mant = base + bcdtab->neg_pow2mant_offset; neg_pow2exp = (int *) (base + bcdtab->neg_pow2exp_offset); return; } /****************************************************************/ /* Since I can't find a portable API or macro that gives me */ /* the largest positive and negative numbers (no, I don't want */ /* to hard-code IEEE-754 -- I'm paranoid), I just find them out */ /* myself. */ /****************************************************************/ pd = 1; while (1) { tmp = pd * 2; if (isinf(tmp) || tmp <= pd) break; pd = tmp; } POS_HUGE_DOUBLE = pd; while (1) { pd = pd / 2; tmp = POS_HUGE_DOUBLE + pd; if (isinf(tmp)) continue; if (POS_HUGE_DOUBLE == tmp) break; POS_HUGE_DOUBLE = tmp; } nd = -1; while (1) { tmp = nd * 2; if (isinf(tmp) || tmp >= nd) break; nd = tmp; } NEG_HUGE_DOUBLE = nd; while (1) { nd = nd / 2; tmp = NEG_HUGE_DOUBLE + nd; if (isinf(tmp)) continue; if (NEG_HUGE_DOUBLE == tmp) break; NEG_HUGE_DOUBLE = tmp; } /*************************************************************************/ /* I need the smallest nonzero doubles so I have something to substitute */ /* when I encounter something like 1e-499 in a "raw" file I'm importing. */ /* Normally, underflows just go to zero, but in the case of a number */ /* literal in a program, that seems wrong. */ /*************************************************************************/ pd = 1; while (1) { tmp = pd / 2; #if defined(WINDOWS) && !defined(__GNUC__) /* Without this, Visual C++ 6.0 generates code that leaves the loop * one iteration too late. Looks like an optimizer bug or something. */ fabs(tmp); #endif if (tmp == 0) break; pd = tmp; } POS_TINY_DOUBLE = pd; nd = -1; while (1) { tmp = nd / 2; #if defined(WINDOWS) && !defined(__GNUC__) /* Without this, Visual C++ 6.0 generates code that leaves the loop * one iteration too late. Looks like an optimizer bug or something. */ fabs(tmp); #endif if (tmp == 0) break; nd = tmp; } NEG_TINY_DOUBLE = nd; /*****************************************************************/ /* Compute BCD representations of all representable powers of 2. */ /* These will be used for binary-to-decimal and decimal-to- */ /* binary conversions. */ /*****************************************************************/ d = 1; max_pow2 = 0; while (finite(d)) { max_pow2++; d *= 2; } /* Yes, I deliberately look for max_pow2 such that it is actually * the lowest power of 2 that is *too big* for a 'double'. Of course, * this value is never going to get used in double-to-bcd conversion, * but it is needed as a sentinel in the bcd-to-double conversion * (that code looks starts out by looking for the bcd value of the * highest nonzero bit in the number to be converted, and the process * of finding that value will overshoot the desired power by 1 before * settling on the right value). */ d = 1; min_pow2 = -1; while (d != 0) { min_pow2++; d /= 2; } n1 = 16 * (max_pow2 + 1); n2 = sizeof(int) * (max_pow2 + 1); n3 = 16 * min_pow2; n4 = sizeof(int) * min_pow2; size = sizeof(shell_bcd_table_struct) + n1 + n2 + n3 + n4; bcdtab = (shell_bcd_table_struct *) malloc(size); bcdtab->pos_huge_double = POS_HUGE_DOUBLE; bcdtab->neg_huge_double = NEG_HUGE_DOUBLE; bcdtab->pos_tiny_double = POS_TINY_DOUBLE; bcdtab->neg_tiny_double = NEG_TINY_DOUBLE; bcdtab->max_pow2 = max_pow2; bcdtab->min_pow2 = -min_pow2; bcdtab->pos_pow2exp_offset = n1; bcdtab->neg_pow2mant_offset = n1 + n2; bcdtab->neg_pow2exp_offset = n1 + n2 + n3; base = (char *) (bcdtab + 1); pos_pow2mant = (char *) base; pos_pow2exp = (int *) (base + n1); neg_pow2mant = (char *) (base + n1 + n2); neg_pow2exp = (int *) (base + n1 + n2 + n3); temp_bcd_mantissa[0] = 1; for (i = 1; i < 20; i++) temp_bcd_mantissa[i] = 0; temp_bcd_exponent = 0; for (e = 0; e <= max_pow2; e++) { if (e != 0) { int carry = 0; for (i = 19; i >= 0; i--) { char c = temp_bcd_mantissa[i] * 2 + carry; if (c < 10) { temp_bcd_mantissa[i] = c; carry = 0; } else { temp_bcd_mantissa[i] = c - 10; carry = 1; } } if (carry) { /* Need to shift mantissa 1 position to the right. * Apply rounding on last digit. */ int carry2 = 0; if (temp_bcd_mantissa[19] >= 5) { carry2 = 1; for (i = 18; i >= 0; i--) { char c = temp_bcd_mantissa[i] + carry2; if (c < 10) { temp_bcd_mantissa[i] = c; carry2 = 0; break; } else { temp_bcd_mantissa[i] = c - 10; carry2 = 1; } } } for (i = 19; i >= 1; i--) temp_bcd_mantissa[i] = temp_bcd_mantissa[i - 1]; temp_bcd_mantissa[0] = carry + carry2; temp_bcd_exponent++; } } carry = temp_bcd_mantissa[16] >= 5; for (i = 15; i >= 0; i--) { char c = temp_bcd_mantissa[i] + carry; if (c < 10) { pos_pow2mant[16 * e + i] = c; carry = 0; } else { pos_pow2mant[16 * e + i] = c - 10; carry = 1; } } pos_pow2exp[e] = temp_bcd_exponent; if (carry) { /* Don't shift, but redo the copy with a 1 digit offset. * If we would shift here, we would have to round the mantissa * twice (once for going from 20 digits to 16, then again for * losing a digit when dividing by 10). */ carry = temp_bcd_mantissa[17] >= 5; for (i = 15; i > 0; i--) { char c = temp_bcd_mantissa[i + 1] + carry; if (c < 10) { pos_pow2mant[16 * e + i] = c; carry = 0; } else { pos_pow2mant[16 * e + i] = c - 10; carry = 1; } } pos_pow2mant[16 * e] = carry; pos_pow2exp[e] = temp_bcd_exponent + 1; } } temp_bcd_mantissa[0] = 1; for (i = 1; i < 20; i++) temp_bcd_mantissa[i] = 0; temp_bcd_exponent = 0; for (e = 0; e < min_pow2; e++) { int carry = 0; for (i = 19; i >= 0; i--) { char c = temp_bcd_mantissa[i] * 5 + carry; if (c < 10) { temp_bcd_mantissa[i] = c; carry = 0; } else { temp_bcd_mantissa[i] = c % 10; carry = c / 10; } } temp_bcd_exponent--; if (carry) { /* Need to shift mantissa 1 position to the right. * Apply rounding on last digit. */ int carry2 = 0; if (temp_bcd_mantissa[19] >= 5) { carry2 = 1; for (i = 18; i >= 0; i--) { char c = temp_bcd_mantissa[i] + carry2; if (c < 10) { temp_bcd_mantissa[i] = c; carry2 = 0; break; } else { temp_bcd_mantissa[i] = c - 10; carry2 = 1; } } } for (i = 19; i >= 1; i--) temp_bcd_mantissa[i] = temp_bcd_mantissa[i - 1]; temp_bcd_mantissa[0] = carry + carry2; temp_bcd_exponent++; } carry = temp_bcd_mantissa[16] >= 5; for (i = 15; i >= 0; i--) { char c = temp_bcd_mantissa[i] + carry; if (c < 10) { neg_pow2mant[16 * e + i] = c; carry = 0; } else { neg_pow2mant[16 * e + i] = c - 10; carry = 1; } } neg_pow2exp[e] = temp_bcd_exponent; if (carry) { /* Don't shift, but redo the copy with a 1 digit offset. * If we would shift here, we would have to round the mantissa * twice (once for going from 20 digits to 16, then again for * losing a digit when dividing by 10). */ carry = temp_bcd_mantissa[17] >= 5; for (i = 15; i > 0; i--) { char c = temp_bcd_mantissa[i + 1] + carry; if (c < 10) { neg_pow2mant[16 * e + i] = c; carry = 0; } else { neg_pow2mant[16 * e + i] = c - 10; carry = 1; } } neg_pow2mant[16 * e] = carry; neg_pow2exp[e] = temp_bcd_exponent + 1; } } min_pow2 = -min_pow2; bcdtab = shell_put_bcd_table(bcdtab, size); base = (char *) (bcdtab + 1); pos_pow2mant = (char *) base; pos_pow2exp = (int *) (base + n1); neg_pow2mant = (char *) (base + n1 + n2); neg_pow2exp = (int *) (base + n1 + n2 + n3); POS_HUGE_PHLOAT = POS_HUGE_DOUBLE; NEG_HUGE_PHLOAT = NEG_HUGE_DOUBLE; POS_TINY_PHLOAT = POS_TINY_DOUBLE; NEG_TINY_PHLOAT = NEG_TINY_DOUBLE; } void phloat_cleanup() { shell_release_bcd_table(bcdtab); } static void bcd_add(char *dst_mant, int *dst_exp, const char *src_mant, int src_exp) { int i; if (dst_mant[0] == 0 || src_exp > *dst_exp + 16) { for (i = 0; i < 16; i++) dst_mant[i] = src_mant[i]; *dst_exp = src_exp; } else if (src_exp == *dst_exp) { int carry; add_matching: carry = 0; for (i = 15; i >= 0; i--) { char c = src_mant[i] + dst_mant[i] + carry; if (c < 10) { dst_mant[i] = c; carry = 0; } else { dst_mant[i] = c - 10; carry = 1; } } if (carry) { /* Need to shift mantissa 1 position to the right. * Apply rounding on last digit. */ int carry2 = 0; if (dst_mant[15] >= 5) { carry2 = 1; for (i = 14; i >= 0; i--) { char c = dst_mant[i] + carry2; if (c < 10) { dst_mant[i] = c; carry2 = 0; break; } else { dst_mant[i] = c - 10; carry2 = 1; } } } for (i = 15; i >= 1; i--) dst_mant[i] = dst_mant[i - 1]; dst_mant[0] = carry + carry2; (*dst_exp)++; } } else if (src_exp > *dst_exp) { /* The case that src_exp is greater than *dst_exp+16 is handled * as if dst==0; the case being handled here is src_exp-*dst_exp * between 1 and 16. */ int shift = src_exp - *dst_exp; /* Shift dst so its exponent matches src */ int carry = dst_mant[16 - shift] >= 5; for (i = 15; i >= 0; i--) dst_mant[i] = i < shift ? 0 : dst_mant[i - shift]; if (carry) { for (i = 15; i >= 0; i--) { char c = dst_mant[i] + carry; if (c < 10) { dst_mant[i] = c; carry = 0; break; } else { dst_mant[i] = c - 10; carry = 1; } } } /* Carry is always 0 here, because the leftmost digit * is 0 + carry, which never leads to the carry being propagated * further. */ *dst_exp = src_exp; /* Now that dst is aligned with src, proceed using the code * for the dst_exp == src_exp case. */ goto add_matching; } else /* src_exp < *dst_exp */ { int carry; int shift = *dst_exp - src_exp; if (shift > 16) /* The number being added is too small to matter */ return; carry = src_mant[16 - shift] >= 5; for (i = 15; i >= 0; i--) { char c = dst_mant[i] + carry; if (i >= shift) c += src_mant[i - shift]; if (c < 10) { dst_mant[i] = c; carry = 0; if (i <= shift) break; } else { dst_mant[i] = c - 10; carry = 1; } } if (carry) { /* Need to shift mantissa 1 position to the right. * Apply rounding on last digit. */ int carry2 = 0; if (dst_mant[15] >= 5) { carry2 = 1; for (i = 14; i >= 0; i--) { char c = dst_mant[i] + carry2; if (c < 10) { dst_mant[i] = c; carry2 = 0; break; } else { dst_mant[i] = c - 10; carry2 = 1; } } } for (i = 15; i >= 1; i--) dst_mant[i] = dst_mant[i - 1]; dst_mant[0] = carry + carry2; (*dst_exp)++; } } } static int bcd_cmp(const char *dst_mant, int dst_exp, const char *src_mant, int src_exp) { /* Note: both numbers should be normalized, that is, the leftmost * digit should only be zero if the entire number is zero. */ int i; if (dst_exp > src_exp) return 1; else if (dst_exp < src_exp) return -1; for (i = 0; i < 16; i++) if (dst_mant[i] > src_mant[i]) return 1; else if (dst_mant[i] < src_mant[i]) return -1; return 0; } static void bcd_sub(char *dst_mant, int *dst_exp, const char *src_mant, int src_exp) { /* Note: this function should only be called when dst >= src * (the bcd_cmp() function can be used to ascertain this) */ int i; int exp_offset = *dst_exp - src_exp; int borrow; if (exp_offset > 16) /* The number to be subtracted is too small to make a difference. */ return; if (exp_offset == 0) borrow = 0; else borrow = src_mant[16 - exp_offset] >= 5; /* Note: no check for exp_offset < 0 -- we require dst >= src ! */ for (i = 15; i >= 0; i--) { char c; if (i < exp_offset) c = dst_mant[i] - borrow; else c = dst_mant[i] - src_mant[i - exp_offset] - borrow; if (c >= 0) { dst_mant[i] = c; borrow = 0; if (i <= exp_offset) break; } else { dst_mant[i] = c + 10; borrow = 1; } } /* Note: no check for borrow != 0 at this point; * we require dst >= src ! */ /* Normalize dst */ exp_offset = -1; for (i = 0; i < 15; i++) if (dst_mant[i] != 0) { exp_offset = i; break; } if (exp_offset <= 0) /* exp_offset == -1 means dst == 0 now; * exp_offset == 0 means dst is nonzero and normalized. */ return; for (i = 0; i < 16 - exp_offset; i++) dst_mant[i] = dst_mant[i + exp_offset]; for (i = 16 - exp_offset; i < 16; i++) dst_mant[i] = 0; *dst_exp -= exp_offset; } int string2phloat(const char *buf, int buflen, phloat *d) { /* Convert string to phloat. * Return values: * 0: no error * 1: positive overflow * 2: negative overflow * 3: positive underflow * 4: negative underflow * 5: other error */ char mantissa[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; int mant_sign = 0; int skipping_zeroes = 1; int seen_dot = 0; int mant_pos = 0; int exp_offset = -1; int in_exp = 0; int exp = 0; int exp_sign = 0; int i, mant_digits = 0; char dot = flags.f.decimal_point ? '.' : ','; char sep = flags.f.decimal_point ? ',' : '.'; int d_exp, lastr; int is_zero = 1; double d_mant, d_bit, res; int temp_exp; for (i = 0; i < buflen; i++) { char c = buf[i]; if (c == 24) { in_exp = 1; if (mant_digits == 0) { mant_digits++; mantissa[mant_pos++] = 1; is_zero = 0; exp_offset = 0; } continue; } if (in_exp) { if (c == '-') exp_sign = 1; else exp = exp * 10 + (c - '0'); } else { if (c == sep) continue; if (c == dot) { seen_dot = 1; skipping_zeroes = 0; continue; } if (c == '-') { mant_sign = 1; continue; } /* Once we get here, c should be a digit */ if (++mant_digits > 12) /* Too many digits! We only allow the user to enter 12. */ return 5; if (c == '0' && skipping_zeroes) continue; skipping_zeroes = 0; mantissa[mant_pos++] = c - '0'; if (c != '0') is_zero = 0; if (!seen_dot) exp_offset = mant_pos - 1; } } if (is_zero) { *d = 0; return 0; } if (exp_sign) exp = -exp; exp += exp_offset; /* Get rid of leading zeroes in mantissa * (the loop above removes redundant leading zeroes, e.g. the first two * of 0012.345, but in the case of 0.001, the two zeroes following the * decimal are not redundant, and they end up in the mantissa. */ if (mantissa[0] == 0) { int leadingzeroes = 0; int i; while (mantissa[leadingzeroes] == 0) leadingzeroes++; for (i = 0; i < mant_pos - leadingzeroes; i++) mantissa[i] = mantissa[i + leadingzeroes]; for (i = mant_pos - leadingzeroes; i < mant_pos; i++) mantissa[i] = 0; exp -= leadingzeroes; } /* 'mantissa' now contains the normalized bcd mantissa; * 'exp' contains the normalized signed exponent, * and 'mant_sign' contains the mantissa's sign. */ /* We now perform the decimal-to-binary conversion by subtracting * bcd representations of powers of two, using the same lookup table * that is used for the binary-to-decimal conversion. * First, we must look for the greatest power of two that is less than * or equal to our number... */ d_exp = (int) (((double) exp) / log10(2.0)); if (d_exp < min_pow2) d_exp = min_pow2; else if (d_exp > max_pow2) d_exp = max_pow2; lastr = 0; while (1) { int r; char *bit_mant; int bit_exp; if (d_exp >= 0) { bit_mant = pos_pow2mant + 16 * d_exp; bit_exp = pos_pow2exp[d_exp]; } else { bit_mant = neg_pow2mant + 16 * (-1 - d_exp); bit_exp = neg_pow2exp[-1 - d_exp]; } r = bcd_cmp(mantissa, exp, bit_mant, bit_exp); if (r == 0) break; if (r < 0 && lastr > 0) { d_exp--; break; } if (r > 0 && lastr < 0) break; if (r < 0) { d_exp--; if (d_exp < min_pow2) /* Too small! */ return mant_sign ? 4 : 3; lastr = r; } else { d_exp++; if (d_exp > max_pow2) /* Too big! */ return mant_sign ? 2 : 1; lastr = r; } } /* 'd_exp' is now the greatest power of 2 which is less than or * equal to our decimal number. * Now we subtract this, and successively smaller powers of 2, from * the decimal number, until we hit zero or until the contribution * from the powers of 2 to the final result becomes too small. */ d_mant = 0; d_bit = 1; temp_exp = d_exp; while (1) { char *bit_mant; int bit_exp; int r; if (d_mant + d_bit == d_mant) /* We have reached full precision. */ break; if (temp_exp >= 0) { bit_mant = pos_pow2mant + 16 * temp_exp; bit_exp = pos_pow2exp[temp_exp]; } else { bit_mant = neg_pow2mant + 16 * (-1 - temp_exp); bit_exp = neg_pow2exp[-1 - temp_exp]; } r = bcd_cmp(mantissa, exp, bit_mant, bit_exp); if (r == 0) { d_mant += d_bit; break; } else if (r > 0) { d_mant += d_bit; bcd_sub(mantissa, &exp, bit_mant, bit_exp); } d_bit /= 2; temp_exp--; } res = ldexp(d_mant, (short) d_exp); if (mant_sign) res = -res; if (res == 0) return mant_sign ? 4 : 3; else if (p_isinf(res)) return mant_sign ? 2 : 1; else { *d = res; return 0; } } double bcd2double(short *p, bool old_bcd) { if (old_bcd) bcdfloat_old2new(p); short exp = p[P]; bool neg = (exp & 0x8000) != 0; double zero = 0; #if defined(WINDOWS) && !defined(__GNUC__) if ((exp & 0x4000) != 0) return HUGE_VAL; // NaN else if ((exp & 0x2000) != 0) return neg ? -HUGE_VAL : HUGE_VAL; // -Inf or Inf #else if ((exp & 0x4000) != 0) return 0 / zero; // NaN else if ((exp & 0x2000) != 0) return neg ? -1 / zero : 1 / zero; // -Inf or Inf #endif if (p[0] == 0) return 0; exp = ((short) (exp << 3)) >> 3; // Make sure we get exact results for integers if (exp > 0 && exp <= 5) { int j; for (j = exp; j < P; j++) if (p[j] != 0) goto noninteger; int8 n = 0; for (j = 0; j < exp; j++) n = n * 10000 + p[j]; return (double) (neg ? -n : n); } // TODO: Using a table-based conversion algorithm, like the one I use in // the binary version of string2phloat, I could get better accuracy. // The current code returns an inexact result for 23.5, for instance // (VC++), while the number *is* exactly representable in a 'double'. noninteger: double res = 0; for (int i = 0; i < P; i++) res = res * 10000 + p[i]; if (neg) res = -res; return res * pow(10000.0, (double) (exp - P)); } #endif // BCD_MATH int phloat2string(phloat pd, char *buf, int buflen, int base_mode, int digits, int dispmode, int thousandssep) { int chars_so_far = 0; if (p_isnan(pd)) { string2buf(buf, buflen, &chars_so_far, "", 14); return chars_so_far; } if (p_isinf(pd)) { char2buf(buf, buflen, &chars_so_far, '<'); if (pd < 0) char2buf(buf, buflen, &chars_so_far, '-'); string2buf(buf, buflen, &chars_so_far, "Infinity>", 9); return chars_so_far; } /* base_mode: 0=only decimal, 1=all bases, 2=decimal or binary (SHOW) */ int base = get_base(); if (base_mode == 1 && base != 10 || base_mode == 2 && base == 2) { int8 n; int inexact, shift; char binbuf[36]; int binbufptr = 0; if (pd > 34359738367.0 || pd < -34359738368.0) { if (base_mode == 2) goto decimal_after_all; else { string2buf(buf, buflen, &chars_so_far, "", 9); return chars_so_far; } } n = to_int8(pd); inexact = base_mode == 1 && pd != n; n &= LL(0xfffffffff); shift = base == 2 ? 1 : base == 8 ? 3 : 4; while (n != 0) { int digit = (int) (n & (base - 1)); char c = digit < 10 ? '0' + digit : 'A' + digit - 10; binbuf[binbufptr++] = c; n >>= shift; } if (binbufptr == 0) binbuf[binbufptr++] = '0'; while (binbufptr > 0) char2buf(buf, buflen, &chars_so_far, binbuf[--binbufptr]); if (inexact) char2buf(buf, buflen, &chars_so_far, (char) (flags.f.decimal_point ? '.' : ',')); return chars_so_far; decimal_after_all:; } char bcd_mantissa[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; int bcd_exponent = 0; int bcd_mantissa_sign = 0; if (pd < 0) { pd = -pd; bcd_mantissa_sign = 1; } #ifndef BCD_MATH double d = to_double(pd); double mantissa; int exp; int still_zero = 1; mantissa = frexp(d, &exp); while (mantissa != 0) { int bit; mantissa *= 2; exp--; bit = (int) mantissa; mantissa -= bit; if (bit) { char *bit_mant; int bit_exp; if (exp >= 0) { bit_mant = pos_pow2mant + 16 * exp; bit_exp = pos_pow2exp[exp]; } else { bit_mant = neg_pow2mant + 16 * (-1 - exp); bit_exp = neg_pow2exp[-1 - exp]; } if (!still_zero && bit_exp < bcd_exponent - 17) /* We have reached the full 16 decimal digits precision. */ break; bcd_add(bcd_mantissa, &bcd_exponent, bit_mant, bit_exp); still_zero = 0; } } #else // BCD_MATH if (pd != 0) { int offset, pos = 0; if (pd.bcd.d_[0] >= 1000) offset = 0; else if (pd.bcd.d_[0] >= 100) offset = 1; else if (pd.bcd.d_[0] >= 10) offset = 2; else offset = 3; bcd_exponent = pd.bcd.exp() * 4 - 1 - offset; for (int i = 0; i < 5; i++) { short s = pd.bcd.d_[i]; for (int j = 0; j < 4; j++) { if (pos == 16) break; if (offset == 0) bcd_mantissa[pos++] = s / 1000; else offset--; s = (s % 1000) * 10; } } } #endif // BCD_MATH if (dispmode == 0 || dispmode == 3) { /* FIX and ALL modes */ char norm_ip[12]; char norm_fp[27]; int i; int int_digits, frac_digits; int digits2; if (dispmode == 0) digits2 = digits; else digits2 = 11; for (i = 0; i < 12; i++) norm_ip[i] = 0; for (i = 0; i < 27; i++) norm_fp[i] = 0; if (bcd_exponent > 11 || -bcd_exponent > digits2 + 1) goto do_sci; for (i = 0; i < 16; i++) { if (i <= bcd_exponent) norm_ip[11 - bcd_exponent + i] = bcd_mantissa[i]; else norm_fp[-1 - bcd_exponent + i] = bcd_mantissa[i]; } if (dispmode == 0) { /* NOTE: I don't simply round norm_fp[digits], because of a * circumstance that cannot happen on a real HP-42S: there * may not be enough positions in the display to show a number * at the requested accuracy (e.g. 123456123456.5 at FIX 01) * so I first calculate how many positions I actually have * available, and then I round to that. As a result of this * rounding, the number may overflow the FIX representation, * in which case I fall back on SCI. */ int carry; int visdigits = 11 - bcd_exponent; if (visdigits > digits) visdigits = digits; carry = norm_fp[visdigits] >= 5; for (i = visdigits; i < 27; i++) norm_fp[i] = 0; if (!carry) goto done_rounding; for (i = visdigits - 1; i >= 0; i--) { char c = norm_fp[i] + 1; if (c < 10) { norm_fp[i] = c; goto done_rounding; } else norm_fp[i] = c - 10; } for (i = 11; i >= 0; i--) { char c = norm_ip[i] + 1; if (c < 10) { norm_ip[i] = c; goto done_rounding; } else norm_ip[i] = c - 10; } /* If we get here, the carry went past the 12th integer digit, * and we have to use SCI mode instead. */ goto do_sci; done_rounding:; } else { /* ALL mode: for HP-42S compatibility, round to 12 * digits before proceeding. */ int f = 1000; for (i = 0; i < 39; i++) { char c = i < 12 ? norm_ip[i] : norm_fp[i - 12]; if (c != 0 && f == 1000) f = i; if (i == f + 12) { int carry = c >= 5; if (carry) { int j; for (j = i - 1; j >= 0; j--) { char c2 = j < 12 ? norm_ip[j] : norm_fp[j - 12]; c2++; if (c2 < 10) carry = 0; else { c2 -= 10; carry = 1; } if (j < 12) norm_ip[j] = c2; else norm_fp[j - 12] = c2; if (!carry) break; } if (carry) /* Rounding is making the integer part 13 digits * long; must go to SCI mode. */ goto do_sci; } } if (i >= f + 12) { if (i < 12) norm_ip[i] = 0; else norm_fp[i - 12] = 0; } } } /* Check if the number is still within bounds for FIX or ALL */ if (pd != 0) { /* Make sure that nonzero numbers are not * displayed as zero because of the rounding. */ for (i = 0; i < 12; i++) if (norm_ip[i] != 0) goto fix_ok; for (i = 0; i < digits2; i++) if (norm_fp[i] != 0) goto fix_ok; /* Uh-oh, the number is nonzero, but its rounded representation * is zero. That's not good; use SCI mode instead. */ goto do_sci; fix_ok: if (dispmode == 3) { /* Make sure we're not throwing away anything in ALL mode */ for (i = 11; i < 27; i++) if (norm_fp[i] != 0) goto do_sci; } } int_digits = 1; for (i = 0; i < 12; i++) if (norm_ip[i] != 0) { int_digits = 12 - i; break; } if (bcd_mantissa_sign) char2buf(buf, buflen, &chars_so_far, '-'); for (i = int_digits - 1; i >= 0; i--) { if (thousandssep && i % 3 == 2 && i != int_digits - 1) char2buf(buf, buflen, &chars_so_far, (char) (flags.f.decimal_point ? ',' : '.')); char2buf(buf, buflen, &chars_so_far, (char)('0' + norm_ip[11 - i])); } if (dispmode == 0) frac_digits = digits; else { frac_digits = 0; for (i = 0; i < 27; i++) if (norm_fp[i] != 0) frac_digits = i + 1; } if (frac_digits + int_digits > 12) frac_digits = 12 - int_digits; if (frac_digits > 0 || (dispmode == 0 && thousandssep)) { char2buf(buf, buflen, &chars_so_far, (char) (flags.f.decimal_point ? '.' : ',')); for (i = 0; i < frac_digits; i++) char2buf(buf, buflen, &chars_so_far, (char) ('0' + norm_fp[i])); } return chars_so_far; } else { /* SCI and ENG modes */ /* Also fall-through from FIX and ALL */ int m_digits; int carry; char norm_mantissa[16]; int norm_exponent, e3; int i; do_sci: for (i = 0; i < 16; i++) norm_mantissa[i] = bcd_mantissa[i]; norm_exponent = bcd_exponent; if (dispmode == 3) { /* Round to 12 digits before doing anything else; * this is needed to handle mantissas like 9.99999999999999, * which would otherwise end up getting displayed as * 10.0000000000 instead of 10. */ sci_all_round: carry = norm_mantissa[12] >= 5; for (i = 12; i < 16; i++) norm_mantissa[i] = 0; if (carry) { for (i = 11; i >= 0; i--) { char c = norm_mantissa[i] + carry; if (c < 10) { norm_mantissa[i] = c; carry = 0; break; } else { norm_mantissa[i] = c - 10; carry = 1; } } } if (carry) { /* Don't round by an additional digit: that would mean * we're rounding the same number twice, which is bad * (think about what happens when you round one digit off * 0.45, twice -- you get first 0.5, then 1... Oops). * So, we start over. */ for (i = 0; i < 15; i++) norm_mantissa[i + 1] = bcd_mantissa[i]; norm_mantissa[0] = 0; norm_exponent = bcd_exponent + 1; goto sci_all_round; } m_digits = 0; for (i = 11; i >= 0; i--) if (norm_mantissa[i] != 0) { m_digits = i; break; } } else { m_digits = digits; sci_round: carry = norm_mantissa[m_digits + 1] >= 5; for (i = m_digits + 1; i < 16; i++) norm_mantissa[i] = 0; if (carry) { for (i = m_digits; i >= 0; i--) { char c = norm_mantissa[i] + carry; if (c < 10) { norm_mantissa[i] = c; carry = 0; break; } else { norm_mantissa[i] = c - 10; carry = 1; } } } if (carry) { /* Don't round by an additional digit: that would mean * we're rounding the same number twice, which is bad * (think about what happens when you round one digit off * 0.45, twice -- you get first 0.5, then 1... Oops). * So, we start over. */ for (i = 0; i < 15; i++) norm_mantissa[i + 1] = bcd_mantissa[i]; norm_mantissa[0] = 0; norm_exponent = bcd_exponent + 1; goto sci_round; } } if (bcd_mantissa_sign) char2buf(buf, buflen, &chars_so_far, '-'); if (dispmode == 2) { e3 = norm_exponent % 3; if (e3 < 0) e3 += 3; if (m_digits < e3) m_digits = e3; norm_exponent -= e3; } else e3 = 0; for (i = 0; i <= m_digits; i++) { char2buf(buf, buflen, &chars_so_far, (char) ('0' + norm_mantissa[i])); if (i == e3) char2buf(buf, buflen, &chars_so_far, (char) (flags.f.decimal_point ? '.' : ',')); } char2buf(buf, buflen, &chars_so_far, 24); i = int2string(norm_exponent, buf + chars_so_far, buflen - chars_so_far); chars_so_far += i; return chars_so_far; } } void bcdfloat_old2new(void *bcd) { // Convert old (<= 1.4.51) BCDFloat, where NaN is signalled by // (exp & 0x7FFF) == 0x3000, and Infinity is signalled by // (exp & 0x7FFF) == 0x3FFF, to the new (>= 1.4.52) BCDFloat, where NaN is // signalled by (exp & 0x4000) != 0 and Infinity is signalled by // (exp & 0x2000) != 0 (and the exponent field is 2 bits narrower). short *p = (short *) bcd; short uexp = p[P] & 0x7FFF; if (uexp == 0x3000) // NaN p[P] = 0x4000; else if (uexp == 0x3FFF) // Infinity p[P] = (p[P] & 0x8000) | 0x2000; else p[P] = p[P] & 0x9FFF; } free42-nologo-1.4.77/common/core_phloat.h000644 000765 000024 00000011046 12110237247 020573 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_PHLOAT_H #define CORE_PHLOAT_H 1 #include "free42.h" #ifdef BCD_MATH #include "bcdfloat.h" #endif // A little hack to allow storing 6-character strings in a phloat struct hp_string { char text[6]; unsigned char length; }; #define phloat_text(x) (((hp_string *) &(x))->text) #define phloat_length(x) (((hp_string *) &(x))->length) #ifndef BCD_MATH #define phloat double #define p_isinf isinf #define p_isnan isnan #define to_digit(x) ((int) fmod((x), 10.0)) #define to_char(x) ((char) (x)) #define to_int(x) ((int) (x)) #define to_int4(x) ((int4) (x)) #define to_int8(x) ((int8) (x)) #define to_double(x) ((double) (x)) #define PI 3.1415926535897932384626433 #define P 7 double bcd2double(short *p, bool old_bcd); #else // BCD_MATH #define phloat Phloat class Phloat { public: BCDFloat bcd; Phloat() {} Phloat(BCDFloat b) : bcd(b) {} Phloat(int numer, int denom); Phloat(int i); Phloat(int8 i); Phloat(double d); Phloat(const Phloat &p); Phloat operator=(int i); Phloat operator=(int8 i); Phloat operator=(double d); Phloat operator=(Phloat p); bool operator==(Phloat p) const; bool operator!=(Phloat p) const; bool operator<(Phloat p) const; bool operator<=(Phloat p) const; bool operator>(Phloat p) const; bool operator>=(Phloat p) const; Phloat operator-() const; Phloat operator*(Phloat p) const; Phloat operator/(Phloat p) const; Phloat operator+(Phloat p) const; Phloat operator-(Phloat p) const; Phloat operator*=(Phloat p); Phloat operator/=(Phloat p); Phloat operator+=(Phloat p); Phloat operator-=(Phloat p); Phloat operator++(); // prefix Phloat operator++(int); // postfix Phloat operator--(); // prefix Phloat operator--(int); // postfix }; // I can't simply overload isinf() and isnan(), because the Linux math.h // defines them as macros. int p_isinf(Phloat p); int p_isnan(Phloat p); // We don't define type cast operators, because they just lead // to tons of ambiguities. Defining explicit conversions instead. // Note that these conversion routines assume that the value to be // converted actually fits in the returned type; if not, the result // is undefined, except for to_char(), which will handle the range // -128..255 correctly. int to_digit(Phloat p); // Returns digit in units position char to_char(Phloat p); int to_int(Phloat p); int4 to_int4(Phloat p); int8 to_int8(Phloat p); double to_double(Phloat p); Phloat sin(Phloat p); Phloat cos(Phloat p); Phloat tan(Phloat p); Phloat asin(Phloat p); Phloat acos(Phloat p); Phloat atan(Phloat p); void sincos(Phloat phi, Phloat *s, Phloat *c); Phloat hypot(Phloat x, Phloat y); Phloat atan2(Phloat x, Phloat y); Phloat sinh(Phloat p); Phloat cosh(Phloat p); Phloat tanh(Phloat p); Phloat asinh(Phloat p); Phloat acosh(Phloat p); Phloat atanh(Phloat p); Phloat log(Phloat p); Phloat log1p(Phloat p); Phloat log10(Phloat p); Phloat exp(Phloat p); Phloat expm1(Phloat p); Phloat gamma(Phloat p); Phloat sqrt(Phloat p); Phloat fmod(Phloat x, Phloat y); Phloat fabs(Phloat p); Phloat pow(Phloat x, Phloat y); Phloat floor(Phloat x); Phloat operator*(int x, Phloat y); Phloat operator/(int x, Phloat y); Phloat operator/(double x, Phloat y); Phloat operator+(int x, Phloat y); Phloat operator-(int x, Phloat y); bool operator==(int4 x, Phloat y); extern Phloat PI; BCDFloat double2bcd(double d, bool round = false); double bcd2double(BCDFloat b, bool old_bcd); #endif // BCD_MATH extern phloat POS_HUGE_PHLOAT; extern phloat NEG_HUGE_PHLOAT; extern phloat POS_TINY_PHLOAT; extern phloat NEG_TINY_PHLOAT; void phloat_init(); void phloat_cleanup(); int phloat2string(phloat d, char *buf, int buflen, int base_mode, int digits, int dispmode, int thousandssep); int string2phloat(const char *buf, int buflen, phloat *d); void bcdfloat_old2new(void *bcd); #endif free42-nologo-1.4.77/common/core_sto_rcl.cc000644 000765 000024 00000114106 12110237247 021110 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_helpers.h" #include "core_linalg1.h" #include "core_sto_rcl.h" #include "core_variables.h" static int apply_sto_operation(char operation, vartype *oldval); static void generic_sto_completion(int error, vartype *res); static bool preserve_ij; static int apply_sto_operation(char operation, vartype *oldval) { vartype *newval; int error; switch (operation) { case '/': preserve_ij = true; return generic_div(reg_x, oldval, generic_sto_completion); case '*': preserve_ij = false; return generic_mul(reg_x, oldval, generic_sto_completion); case '-': preserve_ij = true; error = generic_sub(reg_x, oldval, &newval); generic_sto_completion(error, newval); return error; case '+': preserve_ij = true; error = generic_add(reg_x, oldval, &newval); generic_sto_completion(error, newval); return error; default: return ERR_INTERNAL_ERROR; } } int generic_div(const vartype *px, const vartype *py, void (*completion)(int, vartype *)) { if (px->type == TYPE_STRING || py->type == TYPE_STRING) { completion(ERR_ALPHA_DATA_IS_INVALID, NULL); return ERR_ALPHA_DATA_IS_INVALID; } else if ((px->type == TYPE_REALMATRIX || px->type == TYPE_COMPLEXMATRIX) && (py->type == TYPE_REALMATRIX || py->type == TYPE_COMPLEXMATRIX)){ return linalg_div(py, px, completion); } else { vartype *dst; int error = map_binary(px, py, &dst, div_rr, div_rc, div_cr, div_cc); completion(error, dst); return error; } } int generic_mul(const vartype *px, const vartype *py, void (*completion)(int, vartype *)) { if (px->type == TYPE_STRING || py->type == TYPE_STRING) { completion(ERR_ALPHA_DATA_IS_INVALID, NULL); return ERR_ALPHA_DATA_IS_INVALID; } else if ((px->type == TYPE_REALMATRIX || px->type == TYPE_COMPLEXMATRIX) && (py->type == TYPE_REALMATRIX || py->type == TYPE_COMPLEXMATRIX)){ return linalg_mul(py, px, completion); } else { vartype *dst; int error = map_binary(px, py, &dst, mul_rr, mul_rc, mul_cr, mul_cc); completion(error, dst); return error; } } int generic_sub(const vartype *px, const vartype *py, vartype **dst) { if (px->type == TYPE_REAL && py->type == TYPE_REAL) { vartype_real *x = (vartype_real *) px; vartype_real *y = (vartype_real *) py; phloat res = y->x - x->x; int inf = p_isinf(res); if (inf != 0) { if (flags.f.range_error_ignore) res = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_real(res); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_COMPLEX && py->type == TYPE_COMPLEX) { vartype_complex *x = (vartype_complex *) px; vartype_complex *y = (vartype_complex *) py; int inf; phloat re, im; re = y->re - x->re; inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } im = y->im - x->im; inf = p_isinf(im); if (inf != 0) { if (flags.f.range_error_ignore) im = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_REAL && py->type == TYPE_COMPLEX) { vartype_real *x = (vartype_real *) px; vartype_complex *y = (vartype_complex *) py; int inf; phloat re = y->re - x->x; inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, y->im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_COMPLEX && py->type == TYPE_REAL) { vartype_complex *x = (vartype_complex *) px; vartype_real *y = (vartype_real *) py; phloat re = y->x - x->re; int inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, -x->im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_STRING || py->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return map_binary(px, py, dst, sub_rr, sub_rc, sub_cr, sub_cc); } int generic_add(const vartype *px, const vartype *py, vartype **dst) { if (px->type == TYPE_REAL && py->type == TYPE_REAL) { vartype_real *x = (vartype_real *) px; vartype_real *y = (vartype_real *) py; phloat res = y->x + x->x; int inf = p_isinf(res); if (inf != 0) { if (flags.f.range_error_ignore) res = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_real(res); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_COMPLEX && py->type == TYPE_COMPLEX) { vartype_complex *x = (vartype_complex *) px; vartype_complex *y = (vartype_complex *) py; int inf; phloat re, im; re = x->re + y->re; inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } im = x->im + y->im; inf = p_isinf(im); if (inf != 0) { if (flags.f.range_error_ignore) im = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_REAL && py->type == TYPE_COMPLEX) { vartype_real *x = (vartype_real *) px; vartype_complex *y = (vartype_complex *) py; phloat re = y->re + x->x; int inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, y->im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_COMPLEX && py->type == TYPE_REAL) { vartype_complex *x = (vartype_complex *) px; vartype_real *y = (vartype_real *) py; phloat re = x->re + y->x; int inf = p_isinf(re); if (inf != 0) { if (flags.f.range_error_ignore) re = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *dst = new_complex(re, x->im); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; else return ERR_NONE; } else if (px->type == TYPE_STRING || py->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else return map_binary(px, py, dst, add_rr, add_rc, add_cr, add_cc); } int generic_rcl(arg_struct *arg, vartype **dst) { int err; if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } switch (arg->type) { case ARGTYPE_NUM: { vartype *regs = recall_var("REGS", 4); if (regs == NULL) return ERR_SIZE_ERROR; else if (regs->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) regs; int4 size = rm->rows * rm->columns; int4 index = arg->val.num; phloat ds; if (index >= size) return ERR_SIZE_ERROR; ds = rm->array->data[index]; if (rm->array->is_string[index]) *dst = new_string(phloat_text(ds), phloat_length(ds)); else *dst = new_real(ds); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; return ERR_NONE; } else if (regs->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) regs; int4 size = cm->rows * cm->columns; if (arg->val.num >= size) return ERR_SIZE_ERROR; *dst = new_complex(cm->array->data[arg->val.num * 2], cm->array->data[arg->val.num * 2 + 1]); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; return ERR_NONE; } else { /* This should never happen; STO should prevent * "REGS" from being any other type than a real or * complex matrix. */ return ERR_INTERNAL_ERROR; } } case ARGTYPE_STK: { switch (arg->val.stk) { case 'X': *dst = reg_x; break; case 'Y': *dst = reg_y; break; case 'Z': *dst = reg_z; break; case 'T': *dst = reg_t; break; case 'L': *dst = reg_lastx; break; } *dst = dup_vartype(*dst); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; return ERR_NONE; } case ARGTYPE_STR: { *dst = recall_var(arg->val.text, arg->length); if (*dst == NULL) return ERR_NONEXISTENT; *dst = dup_vartype(*dst); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } } static arg_struct temp_arg; static void generic_sto_completion(int error, vartype *res) { if (error != ERR_NONE) return; if (temp_arg.type == ARGTYPE_STK) { switch (temp_arg.val.stk) { case 'X': free_vartype(reg_x); reg_x = res; break; case 'Y': free_vartype(reg_y); reg_y = res; break; case 'Z': free_vartype(reg_z); reg_z = res; break; case 'T': free_vartype(reg_t); reg_t = res; break; case 'L': free_vartype(reg_lastx); reg_lastx = res; break; } } else /* temp_arg.type == ARGTYPE_STR */ { // If the destination of store_var() is the indexed matrix, it sets I // and J to 1. This is *not* the desired behavior for STO+, STO-, and // STO/. (It is correct for STO and STO*, since those can cause the // destination's dimensions to change.) int4 i = matedit_i; int4 j = matedit_j; store_var(temp_arg.val.text, temp_arg.length, res); if (preserve_ij) { matedit_i = i; matedit_j = j; } } } int generic_sto(arg_struct *arg, char operation) { if (arg->type == ARGTYPE_IND_NUM || arg->type == ARGTYPE_IND_STK || arg->type == ARGTYPE_IND_STR) { int err = resolve_ind_arg(arg); if (err != ERR_NONE) return err; } if (operation != 0 && reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; switch (arg->type) { case ARGTYPE_NUM: { vartype *regs = recall_var("REGS", 4); if (regs == NULL) return ERR_SIZE_ERROR; if (regs->type == TYPE_REALMATRIX) { vartype_realmatrix *rm = (vartype_realmatrix *) regs; int4 size = rm->rows * rm->columns; int4 num = arg->val.num; if (num >= size) return ERR_SIZE_ERROR; if (reg_x->type == TYPE_STRING) { vartype_string *vs = (vartype_string *) reg_x; phloat *ds = rm->array->data + num; int len, i; if (!disentangle((vartype *) rm)) return ERR_INSUFFICIENT_MEMORY; len = vs->length; phloat_length(*ds) = len; for (i = 0; i < len; i++) phloat_text(*ds)[i] = vs->text[i]; rm->array->is_string[num] = 1; return ERR_NONE; } else if (reg_x->type == TYPE_REAL) { if (!disentangle((vartype *) rm)) return ERR_INSUFFICIENT_MEMORY; if (operation == 0) { rm->array->data[num] = ((vartype_real *) reg_x)->x; rm->array->is_string[num] = 0; } else { phloat x, n; int inf; if (rm->array->is_string[num]) return ERR_ALPHA_DATA_IS_INVALID; x = ((vartype_real *) reg_x)->x; n = rm->array->data[num]; switch (operation) { case '/': if (x == 0) return ERR_DIVIDE_BY_0; n /= x; break; case '*': n *= x; break; case '-': n -= x; break; case '+': n += x; break; } inf = p_isinf(n); if (inf != 0) { if (flags.f.range_error_ignore) n = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rm->array->data[num] = n; } return ERR_NONE; } else return ERR_INVALID_TYPE; } else if (regs->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm = (vartype_complexmatrix *) regs; int4 size = cm->rows * cm->columns; int4 num = arg->val.num; phloat re, im; if (num >= size) return ERR_SIZE_ERROR; if (reg_x->type == TYPE_STRING) return ERR_ALPHA_DATA_IS_INVALID; else if (reg_x->type != TYPE_REAL && reg_x->type != TYPE_COMPLEX) return ERR_INVALID_TYPE; if (!disentangle((vartype *) cm)) return ERR_INSUFFICIENT_MEMORY; if (operation == 0) { if (reg_x->type == TYPE_REAL) { re = ((vartype_real *) reg_x)->x; im = 0; } else { re = ((vartype_complex *) reg_x)->re; im = ((vartype_complex *) reg_x)->im; } cm->array->data[num * 2] = re; cm->array->data[num * 2 + 1] = im; } else { phloat nre = cm->array->data[num * 2]; phloat nim = cm->array->data[num * 2 + 1]; int inf; if (reg_x->type == TYPE_REAL) { phloat x; x = ((vartype_real *) reg_x)->x; switch (operation) { case '/': if (x == 0) return ERR_DIVIDE_BY_0; nre /= x; nim /= x; break; case '*': nre *= x; nim *= x; break; case '-': nre -= x; break; case '+': nre += x; break; } } else { phloat xre, xim, h, tmp; xre = ((vartype_complex *) reg_x)->re; xim = ((vartype_complex *) reg_x)->im; h = xre * xre + xim * xim; /* TODO: handle overflows in intermediate results */ switch (operation) { case '/': if (h == 0) return ERR_DIVIDE_BY_0; tmp = (nre * xre + nim * xim) / h; nim = (xre * nim - xim * nre) / h; nre = tmp; break; case '*': tmp = nre * xre - nim * xim; nim = nre * xim + nim * xre; nre = tmp; break; case '-': nre -= xre; nim -= xim; break; case '+': nre += xre; nim += xim; break; } } inf = p_isinf(nre); if (inf != 0) { if (flags.f.range_error_ignore) nre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } inf = p_isinf(nim); if (inf != 0) { if (flags.f.range_error_ignore) nim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } cm->array->data[num * 2] = nre; cm->array->data[num * 2 + 1] = nim; } return ERR_NONE; } else { /* This should never happen; STO should prevent * "REGS" from being any other type than a real or * complex matrix. */ return ERR_INTERNAL_ERROR; } } case ARGTYPE_STK: { vartype *newval; if (operation == 0) { if (arg->val.stk == 'X') { /* STO ST X : no-op! */ return ERR_NONE; } newval = dup_vartype(reg_x); if (newval == NULL) return ERR_INSUFFICIENT_MEMORY; switch (arg->val.stk) { case 'Y': free_vartype(reg_y); reg_y = newval; break; case 'Z': free_vartype(reg_z); reg_z = newval; break; case 'T': free_vartype(reg_t); reg_t = newval; break; case 'L': free_vartype(reg_lastx); reg_lastx = newval; break; } return ERR_NONE; } else { vartype *oldval; switch (arg->val.stk) { case 'X': oldval = reg_x; break; case 'Y': oldval = reg_y; break; case 'Z': oldval = reg_z; break; case 'T': oldval = reg_t; break; case 'L': oldval = reg_lastx; break; } temp_arg = *arg; return apply_sto_operation(operation, oldval); } } case ARGTYPE_STR: { if (operation == 0) { vartype *newval; /* Only allow matrices to be stored in "REGS" */ if (arg->length == 4 && arg->val.text[0] == 'R' && arg->val.text[1] == 'E' && arg->val.text[2] == 'G' && arg->val.text[3] == 'S' && reg_x->type != TYPE_REALMATRIX && reg_x->type != TYPE_COMPLEXMATRIX) return ERR_RESTRICTED_OPERATION; /* When EDITN is active, don't allow the matrix being * edited to be overwritten by a string or scalar. */ if (matedit_mode == 3 && arg->length == matedit_length && (reg_x->type == TYPE_REAL || reg_x->type == TYPE_COMPLEX || reg_x->type == TYPE_STRING)) { bool equal = true; for (int i = 0; i < arg->length; i++) if (arg->val.text[i] != matedit_name[i]) { equal = false; break; } if (equal) return ERR_RESTRICTED_OPERATION; } newval = dup_vartype(reg_x); if (newval == NULL) return ERR_INSUFFICIENT_MEMORY; store_var(arg->val.text, arg->length, newval); return ERR_NONE; } else { vartype *oldval = recall_var(arg->val.text, arg->length); if (oldval == NULL) return ERR_NONEXISTENT; temp_arg = *arg; return apply_sto_operation(operation, oldval); } } default: return ERR_INVALID_TYPE; } } int map_unary(const vartype *src, vartype **dst, mappable_r mr, mappable_c mc) { int error; switch (src->type) { case TYPE_REAL: { phloat r; error = mr(((vartype_real *) src)->x, &r); if (error == ERR_NONE) { *dst = new_real(r); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_COMPLEX: { phloat rre, rim; error = mc(((vartype_complex *) src)->re, ((vartype_complex *) src)->im, &rre, &rim); if (error == ERR_NONE) { *dst = new_complex(rre, rim); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_REALMATRIX: { vartype_realmatrix *sm = (vartype_realmatrix *) src; vartype_realmatrix *dm; int4 size, i; int error; dm = (vartype_realmatrix *) new_realmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm->rows * sm->columns; for (i = 0; i < size; i++) { if (sm->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } } for (i = 0; i < size; i++) { error = mr(sm->array->data[i], &dm->array->data[i]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *sm = (vartype_complexmatrix *) src; vartype_complexmatrix *dm; int4 rows = sm->rows; int4 columns = sm->columns; int4 size = 2 * rows * columns; int4 i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(rows, columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; for (i = 0; i < size; i += 2) { error = mc(sm->array->data[i], sm->array->data[i + 1], &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } } int map_binary(const vartype *src1, const vartype *src2, vartype **dst, mappable_rr mrr, mappable_rc mrc, mappable_cr mcr, mappable_cc mcc) { int error; switch (src1->type) { case TYPE_REAL: switch (src2->type) { case TYPE_REAL: { phloat r; error = mrr(((vartype_real *) src1)->x, ((vartype_real *) src2)->x, &r); if (error == ERR_NONE) { *dst = new_real(r); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_COMPLEX: { phloat rre, rim; error = mrc(((vartype_real *) src1)->x, ((vartype_complex *) src2)->re, ((vartype_complex *) src2)->im, &rre, &rim); if (error == ERR_NONE) { *dst = new_complex(rre, rim); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_REALMATRIX: { vartype_realmatrix *sm = (vartype_realmatrix *) src2; vartype_realmatrix *dm; int4 size, i; int error; dm = (vartype_realmatrix *) new_realmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm->rows * sm->columns; for (i = 0; i < size; i++) if (sm->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mrr(((vartype_real *) src1)->x, sm->array->data[i], &dm->array->data[i]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *sm = (vartype_complexmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = 2 * sm->rows * sm->columns; for (i = 0; i < size; i += 2) { error = mrc(((vartype_real *) src1)->x, sm->array->data[i], sm->array->data[i + 1], &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } case TYPE_COMPLEX: switch (src2->type) { case TYPE_REAL: { phloat rre, rim; error = mcr(((vartype_complex *) src1)->re, ((vartype_complex *) src1)->im, ((vartype_real *) src2)->x, &rre, &rim); if (error == ERR_NONE) { *dst = new_complex(rre, rim); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_COMPLEX: { phloat rre, rim; error = mcc(((vartype_complex *) src1)->re, ((vartype_complex *) src1)->im, ((vartype_complex *) src2)->re, ((vartype_complex *) src2)->im, &rre, &rim); if (error == ERR_NONE) { *dst = new_complex(rre, rim); if (*dst == NULL) return ERR_INSUFFICIENT_MEMORY; } return error; } case TYPE_REALMATRIX: { vartype_realmatrix *sm = (vartype_realmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm->rows * sm->columns; for (i = 0; i < size; i++) if (sm->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mcr(((vartype_complex *) src1)->re, ((vartype_complex *) src1)->im, sm->array->data[i], &dm->array->data[i * 2], &dm->array->data[i * 2 + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *sm = (vartype_complexmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = 2 * sm->rows * sm->columns; for (i = 0; i < size; i += 2) { error = mcc(((vartype_complex *) src1)->re, ((vartype_complex *) src1)->im, sm->array->data[i], sm->array->data[i + 1], &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } case TYPE_REALMATRIX: switch (src2->type) { case TYPE_REAL: { vartype_realmatrix *sm = (vartype_realmatrix *) src1; vartype_realmatrix *dm; int4 size, i; int error; dm = (vartype_realmatrix *) new_realmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm->rows * sm->columns; for (i = 0; i < size; i++) if (sm->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mrr(sm->array->data[i], ((vartype_real *) src2)->x, &dm->array->data[i]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEX: { vartype_realmatrix *sm = (vartype_realmatrix *) src1; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm->rows * sm->columns; for (i = 0; i < size; i++) if (sm->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mrc(sm->array->data[i], ((vartype_complex *) src2)->re, ((vartype_complex *) src2)->im, &dm->array->data[i * 2], &dm->array->data[i * 2 + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_REALMATRIX: { vartype_realmatrix *sm1 = (vartype_realmatrix *) src1; vartype_realmatrix *sm2 = (vartype_realmatrix *) src2; vartype_realmatrix *dm; int4 size, i; int error; if (sm1->rows != sm2->rows || sm1->columns != sm2->columns) return ERR_DIMENSION_ERROR; dm = (vartype_realmatrix *) new_realmatrix(sm1->rows, sm1->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm1->rows * sm1->columns; for (i = 0; i < size; i++) if (sm1->array->is_string[i] || sm2->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mrr(sm1->array->data[i], sm2->array->data[i], &dm->array->data[i]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_realmatrix *sm1 = (vartype_realmatrix *) src1; vartype_complexmatrix *sm2 = (vartype_complexmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; if (sm1->rows != sm2->rows || sm1->columns != sm2->columns) return ERR_DIMENSION_ERROR; dm = (vartype_complexmatrix *) new_complexmatrix(sm1->rows, sm1->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm1->rows * sm1->columns; for (i = 0; i < size; i++) if (sm1->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mrc(sm1->array->data[i], sm2->array->data[i * 2], sm2->array->data[i * 2 + 1], &dm->array->data[i * 2], &dm->array->data[i * 2 + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } case TYPE_COMPLEXMATRIX: switch (src2->type) { case TYPE_REAL: { vartype_complexmatrix *sm = (vartype_complexmatrix *) src1; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = 2 * sm->rows * sm->columns; for (i = 0; i < size; i += 2) { error = mcr(sm->array->data[i], sm->array->data[i + 1], ((vartype_real *) src2)->x, &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEX: { vartype_complexmatrix *sm = (vartype_complexmatrix *) src1; vartype_complexmatrix *dm; int4 size, i; int error; dm = (vartype_complexmatrix *) new_complexmatrix(sm->rows, sm->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = 2 * sm->rows * sm->columns; for (i = 0; i < size; i += 2) { error = mcc(sm->array->data[i], sm->array->data[i + 1], ((vartype_complex *) src2)->re, ((vartype_complex *) src2)->im, &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_REALMATRIX: { vartype_complexmatrix *sm1 = (vartype_complexmatrix *) src1; vartype_realmatrix *sm2 = (vartype_realmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; if (sm1->rows != sm2->rows || sm1->columns != sm2->columns) return ERR_DIMENSION_ERROR; dm = (vartype_complexmatrix *) new_complexmatrix(sm1->rows, sm1->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = sm1->rows * sm1->columns; for (i = 0; i < size; i++) if (sm2->array->is_string[i]) { free_vartype((vartype *) dm); return ERR_ALPHA_DATA_IS_INVALID; } for (i = 0; i < size; i++) { error = mcr(sm1->array->data[i * 2], sm1->array->data[i * 2 + 1], sm2->array->data[i], &dm->array->data[i * 2], &dm->array->data[i * 2 + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *sm1 = (vartype_complexmatrix *) src1; vartype_complexmatrix *sm2 = (vartype_complexmatrix *) src2; vartype_complexmatrix *dm; int4 size, i; int error; if (sm1->rows != sm2->rows || sm1->columns != sm2->columns) return ERR_DIMENSION_ERROR; dm = (vartype_complexmatrix *) new_complexmatrix(sm1->rows, sm1->columns); if (dm == NULL) return ERR_INSUFFICIENT_MEMORY; size = 2 * sm1->rows * sm1->columns; for (i = 0; i < size; i += 2) { error = mcc(sm1->array->data[i], sm1->array->data[i + 1], sm2->array->data[i], sm2->array->data[i + 1], &dm->array->data[i], &dm->array->data[i + 1]); if (error != ERR_NONE) { free_vartype((vartype *) dm); return error; } } *dst = (vartype *) dm; return ERR_NONE; } default: return ERR_INTERNAL_ERROR; } default: return ERR_INTERNAL_ERROR; } } int div_rr(phloat x, phloat y, phloat *z) { phloat r; int inf; if (x == 0) return ERR_DIVIDE_BY_0; r = y / x; inf = p_isinf(r); if (inf != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *z = r; return ERR_NONE; } int div_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim; int inf; if (x == 0) return ERR_DIVIDE_BY_0; rre = yre / x; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = yim / x; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int div_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim) { phloat rre, rim, h; int inf; /* TODO: overflows in intermediate results */ h = xre * xre + xim * xim; if (h == 0) return ERR_DIVIDE_BY_0; rre = y * xre / h; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = -y * xim / h; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int div_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim, h; int inf; /* TODO: overflows in intermediate results */ h = xre * xre + xim * xim; if (h == 0) return ERR_DIVIDE_BY_0; rre = (xre * yre + xim * yim) / h; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = (xre * yim - yre * xim) / h; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int mul_rr(phloat x, phloat y, phloat *z) { phloat r; int inf; r = y * x; inf = p_isinf(r); if (inf != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *z = r; return ERR_NONE; } int mul_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim; int inf; rre = yre * x; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = yim * x; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int mul_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim) { phloat rre, rim; int inf; rre = y * xre; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = y * xim; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int mul_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim; int inf; rre = xre * yre - xim * yim; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = xre * yim + yre * xim; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int sub_rr(phloat x, phloat y, phloat *z) { phloat r; int inf; r = y - x; inf = p_isinf(r); if (inf != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *z = r; return ERR_NONE; } int sub_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre; int inf; rre = yre - x; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = yim; return ERR_NONE; } int sub_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim) { phloat rre; int inf; rre = y - xre; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = -xim; return ERR_NONE; } int sub_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim; int inf; rre = yre - xre; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = yim - xim; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } int add_rr(phloat x, phloat y, phloat *z) { phloat r; int inf; r = y + x; inf = p_isinf(r); if (inf != 0) { if (flags.f.range_error_ignore) r = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *z = r; return ERR_NONE; } int add_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre; int inf; rre = yre + x; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = yim; return ERR_NONE; } int add_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim) { phloat rre; int inf; rre = y + xre; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = xim; return ERR_NONE; } int add_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim) { phloat rre, rim; int inf; rre = yre + xre; inf = p_isinf(rre); if (inf != 0) { if (flags.f.range_error_ignore) rre = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } rim = yim + xim; inf = p_isinf(rim); if (inf != 0) { if (flags.f.range_error_ignore) rim = inf == 1 ? POS_HUGE_PHLOAT : NEG_HUGE_PHLOAT; else return ERR_OUT_OF_RANGE; } *zre = rre; *zim = rim; return ERR_NONE; } free42-nologo-1.4.77/common/core_sto_rcl.h000644 000765 000024 00000010144 12110237247 020747 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_STO_RCL_H #define CORE_STO_RCL_H 1 #include "free42.h" #include "core_phloat.h" #include "core_globals.h" /************************************************/ /* Signatures for functions mapped by map_unary */ /************************************************/ typedef int (*mappable_r)(phloat x, phloat *z); typedef int (*mappable_c)(phloat xre, phloat xim, phloat *zre, phloat *zim); /*************************************************/ /* Signatures for functions mapped by map_binary */ /*************************************************/ typedef int (*mappable_rr)(phloat x, phloat y, phloat *z); typedef int (*mappable_rc)(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim); typedef int (*mappable_cr)(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim); typedef int (*mappable_cc)(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim); /****************************************************************/ /* Generic arithmetic operators, for use in the implementations */ /* of +, -, *, /, STO+, STO-, etc... */ /****************************************************************/ int generic_div(const vartype *x, const vartype *y, void (*completion)(int, vartype *)); int generic_mul(const vartype *x, const vartype *y, void (*completion)(int, vartype *)); int generic_sub(const vartype *x, const vartype *y, vartype **res); int generic_add(const vartype *x, const vartype *y, vartype **res); int generic_rcl(arg_struct *arg, vartype **dst); int generic_sto(arg_struct *arg, char operation); /**********************************************/ /* Mappers to apply unary or binary operators */ /* to arbitrary parameter types */ /**********************************************/ int map_unary(const vartype *src, vartype **dst, mappable_r, mappable_c mc); int map_binary(const vartype *src1, const vartype *src2, vartype **dst, mappable_rr mrr, mappable_rc mrc, mappable_cr mcr, mappable_cc mcc); /**************************************************************/ /* Operators that can be used by the mapping functions, above */ /**************************************************************/ int div_rr(phloat x, phloat y, phloat *z); int div_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim); int div_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim); int div_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim); int mul_rr(phloat x, phloat y, phloat *z); int mul_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim); int mul_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim); int mul_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim); int sub_rr(phloat x, phloat y, phloat *z); int sub_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim); int sub_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim); int sub_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim); int add_rr(phloat x, phloat y, phloat *z); int add_rc(phloat x, phloat yre, phloat yim, phloat *zre, phloat *zim); int add_cr(phloat xre, phloat xim, phloat y, phloat *zre, phloat *zim); int add_cc(phloat xre, phloat xim, phloat yre, phloat yim, phloat *zre, phloat *zim); #endif free42-nologo-1.4.77/common/core_tables.cc000644 000765 000024 00000151460 12110237247 020721 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_tables.h" #include "core_commands1.h" #include "core_commands2.h" #include "core_commands3.h" #include "core_commands4.h" #include "core_commands5.h" #include "core_commands6.h" #include "core_commands7.h" #ifndef COPAN #define docmd_openf docmd_xrom #define docmd_closef docmd_xrom #define docmd_readp docmd_xrom #define docmd_writp docmd_xrom #define docmd_getxy docmd_xrom #define docmd_putxy docmd_xrom #define docmd_clrp docmd_xrom #define docmd_clrd docmd_xrom #define docmd_appd docmd_xrom #define docmd_getn docmd_xrom #define docmd_putn docmd_xrom #define docmd_getz docmd_xrom #define docmd_putz docmd_xrom #define docmd_delp docmd_xrom #endif #ifndef BIGSTACK #define docmd_drop docmd_xrom #endif #if !defined(ANDROID) && !defined(IPHONE) #define docmd_accel docmd_xrom #define docmd_locat docmd_xrom #define docmd_heading docmd_xrom #endif /* PalmOS: even though this array is declared "const", it ends up in the * globals area. Is this because of the function pointers? */ static const command_spec cmd_array[] = { { /* CLX */ "CLX", 3, docmd_clx, 0x00000077, ARG_NONE, FLAG_NONE }, { /* ENTER */ "ENT\305R", 5, docmd_enter, 0x00000083, ARG_NONE, FLAG_NONE }, { /* SWAP */ "X<>Y", 4, docmd_swap, 0x00000071, ARG_NONE, FLAG_NONE }, { /* RDN */ "R\016", 2, docmd_rdn, 0x00000075, ARG_NONE, FLAG_NONE }, { /* CHS */ "+/-", 3, docmd_chs, 0x00000054, ARG_NONE, FLAG_NONE }, { /* DIV */ "\000", 1, docmd_div, 0x00000043, ARG_NONE, FLAG_NONE }, { /* MUL */ "\001", 1, docmd_mul, 0x00000042, ARG_NONE, FLAG_NONE }, { /* SUB */ "-", 1, docmd_sub, 0x00000041, ARG_NONE, FLAG_NONE }, { /* ADD */ "+", 1, docmd_add, 0x00000040, ARG_NONE, FLAG_NONE }, { /* LASTX */ "LASTX", 5, docmd_lastx, 0x00000076, ARG_NONE, FLAG_NONE }, { /* SILENT_OFF */ "", 0, NULL, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_SHOW }, { /* SILENT_ON */ "", 0, NULL, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_SHOW }, { /* SIN */ "SIN", 3, docmd_sin, 0x00000059, ARG_NONE, FLAG_NONE }, { /* COS */ "COS", 3, docmd_cos, 0x0000005a, ARG_NONE, FLAG_NONE }, { /* TAN */ "TAN", 3, docmd_tan, 0x0000005b, ARG_NONE, FLAG_NONE }, { /* ASIN */ "ASIN", 4, docmd_asin, 0x0000005c, ARG_NONE, FLAG_NONE }, { /* ACOS */ "ACOS", 4, docmd_acos, 0x0000005d, ARG_NONE, FLAG_NONE }, { /* ATAN */ "ATAN", 4, docmd_atan, 0x0000005e, ARG_NONE, FLAG_NONE }, { /* LOG */ "LOG", 3, docmd_log, 0x00000056, ARG_NONE, FLAG_NONE }, { /* 10_POW_X */ "10^X", 4, docmd_10_pow_x, 0x00000057, ARG_NONE, FLAG_NONE }, { /* LN */ "LN", 2, docmd_ln, 0x00000050, ARG_NONE, FLAG_NONE }, { /* E_POW_X */ "E^X", 3, docmd_e_pow_x, 0x00000055, ARG_NONE, FLAG_NONE }, { /* SQRT */ "SQRT", 4, docmd_sqrt, 0x00000052, ARG_NONE, FLAG_NONE }, { /* SQUARE */ "X^2", 3, docmd_square, 0x00000051, ARG_NONE, FLAG_NONE }, { /* INV */ "1/X", 3, docmd_inv, 0x00000060, ARG_NONE, FLAG_NONE }, { /* Y_POW_X */ "Y^X", 3, docmd_y_pow_x, 0x00000053, ARG_NONE, FLAG_NONE }, { /* PERCENT */ "%", 1, docmd_percent, 0x0000004c, ARG_NONE, FLAG_NONE }, { /* PI */ "PI", 2, docmd_pi, 0x00000072, ARG_NONE, FLAG_NONE }, { /* COMPLEX */ "C\317\315PL\305X", 7, docmd_complex, 0x0000a072, ARG_NONE, FLAG_NONE }, { /* STO */ "STO", 3, docmd_sto, 0x01810091, ARG_VAR, FLAG_NONE }, { /* STO_DIV */ "STO\000", 4, docmd_sto_div, 0x00850095, ARG_VAR, FLAG_NONE }, { /* STO_MUL */ "STO\001", 4, docmd_sto_mul, 0x00840094, ARG_VAR, FLAG_NONE }, { /* STO_SUB */ "STO-", 4, docmd_sto_sub, 0x00830093, ARG_VAR, FLAG_NONE }, { /* STO_ADD */ "STO+", 4, docmd_sto_add, 0x00820092, ARG_VAR, FLAG_NONE }, { /* RCL */ "RCL", 3, docmd_rcl, 0x01910090, ARG_VAR, FLAG_NONE }, { /* RCL_DIV */ "RCL\000", 4, docmd_rcl_div, 0x0095f2d4, ARG_VAR, FLAG_NONE }, { /* RCL_MUL */ "RCL\001", 4, docmd_rcl_mul, 0x0094f2d3, ARG_VAR, FLAG_NONE }, { /* RCL_SUB */ "RCL-", 4, docmd_rcl_sub, 0x0093f2d2, ARG_VAR, FLAG_NONE }, { /* RCL_ADD */ "RCL+", 4, docmd_rcl_add, 0x0092f2d1, ARG_VAR, FLAG_NONE }, { /* FIX */ "FIX", 3, docmd_fix, 0x01d4009c, ARG_NUM11, FLAG_NONE }, { /* SCI */ "SCI", 3, docmd_sci, 0x01d5009d, ARG_NUM11, FLAG_NONE }, { /* ENG */ "ENG", 3, docmd_eng, 0x01d6009e, ARG_NUM11, FLAG_NONE }, { /* ALL */ "ALL", 3, docmd_all, 0x0000a25d, ARG_NONE, FLAG_NONE }, { /* NULL */ "\316\325\314\314", 4, docmd_null, 0x02000000, ARG_NONE, FLAG_HIDDEN }, { /* ASTO */ "ASTO", 4, docmd_asto, 0x00b2009a, ARG_VAR, FLAG_NONE }, { /* ARCL */ "ARCL", 4, docmd_arcl, 0x00b3009b, ARG_VAR, FLAG_NONE }, { /* CLA */ "CLA", 3, docmd_cla, 0x00000087, ARG_NONE, FLAG_NONE }, { /* DEG */ "DEG", 3, docmd_deg, 0x00000080, ARG_NONE, FLAG_NONE }, { /* RAD */ "RAD", 3, docmd_rad, 0x00000081, ARG_NONE, FLAG_NONE }, { /* GRAD */ "GRAD", 4, docmd_grad, 0x00000082, ARG_NONE, FLAG_NONE }, { /* RECT */ "RECT", 4, docmd_rect, 0x0000a25a, ARG_NONE, FLAG_NONE }, { /* POLAR */ "POLAR", 5, docmd_polar, 0x0000a259, ARG_NONE, FLAG_NONE }, { /* SIZE */ "SIZE", 4, docmd_size, 0x01000000, ARG_COUNT, FLAG_NONE }, { /* QUIET */ "QUIET", 5, docmd_quiet, 0x02000000, ARG_NONE, FLAG_IMMED }, { /* CPXRES */ "C\320\330RES", 6, docmd_cpxres, 0x0000a26a, ARG_NONE, FLAG_NONE }, { /* REALRES */ "R\305\301\314RES", 7, docmd_realres, 0x0000a26b, ARG_NONE, FLAG_NONE }, { /* KEYASN */ "KEY\301\323\316", 6, docmd_keyasn, 0x0000a263, ARG_NONE, FLAG_NONE }, { /* LCLBL */ "LCLBL", 5, docmd_lclbl, 0x0000a264, ARG_NONE, FLAG_NONE }, { /* RDXDOT */ "RDX.", 4, docmd_rdxdot, 0x0000a25b, ARG_NONE, FLAG_NONE }, { /* RDXCOMMA */ "RDX,", 4, docmd_rdxcomma, 0x0000a25c, ARG_NONE, FLAG_NONE }, { /* CLSIGMA */ "CL\005", 3, docmd_clsigma, 0x00000070, ARG_NONE, FLAG_NONE }, { /* CLP */ "CLP", 3, docmd_clp, 0x00f00000, ARG_PRGM, FLAG_NONE }, { /* CLV */ "CLV", 3, docmd_clv, 0x00b0f2d8, ARG_NAMED, FLAG_NONE }, { /* CLST */ "CLST", 4, docmd_clst, 0x00000073, ARG_NONE, FLAG_NONE }, { /* CLRG */ "CLRG", 4, docmd_clrg, 0x0000008a, ARG_NONE, FLAG_NONE }, { /* DEL */ "DEL", 3, docmd_del, 0x02000000, ARG_COUNT, FLAG_PRGM_ONLY + FLAG_IMMED }, { /* CLKEYS */ "CLK\305Y\323", 6, docmd_clkeys, 0x0000a262, ARG_NONE, FLAG_NONE }, { /* CLLCD */ "CLLCD", 5, docmd_cllcd, 0x0000a763, ARG_NONE, FLAG_NONE }, { /* CLMENU */ "CLM\305N\325", 6, docmd_clmenu, 0x0000a26d, ARG_NONE, FLAG_NONE }, { /* CLALLa */ "CLALL", 5, NULL, 0x02000000, ARG_NONE, FLAG_IMMED }, { /* TO_DEG */ "\017DEG", 4, docmd_to_deg, 0x0000006b, ARG_NONE, FLAG_NONE }, { /* TO_RAD */ "\017RAD", 4, docmd_to_rad, 0x0000006a, ARG_NONE, FLAG_NONE }, { /* TO_HR */ "\017HR", 3, docmd_to_hr, 0x0000006d, ARG_NONE, FLAG_NONE }, { /* TO_HMS */ "\017HMS", 4, docmd_to_hms, 0x0000006c, ARG_NONE, FLAG_NONE }, { /* TO_REC */ "\017REC", 4, docmd_to_rec, 0x0000004e, ARG_NONE, FLAG_NONE }, { /* TO_POL */ "\017POL", 4, docmd_to_pol, 0x0000004f, ARG_NONE, FLAG_NONE }, { /* IP */ "IP", 2, docmd_ip, 0x00000068, ARG_NONE, FLAG_NONE }, { /* FP */ "FP", 2, docmd_fp, 0x00000069, ARG_NONE, FLAG_NONE }, { /* RND */ "RND", 3, docmd_rnd, 0x0000006e, ARG_NONE, FLAG_NONE }, { /* ABS */ "ABS", 3, docmd_abs, 0x00000061, ARG_NONE, FLAG_NONE }, { /* SIGN */ "SIGN", 4, docmd_sign, 0x0000007a, ARG_NONE, FLAG_NONE }, { /* MOD */ "MOD", 3, docmd_mod, 0x0000004b, ARG_NONE, FLAG_NONE }, { /* SF */ "SF", 2, docmd_sf, 0x00a000a8, ARG_NUM99, FLAG_NONE }, { /* CF */ "CF", 2, docmd_cf, 0x00a100a9, ARG_NUM99, FLAG_NONE }, { /* FS_T */ "FS?", 3, docmd_fs_t, 0x00a400ac, ARG_NUM99, FLAG_NONE }, { /* FC_T */ "FC?", 3, docmd_fc_t, 0x00a500ad, ARG_NUM99, FLAG_NONE }, { /* FSC_T */ "FS?C", 4, docmd_fsc_t, 0x00a200aa, ARG_NUM99, FLAG_NONE }, { /* FCC_T */ "FC?C", 4, docmd_fcc_t, 0x00a300ab, ARG_NUM99, FLAG_NONE }, { /* COMB */ "COMB", 4, docmd_comb, 0x0000a06f, ARG_NONE, FLAG_NONE }, { /* PERM */ "PERM", 4, docmd_perm, 0x0000a070, ARG_NONE, FLAG_NONE }, { /* FACT */ "N!", 2, docmd_fact, 0x00000062, ARG_NONE, FLAG_NONE }, { /* GAMMA */ "GAM\315\301", 5, docmd_gamma, 0x0000a074, ARG_NONE, FLAG_NONE }, { /* RAN */ "RAN", 3, docmd_ran, 0x0000a071, ARG_NONE, FLAG_NONE }, { /* SEED */ "SEED", 4, docmd_seed, 0x0000a073, ARG_NONE, FLAG_NONE }, { /* LBL */ "LBL", 3, docmd_lbl, 0x010000cf, ARG_OTHER, FLAG_PRGM_ONLY }, { /* RTN */ "RTN", 3, docmd_rtn, 0x00000085, ARG_NONE, FLAG_NONE }, { /* INPUT */ "INPUT", 5, docmd_input, 0x01c5f2d0, ARG_VAR, FLAG_PRGM_ONLY }, { /* VIEW */ "VIEW", 4, docmd_view, 0x00800098, ARG_VAR, FLAG_NONE }, { /* AVIEW */ "AVIEW", 5, docmd_aview, 0x0000007e, ARG_NONE, FLAG_NONE }, { /* XEQ */ "XEQ", 3, docmd_xeq, 0x01a700ae, ARG_LBL, FLAG_NONE }, { /* PROMPT */ "PROM\320\324", 6, docmd_prompt, 0x0000008e, ARG_NONE, FLAG_NONE }, { /* PSE */ "PSE", 3, docmd_pse, 0x00000089, ARG_NONE, FLAG_NONE }, { /* ISG */ "ISG", 3, docmd_isg, 0x00960096, ARG_REAL, FLAG_NONE }, { /* DSE */ "DSE", 3, docmd_dse, 0x00970097, ARG_REAL, FLAG_NONE }, { /* AIP */ "AIP", 3, docmd_aip, 0x0000a631, ARG_NONE, FLAG_NONE }, { /* XTOA */ "XTOA", 4, docmd_xtoa, 0x0000a66f, ARG_NONE, FLAG_NONE }, { /* AGRAPH */ "AGRA\320\310", 6, docmd_agraph, 0x0000a764, ARG_NONE, FLAG_NONE }, { /* PIXEL */ "PIXEL", 5, docmd_pixel, 0x0000a765, ARG_NONE, FLAG_NONE }, { /* BEEP */ "BEEP", 4, docmd_beep, 0x00000086, ARG_NONE, FLAG_NONE }, { /* TONE */ "TONE", 4, docmd_tone, 0x00d7009f, ARG_NUM9, FLAG_NONE }, { /* MVAR */ "MVAR", 4, docmd_mvar, 0x00900000, ARG_RVAR, FLAG_NONE }, { /* VARMENU */ "VARM\305\316\325", 7, docmd_varmenu, 0x00c1f2f8, ARG_PRGM, FLAG_NONE }, { /* GETKEY */ "GETK\305\331", 6, docmd_getkey, 0x0000a26e, ARG_NONE, FLAG_NONE }, { /* MENU */ "MENU", 4, docmd_menu, 0x0000a25e, ARG_NONE, FLAG_PRGM_ONLY }, { /* KEYG */ "KEYG", 4, NULL, 0x02000000, ARG_MKEY, FLAG_NONE }, { /* KEYX */ "KEYX", 4, NULL, 0x02000000, ARG_MKEY, FLAG_NONE }, { /* X_EQ_0 */ "X=0?", 4, docmd_x_eq_0, 0x00000067, ARG_NONE, FLAG_NONE }, { /* X_NE_0 */ "X\0140?", 4, docmd_x_ne_0, 0x00000063, ARG_NONE, FLAG_NONE }, { /* X_LT_0 */ "X<0?", 4, docmd_x_lt_0, 0x00000066, ARG_NONE, FLAG_NONE }, { /* X_GT_0 */ "X>0?", 4, docmd_x_gt_0, 0x00000064, ARG_NONE, FLAG_NONE }, { /* X_LE_0 */ "X\0110?", 4, docmd_x_le_0, 0x0000007b, ARG_NONE, FLAG_NONE }, { /* X_GE_0 */ "X\0130?", 4, docmd_x_ge_0, 0x0000a25f, ARG_NONE, FLAG_NONE }, { /* X_EQ_Y */ "X=Y?", 4, docmd_x_eq_y, 0x00000078, ARG_NONE, FLAG_NONE }, { /* X_NE_Y */ "X\014Y?", 4, docmd_x_ne_y, 0x00000079, ARG_NONE, FLAG_NONE }, { /* X_LT_Y */ "XY?", 4, docmd_x_gt_y, 0x00000045, ARG_NONE, FLAG_NONE }, { /* X_LE_Y */ "X\011Y?", 4, docmd_x_le_y, 0x00000046, ARG_NONE, FLAG_NONE }, { /* X_GE_Y */ "X\013Y?", 4, docmd_x_ge_y, 0x0000a260, ARG_NONE, FLAG_NONE }, { /* PRSIGMA */ "PR\005", 3, docmd_prsigma, 0x0000a752, ARG_NONE, FLAG_NONE }, { /* PRP */ "PRP", 3, docmd_prp, 0x02000000, ARG_PRGM, FLAG_IMMED }, { /* PRV */ "PRV", 3, docmd_prv, 0x00b1f2d9, ARG_NAMED, FLAG_NONE }, { /* PRSTK */ "PRST\313", 5, docmd_prstk, 0x0000a753, ARG_NONE, FLAG_NONE }, { /* PRA */ "PRA", 3, docmd_pra, 0x0000a748, ARG_NONE, FLAG_NONE }, { /* PRX */ "PRX", 3, docmd_prx, 0x0000a754, ARG_NONE, FLAG_NONE }, { /* PRUSR */ "PRUSR", 5, docmd_prusr, 0x0000a761, ARG_NONE, FLAG_NONE }, { /* LIST */ "LIST", 4, docmd_list, 0x02000000, ARG_COUNT, FLAG_IMMED }, { /* ADV */ "ADV", 3, docmd_adv, 0x0000008f, ARG_NONE, FLAG_NONE }, { /* PRLCD */ "PRLCD", 5, docmd_prlcd, 0x0000a762, ARG_NONE, FLAG_NONE }, { /* DELAY */ "DELAY", 5, docmd_delay, 0x0000a760, ARG_NONE, FLAG_NONE }, { /* PON */ "P\322ON", 4, docmd_pon, 0x0000a75e, ARG_NONE, FLAG_NONE }, { /* POFF */ "P\322OFF", 5, docmd_poff, 0x0000a75f, ARG_NONE, FLAG_NONE }, { /* MAN */ "MAN", 3, docmd_man, 0x0000a75b, ARG_NONE, FLAG_NONE }, { /* NORM */ "NORM", 4, docmd_norm, 0x0000a75c, ARG_NONE, FLAG_NONE }, { /* TRACE */ "TRACE", 5, docmd_trace, 0x0000a75d, ARG_NONE, FLAG_NONE }, { /* SIGMAADD */ "\005+", 2, docmd_sigmaadd, 0x00000047, ARG_NONE, FLAG_NONE }, { /* SIGMASUB */ "\005-", 2, docmd_sigmasub, 0x00000048, ARG_NONE, FLAG_NONE }, { /* GTO */ "GTO", 3, docmd_gto, 0x01a60000, ARG_LBL, FLAG_NONE }, { /* END */ "END", 3, docmd_end, 0x01000000, ARG_NONE, FLAG_NONE }, { /* NUMBER */ "", 0, docmd_number, 0x01000000, ARG_NONE, FLAG_HIDDEN }, { /* STRING */ "", 0, docmd_string, 0x01000000, ARG_NONE, FLAG_HIDDEN }, { /* RUN */ "RUN", 3, NULL, 0x02000000, ARG_NONE, FLAG_HIDDEN }, { /* SST */ "SST", 3, NULL, 0x02000000, ARG_NONE, FLAG_NONE }, { /* GTODOT */ "GTO .", 5, docmd_gtodot, 0x02000000, ARG_OTHER, FLAG_IMMED }, { /* GTODOTDOT */ "GTO ..", 6, docmd_gtodotdot, 0x02000000, ARG_NONE, FLAG_IMMED }, { /* STOP */ "STOP", 4, docmd_stop, 0x00000084, ARG_NONE, FLAG_NONE }, { /* NEWMAT */ "NEW\315\301\324", 6, docmd_newmat, 0x0000a6da, ARG_NONE, FLAG_NONE }, { /* RUP */ "R^", 2, docmd_rup, 0x00000074, ARG_NONE, FLAG_NONE }, { /* REAL_T */ "RE\301L?", 5, docmd_real_t, 0x0000a265, ARG_NONE, FLAG_NONE }, { /* CPX_T */ "CPX?", 4, docmd_cpx_t, 0x0000a267, ARG_NONE, FLAG_NONE }, { /* STR_T */ "STR?", 4, docmd_str_t, 0x0000a268, ARG_NONE, FLAG_NONE }, { /* MAT_T */ "MAT?", 4, docmd_mat_t, 0x0000a266, ARG_NONE, FLAG_NONE }, { /* DIM_T */ "DIM?", 4, docmd_dim_t, 0x0000a6e7, ARG_NONE, FLAG_NONE }, { /* ASSIGNa */ "AS\323\311GN", 6, NULL, 0x02000000, ARG_NAMED, FLAG_NONE }, { /* ASSIGNb */ "", 0, NULL, 0x02000000, ARG_CKEY, FLAG_HIDDEN }, { /* ASGN01 */ "", 0, docmd_asgn01, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN02 */ "", 0, docmd_asgn02, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN03 */ "", 0, docmd_asgn03, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN04 */ "", 0, docmd_asgn04, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN05 */ "", 0, docmd_asgn05, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN06 */ "", 0, docmd_asgn06, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN07 */ "", 0, docmd_asgn07, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN08 */ "", 0, docmd_asgn08, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN09 */ "", 0, docmd_asgn09, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN10 */ "", 0, docmd_asgn10, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN11 */ "", 0, docmd_asgn11, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN12 */ "", 0, docmd_asgn12, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN13 */ "", 0, docmd_asgn13, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN14 */ "", 0, docmd_asgn14, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN15 */ "", 0, docmd_asgn15, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN16 */ "", 0, docmd_asgn16, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN17 */ "", 0, docmd_asgn17, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ASGN18 */ "", 0, docmd_asgn18, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, { /* ON */ "ON", 2, docmd_on, 0x0000a270, ARG_NONE, FLAG_NONE }, { /* OFF */ "OFF", 3, docmd_off, 0x0000008d, ARG_NONE, FLAG_NONE }, { /* KEY1G */ "KEY 1 GTO", 9, docmd_key1g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY2G */ "KEY 2 GTO", 9, docmd_key2g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY3G */ "KEY 3 GTO", 9, docmd_key3g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY4G */ "KEY 4 GTO", 9, docmd_key4g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY5G */ "KEY 5 GTO", 9, docmd_key5g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY6G */ "KEY 6 GTO", 9, docmd_key6g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY7G */ "KEY 7 GTO", 9, docmd_key7g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY8G */ "KEY 8 GTO", 9, docmd_key8g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY9G */ "KEY 9 GTO", 9, docmd_key9g, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY1X */ "KEY 1 XEQ", 9, docmd_key1x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY2X */ "KEY 2 XEQ", 9, docmd_key2x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY3X */ "KEY 3 XEQ", 9, docmd_key3x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY4X */ "KEY 4 XEQ", 9, docmd_key4x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY5X */ "KEY 5 XEQ", 9, docmd_key5x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY6X */ "KEY 6 XEQ", 9, docmd_key6x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY7X */ "KEY 7 XEQ", 9, docmd_key7x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY8X */ "KEY 8 XEQ", 9, docmd_key8x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* KEY9X */ "KEY 9 XEQ", 9, docmd_key9x, 0x01000000, ARG_LBL, FLAG_HIDDEN }, { /* VMEXEC */ "", 0, NULL, 0x02000000, ARG_OTHER, FLAG_HIDDEN }, { /* VMSTO */ "STO", 3, docmd_vmsto, 0x02000000, ARG_OTHER, FLAG_HIDDEN }, { /* SIGMAREG */ "\005REG", 4, docmd_sigma_reg, 0x00d30099, ARG_NUM99, FLAG_NONE }, { /* SIGMAREG_T */ "\005R\305G?", 5, docmd_sigma_reg_t, 0x0000a678, ARG_NONE, FLAG_NONE }, { /* CLD */ "CLD", 3, docmd_cld, 0x0000007f, ARG_NONE, FLAG_NONE }, { /* ACOSH */ "ACOSH", 5, docmd_acosh, 0x0000a066, ARG_NONE, FLAG_NONE }, { /* ALENG */ "ALEN\307", 5, docmd_aleng, 0x0000a641, ARG_NONE, FLAG_NONE }, { /* ALLSIGMA */ "ALL\005", 4, docmd_allsigma, 0x0000a0ae, ARG_NONE, FLAG_NONE }, { /* AND */ "AND", 3, docmd_and, 0x0000a588, ARG_NONE, FLAG_NONE }, { /* AOFF */ "AOFF", 4, docmd_aoff, 0x0000008b, ARG_NONE, FLAG_NONE }, { /* AON */ "AON", 3, docmd_aon, 0x0000008c, ARG_NONE, FLAG_NONE }, { /* AROT */ "AROT", 4, docmd_arot, 0x0000a646, ARG_NONE, FLAG_NONE }, { /* ASHF */ "ASHF", 4, docmd_ashf, 0x00000088, ARG_NONE, FLAG_NONE }, { /* ASINH */ "ASINH", 5, docmd_asinh, 0x0000a064, ARG_NONE, FLAG_NONE }, { /* ATANH */ "AT\301NH", 5, docmd_atanh, 0x0000a065, ARG_NONE, FLAG_NONE }, { /* ATOX */ "ATOX", 4, docmd_atox, 0x0000a647, ARG_NONE, FLAG_NONE }, { /* BASEADD */ "BASE+", 5, docmd_baseadd, 0x0000a0e6, ARG_NONE, FLAG_NONE }, { /* BASESUB */ "BASE-", 5, docmd_basesub, 0x0000a0e7, ARG_NONE, FLAG_NONE }, { /* BASEMUL */ "BASE\001", 5, docmd_basemul, 0x0000a0e8, ARG_NONE, FLAG_NONE }, { /* BASEDIV */ "BASE\000", 5, docmd_basediv, 0x0000a0e9, ARG_NONE, FLAG_NONE }, { /* BASECHS */ "B\301\323\305+/-", 7, docmd_basechs, 0x0000a0ea, ARG_NONE, FLAG_NONE }, { /* BEST */ "BEST", 4, docmd_best, 0x0000a09f, ARG_NONE, FLAG_NONE }, { /* BINM */ "BINM", 4, docmd_binm, 0x0000a0e5, ARG_NONE, FLAG_NONE }, { /* BIT_T */ "BIT?", 4, docmd_bit_t, 0x0000a58c, ARG_NONE, FLAG_NONE }, { /* BST */ "BST", 3, NULL, 0x02000000, ARG_NONE, FLAG_NONE }, { /* CORR */ "CORR", 4, docmd_corr, 0x0000a0a7, ARG_NONE, FLAG_NONE }, { /* COSH */ "COSH", 4, docmd_cosh, 0x0000a062, ARG_NONE, FLAG_NONE }, { /* CROSS */ "CROSS", 5, docmd_cross, 0x0000a6ca, ARG_NONE, FLAG_NONE }, { /* CUSTOM */ "CUST\317\315", 6, docmd_custom, 0x0000a26f, ARG_NONE, FLAG_NONE }, { /* DECM */ "DECM", 4, docmd_decm, 0x0000a0e3, ARG_NONE, FLAG_NONE }, { /* DELR */ "DELR", 4, docmd_delr, 0x0000a0ab, ARG_NONE, FLAG_NONE }, { /* DET */ "DET", 3, docmd_det, 0x0000a6cc, ARG_NONE, FLAG_NONE }, { /* DIM */ "DIM", 3, docmd_dim, 0x00c4f2ec, ARG_MAT, FLAG_NONE }, { /* DOT */ "DOT", 3, docmd_dot, 0x0000a6cb, ARG_NONE, FLAG_NONE }, { /* EDIT */ "EDIT", 4, docmd_edit, 0x0000a6e1, ARG_NONE, FLAG_NONE }, { /* EDITN */ "EDITN", 5, docmd_editn, 0x00c6f2ef, ARG_MAT, FLAG_NONE }, { /* EXITALL */ "EXITA\314\314", 7, docmd_exitall, 0x0000a26c, ARG_NONE, FLAG_NONE }, { /* EXPF */ "EXPF", 4, docmd_expf, 0x0000a0a0, ARG_NONE, FLAG_NONE }, { /* E_POW_X_1 */ "E^X-\261", 5, docmd_e_pow_x_1, 0x00000058, ARG_NONE, FLAG_NONE }, { /* FCSTX */ "FCSTX", 5, docmd_fcstx, 0x0000a0a8, ARG_NONE, FLAG_NONE }, { /* FCSTY */ "FCSTY", 5, docmd_fcsty, 0x0000a0a9, ARG_NONE, FLAG_NONE }, { /* FNRM */ "FNRM", 4, docmd_fnrm, 0x0000a6cf, ARG_NONE, FLAG_NONE }, { /* GETM */ "GETM", 4, docmd_getm, 0x0000a6e8, ARG_NONE, FLAG_NONE }, { /* GROW */ "GROW", 4, docmd_grow, 0x0000a6e3, ARG_NONE, FLAG_NONE }, { /* HEXM */ "HEXM", 4, docmd_hexm, 0x0000a0e2, ARG_NONE, FLAG_NONE }, { /* HMSADD */ "HMS+", 4, docmd_hmsadd, 0x00000049, ARG_NONE, FLAG_NONE }, { /* HMSSUB */ "HMS-", 4, docmd_hmssub, 0x0000004a, ARG_NONE, FLAG_NONE }, { /* I_ADD */ "I+", 2, docmd_i_add, 0x0000a6d2, ARG_NONE, FLAG_NONE }, { /* I_SUB */ "I-", 2, docmd_i_sub, 0x0000a6d3, ARG_NONE, FLAG_NONE }, { /* INDEX */ "INDEX", 5, docmd_index, 0x0087f2da, ARG_MAT, FLAG_NONE }, { /* INSR */ "INSR", 4, docmd_insr, 0x0000a0aa, ARG_NONE, FLAG_NONE }, { /* INTEG */ "INTEG", 5, docmd_integ, 0x00b6f2ea, ARG_RVAR, FLAG_NONE }, { /* INVRT */ "INV\322\324", 5, docmd_invrt, 0x0000a6ce, ARG_NONE, FLAG_NONE }, { /* J_ADD */ "J+", 2, docmd_j_add, 0x0000a6d4, ARG_NONE, FLAG_NONE }, { /* J_SUB */ "J-", 2, docmd_j_sub, 0x0000a6d5, ARG_NONE, FLAG_NONE }, { /* LINF */ "LINF", 4, docmd_linf, 0x0000a0a1, ARG_NONE, FLAG_NONE }, { /* LINSIGMA */ "LIN\005", 4, docmd_linsigma, 0x0000a0ad, ARG_NONE, FLAG_NONE }, { /* LN_1_X */ "LN1+\330", 5, docmd_ln_1_x, 0x00000065, ARG_NONE, FLAG_NONE }, { /* LOGF */ "LOGF", 4, docmd_logf, 0x0000a0a2, ARG_NONE, FLAG_NONE }, { /* MEAN */ "MEAN", 4, docmd_mean, 0x0000007c, ARG_NONE, FLAG_NONE }, { /* NOT */ "NOT", 3, docmd_not, 0x0000a587, ARG_NONE, FLAG_NONE }, { /* OCTM */ "OCTM", 4, docmd_octm, 0x0000a0e4, ARG_NONE, FLAG_NONE }, { /* OLD */ "OLD", 3, docmd_old, 0x0000a6db, ARG_NONE, FLAG_NONE }, { /* OR */ "OR", 2, docmd_or, 0x0000a589, ARG_NONE, FLAG_NONE }, { /* PGMSLV */ "P\307\315SLV", 6, docmd_pgmslv, 0x00b5f2e9, ARG_PRGM, FLAG_NONE }, { /* PGMINT */ "P\307\315INT", 6, docmd_pgmint, 0x00b4f2e8, ARG_PRGM, FLAG_NONE }, { /* POSA */ "POSA", 4, docmd_posa, 0x0000a65c, ARG_NONE, FLAG_NONE }, { /* PUTM */ "PUTM", 4, docmd_putm, 0x0000a6e9, ARG_NONE, FLAG_NONE }, { /* PWRF */ "PWRF", 4, docmd_pwrf, 0x0000a0a3, ARG_NONE, FLAG_NONE }, { /* RCLEL */ "RCLEL", 5, docmd_rclel, 0x0000a6d7, ARG_NONE, FLAG_NONE }, { /* RCLIJ */ "RCLIJ", 5, docmd_rclij, 0x0000a6d9, ARG_NONE, FLAG_NONE }, { /* RNRM */ "RNRM", 4, docmd_rnrm, 0x0000a6ed, ARG_NONE, FLAG_NONE }, { /* ROTXY */ "ROTXY", 5, docmd_rotxy, 0x0000a58b, ARG_NONE, FLAG_NONE }, { /* RSUM */ "RSUM", 4, docmd_rsum, 0x0000a6d0, ARG_NONE, FLAG_NONE }, { /* SWAP_R */ "R<>R", 4, docmd_swap_r, 0x0000a6d1, ARG_NONE, FLAG_NONE }, { /* SDEV */ "SDEV", 4, docmd_sdev, 0x0000007d, ARG_NONE, FLAG_NONE }, { /* SINH */ "SINH", 4, docmd_sinh, 0x0000a061, ARG_NONE, FLAG_NONE }, { /* SLOPE */ "SLOPE", 5, docmd_slope, 0x0000a0a4, ARG_NONE, FLAG_NONE }, { /* SOLVE */ "SOLVE", 5, docmd_solve, 0x00b7f2eb, ARG_RVAR, FLAG_NONE }, { /* STOEL */ "STOEL", 5, docmd_stoel, 0x0000a6d6, ARG_NONE, FLAG_NONE }, { /* STOIJ */ "STOIJ", 5, docmd_stoij, 0x0000a6d8, ARG_NONE, FLAG_NONE }, { /* SUM */ "SUM", 3, docmd_sum, 0x0000a0a5, ARG_NONE, FLAG_NONE }, { /* TANH */ "TANH", 4, docmd_tanh, 0x0000a063, ARG_NONE, FLAG_NONE }, { /* TRANS */ "TRANS", 5, docmd_trans, 0x0000a6c9, ARG_NONE, FLAG_NONE }, { /* UVEC */ "UVEC", 4, docmd_uvec, 0x0000a6cd, ARG_NONE, FLAG_NONE }, { /* WMEAN */ "WM\305\301N", 5, docmd_wmean, 0x0000a0ac, ARG_NONE, FLAG_NONE }, { /* WRAP */ "WRAP", 4, docmd_wrap, 0x0000a6e2, ARG_NONE, FLAG_NONE }, { /* X_SWAP */ "X<>", 3, docmd_x_swap, 0x008600ce, ARG_VAR, FLAG_NONE }, { /* XOR */ "XOR", 3, docmd_xor, 0x0000a58a, ARG_NONE, FLAG_NONE }, { /* YINT */ "YINT", 4, docmd_yint, 0x0000a0a6, ARG_NONE, FLAG_NONE }, { /* TO_DEC */ "\017DEC", 4, docmd_to_dec, 0x0000005f, ARG_NONE, FLAG_NONE }, { /* TO_OCT */ "\017OCT", 4, docmd_to_oct, 0x0000006f, ARG_NONE, FLAG_NONE }, { /* LEFT */ "\020", 1, docmd_left, 0x0000a6dc, ARG_NONE, FLAG_NONE }, { /* UP */ "^", 1, docmd_up, 0x0000a6de, ARG_NONE, FLAG_NONE }, { /* DOWN */ "\016", 1, docmd_down, 0x0000a6df, ARG_NONE, FLAG_NONE }, { /* RIGHT */ "\017", 1, docmd_right, 0x0000a6dd, ARG_NONE, FLAG_NONE }, { /* PERCENT_CH */ "%CH", 3, docmd_percent_ch, 0x0000004d, ARG_NONE, FLAG_NONE }, { /* SIMQ */ "SIMQ", 4, docmd_simq, 0x02000000, ARG_COUNT, FLAG_HIDDEN + FLAG_NO_PRGM }, { /* MATA */ "MATA", 4, docmd_mata, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_PRGM }, { /* MATB */ "MATB", 4, docmd_matb, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_PRGM }, { /* MATX */ "MATX", 4, docmd_matx, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_PRGM }, { /* GOTOROW */ "GOTO\240\322\357\367", 8, NULL, 0x02000000, ARG_COUNT, FLAG_HIDDEN }, { /* GOTOCOLUMN */ "GOTO Column", 11, NULL, 0x02000000, ARG_COUNT, FLAG_HIDDEN }, { /* A_THRU_F */ "A...F", 5, NULL, 0x02000000, ARG_NONE, FLAG_HIDDEN + FLAG_NO_PRGM }, { /* CLALLb */ "CLALL", 5, docmd_clall, 0x02000000, ARG_NONE, FLAG_HIDDEN }, { /* PGMSLVi */ "P\307\315SLV", 6, docmd_pgmslvi, 0x02000000, ARG_PRGM, FLAG_HIDDEN }, { /* PGMINTi */ "P\307\315INT", 6, docmd_pgminti, 0x02000000, ARG_PRGM, FLAG_HIDDEN }, { /* VMSTO2 */ "STO", 3, docmd_vmsto2, 0x02000000, ARG_OTHER, FLAG_HIDDEN }, { /* VMSOLVE */ "SOLVE", 5, docmd_vmsolve, 0x02000000, ARG_OTHER, FLAG_HIDDEN }, { /* MAX */ "[MAX]", 5, docmd_max, 0x0000a6eb, ARG_NONE, FLAG_NONE }, { /* MIN */ "[MIN]", 5, docmd_min, 0x0000a6ea, ARG_NONE, FLAG_NONE }, { /* FIND */ "[FIND]", 6, docmd_find, 0x0000a6ec, ARG_NONE, FLAG_NONE }, { /* XROM */ "XROM", 4, docmd_xrom, 0x01000000, ARG_OTHER, FLAG_HIDDEN }, /* Here endeth the original Free42 function table. * There are two extensions to this table out there in the wild today: * Underhill's COPAN extensions for surveyors * (http://www.underhill.ca/Software/Free42/Free42Copan.php), * and Byron Foster's Big Stack in 42s for iPhone * (http://free42iphone.googlecode.com/). * I'm merging both of these extensions into the main source repository -- * not the actual implementations, but the function table entries, so that * programs created by these extended versions can be shared more easily. * Note that I had to move Byron's DROP extension from position 315 to * 329 to resolve the clash with the older Underhill extensions. * The function handlers (i.e. the docmd_ functions) are #defined as * docmd_xrom if the appropriate extensions are not present, so executing * them will raise a Nonexistent error, but you will see the function * *names* from this table in the actual program listings, regardless of * whether the extensions are present or not. * UPDATE: To support "pure" HP-42S behavior, all extensions can be * disabled at runtime, using the core_settings.enable_ext_* flags. * When an extension is disabled, its commands disappear from the FCN * catalog, are not recognized by XEQ, are displayed as their XROM * equivalents in programs, and raise a Nonexistent error when trying to * execute them from programs. * When a shell disables or enables an extension in response to the user * changing a setting in the Preferences dialog, it should call redisplay() * to make sure the display reflects the new setting. */ /* Underhill's COPAN */ { /* OPENF */ "OPENF", 5, docmd_openf, 0x0000a7c1, ARG_NONE, FLAG_NONE }, { /* CLOSF */ "CLOSF", 5, docmd_closef, 0x0000a7c2, ARG_NONE, FLAG_NONE }, { /* READP */ "READP", 5, docmd_readp, 0x0000a7c3, ARG_NONE, FLAG_NONE }, { /* WRITP */ "WRITP", 5, docmd_writp, 0x0000a7c4, ARG_NONE, FLAG_NONE }, { /* GETXY */ "GETXY", 5, docmd_getxy, 0x0000a7c5, ARG_NONE, FLAG_NONE }, { /* PUTXY */ "PUTXY", 5, docmd_putxy, 0x0000a7c6, ARG_NONE, FLAG_NONE }, { /* CLRP */ "CLRP", 4, docmd_clrp, 0x0000a7c7, ARG_NONE, FLAG_NONE }, { /* CLRD */ "CLRD", 4, docmd_clrd, 0x0000a7c8, ARG_NONE, FLAG_NONE }, { /* APPD */ "APPD", 4, docmd_appd, 0x0000a7c9, ARG_NONE, FLAG_NONE }, { /* GETN */ "GETN", 4, docmd_getn, 0x0000a7ca, ARG_NONE, FLAG_NONE }, { /* PUTN */ "PUTN", 4, docmd_putn, 0x0000a7cb, ARG_NONE, FLAG_NONE }, { /* GETZ */ "GETZ", 4, docmd_getz, 0x0000a7cc, ARG_NONE, FLAG_NONE }, { /* PUTZ */ "PUTZ", 4, docmd_putz, 0x0000a7cd, ARG_NONE, FLAG_NONE }, { /* DELP */ "DELP", 4, docmd_delp, 0x0000a7ce, ARG_NONE, FLAG_NONE }, /* Byron Foster's DROP for Bigstack */ { /* DROP */ "DROP", 4, docmd_drop, 0x0000a271, ARG_NONE, FLAG_NONE }, /* Accelerometer, GPS, and compass support */ { /* ACCEL */ "ACCEL", 5, docmd_accel, 0x0000a7cf, ARG_NONE, FLAG_NONE }, { /* LOCAT */ "LOCAT", 5, docmd_locat, 0x0000a7d0, ARG_NONE, FLAG_NONE }, { /* HEADING */ "H\305\301D\311NG", 7, docmd_heading, 0x0000a7d1, ARG_NONE, FLAG_NONE }, /* Time Module & CX Time support*/ { /* ADATE */ "ADATE", 5, docmd_adate, 0x0000a681, ARG_NONE, FLAG_NONE }, { /* ALMCAT */ "AL\315CAT", 6, docmd_xrom, 0x0000a682, ARG_NONE, FLAG_HIDDEN }, { /* ALMNOW */ "AL\315N\317W", 6, docmd_xrom, 0x0000a683, ARG_NONE, FLAG_HIDDEN }, { /* ATIME */ "ATIME", 5, docmd_atime, 0x0000a684, ARG_NONE, FLAG_NONE }, { /* ATIME24 */ "AT\311\315\30524", 7, docmd_atime24, 0x0000a685, ARG_NONE, FLAG_NONE }, { /* CLK12 */ "CL\31312", 5, docmd_clk12, 0x0000a686, ARG_NONE, FLAG_NONE }, { /* CLK24 */ "CL\31324", 5, docmd_clk24, 0x0000a687, ARG_NONE, FLAG_NONE }, { /* CLKT */ "CLKT", 4, docmd_xrom, 0x0000a688, ARG_NONE, FLAG_HIDDEN }, { /* CLKTD */ "CL\313TD", 5, docmd_xrom, 0x0000a689, ARG_NONE, FLAG_HIDDEN }, { /* CLOCK */ "CL\317\303K", 5, docmd_xrom, 0x0000a68a, ARG_NONE, FLAG_HIDDEN }, { /* CORRECT */ "CORR\305\303\324", 7, docmd_xrom, 0x0000a68b, ARG_NONE, FLAG_HIDDEN }, { /* DATE */ "DATE", 4, docmd_date, 0x0000a68c, ARG_NONE, FLAG_NONE }, { /* DATE_PLUS */ "DATE+", 5, docmd_date_plus, 0x0000a68d, ARG_NONE, FLAG_NONE }, { /* DDAYS */ "DDAYS", 5, docmd_ddays, 0x0000a68e, ARG_NONE, FLAG_NONE }, { /* DMY */ "DMY", 3, docmd_dmy, 0x0000a68f, ARG_NONE, FLAG_NONE }, { /* DOW */ "DOW", 3, docmd_dow, 0x0000a690, ARG_NONE, FLAG_NONE }, { /* MDY */ "MDY", 3, docmd_mdy, 0x0000a691, ARG_NONE, FLAG_NONE }, { /* RCLAF */ "RCLAF", 5, docmd_xrom, 0x0000a692, ARG_NONE, FLAG_HIDDEN }, { /* RCLSW */ "RC\314SW", 5, docmd_xrom, 0x0000a693, ARG_NONE, FLAG_HIDDEN }, { /* RUNSW */ "R\325NSW", 5, docmd_xrom, 0x0000a694, ARG_NONE, FLAG_HIDDEN }, { /* SETAF */ "SETAF", 5, docmd_xrom, 0x0000a695, ARG_NONE, FLAG_HIDDEN }, { /* SETDATE */ "S\305\324DATE", 7, docmd_xrom, 0x0000a696, ARG_NONE, FLAG_HIDDEN }, { /* SETIME */ "S\305TIME", 6, docmd_xrom, 0x0000a697, ARG_NONE, FLAG_HIDDEN }, { /* SETSW */ "SE\324SW", 5, docmd_xrom, 0x0000a698, ARG_NONE, FLAG_HIDDEN }, { /* STOPSW */ "ST\317\320SW", 6, docmd_xrom, 0x0000a699, ARG_NONE, FLAG_HIDDEN }, { /* SW */ "SW", 2, docmd_xrom, 0x0000a69a, ARG_NONE, FLAG_HIDDEN }, { /* T_PLUS_X */ "T+X", 3, docmd_xrom, 0x0000a69b, ARG_NONE, FLAG_HIDDEN }, { /* TIME */ "TIME", 4, docmd_time, 0x0000a69c, ARG_NONE, FLAG_NONE }, { /* XYZALM */ "XYZALM", 6, docmd_xrom, 0x0000a69d, ARG_NONE, FLAG_HIDDEN }, { /* CLALMA */ "CLAL\315A", 6, docmd_xrom, 0x0000a69f, ARG_NONE, FLAG_HIDDEN }, { /* CLALMX */ "CLAL\315X", 6, docmd_xrom, 0x0000a6a0, ARG_NONE, FLAG_HIDDEN }, { /* CLRALMS */ "CLRALMS", 7, docmd_xrom, 0x0000a6a1, ARG_NONE, FLAG_HIDDEN }, { /* RCLALM */ "RCLALM", 6, docmd_xrom, 0x0000a6a2, ARG_NONE, FLAG_HIDDEN }, { /* SWPT */ "SWPT", 4, docmd_xrom, 0x0000a6a3, ARG_NONE, FLAG_HIDDEN } }; /* =============================================================================== HP-42S program storage format Suffixes of nn work as follows: 0-65 are 00-101; 66-7F are A-J, T, Z, Y, X, L, M, N, O, P, Q, \append, a, b, c, d, e; 80-FF are IND versions. Fn starts an n-character alpha string; the 42S uses special initial bytes (in the 80-FF range) to encode those of its extensions to the 41C instruction set that take a parameter; parameterless extensions are encoded using XROM instructions (2-byte instructions with 1st byte of A0-A7). Dunno yet how the offsets work (LBL "", END, GTO nn, XEQ nn) TODO: what about 1F (W ""), AF & B0 (SPARE)? Quick instruction length finder: 00-8F are 1 byte, except 1D-1F, which are followed by a string (Fn plus n bytes of text, for a total of n+2 bytes). 90-BF are 2 bytes (but what about AF & B0 (SPARE)?) C0-CD: if byte 3 is Fn, then it's a global label with a total of n+3 bytes (the string has an extra byte prepended which the 41C uses for key assignment); if byte 3 is not Fn (TODO: which values are allowed & what do they mean?) it is an END, 3 bytes. D0-EF: 3 bytes. Fn: string, n+1 bytes. This includes 42S extensions with parameters (42S extensions without parameters are encoded using XROM instructions (A[0-7] nn), always 2 bytes). CLX 77 ENTER 83 SWAP 71 RDN 75 CHS 54 DIV 43 MUL 42 SUB 41 ADD 40 LASTX 76 SILENT_OFF n/a SILENT_ON n/a SIN 59 COS 5A TAN 5B ASIN 5C ACOS 5D ATAN 5E LOG 56 10_POW_X 57 LN 50 E_POW_X 55 SQRT 52 SQUARE 51 INV 60 Y_POW_X 53 PERCENT 4C PI 72 COMPLEX A0 72 STO 91 nn (STO 00-15: 3n; STO "": Fn 81; STO IND "": Fn 89) STO_DIV 95 nn (STO/ "": Fn 85; STO/ IND "": Fn 8D) STO_MUL 94 nn (STO* "": Fn 84; STO* IND "": Fn 8C) STO_SUB 93 nn (STO- "": Fn 83; STO- IND "": Fn 8B) STO_ADD 92 nn (STO+ "": Fn 82; STO+ IND "": Fn 8A) RCL 90 nn (RCL 00-15: 2n; RCL "": Fn 91; RCL IND "": Fn 99) RCL_DIV F2 D4 nn (RCL/ "": Fn 95; RCL/ IND "": Fn 9D) RCL_MUL F2 D3 nn (RCL* "": Fn 94; RCL* IND "": Fn 9C) RCL_SUB F2 D2 nn (RCL- "": Fn 93; RCL- IND "": Fn 9B) RCL_ADD F2 D1 nn (RCL+ "": Fn 92; RCL+ IND "": Fn 9A) FIX 9C nn (FIX 10: F1 D5; FIX 11: F1 E5) (FIX IND "": Fn DC) SCI 9D nn (SCI 10: F1 D6; SCI 11: F1 E6) (SCI IND "": Fn DD) ENG 9E nn (ENG 10: F1 D7; ENG 11: F1 E7) (ENG IND "": Fn DE) ALL A2 5D NULL 00 ASTO 9A nn (ASTO "": Fn B2; ASTO IND "": Fn BA) ARCL 9B nn (ARCL "": Fn B3; ARCL IND "": Fn BB) CLA 87 DEG 80 RAD 81 GRAD 82 RECT A2 5A POLAR A2 59 SIZE F3 F7 nn nn QUIET A2 69 (ill) CPXRES A2 6A REALRES A2 6B KEYASN A2 63 LCLBL A2 64 RDXDOT A2 5B RDXCOMMA A2 5C CLSIGMA 70 CLP Fn F0 CLV F2 D8 nn (IND only) (CLV "": Fn B0; CLV IND "": Fn B8) CLST 73 CLRG 8A DEL F3 F6 nn nn (ill) CLKEYS A2 62 CLLCD A7 63 CLMENU A2 6D CLALLa n/a TO_DEG 6B TO_RAD 6A TO_HR 6D TO_HMS 6C TO_REC 4E TO_POL 4F IP 68 FP 69 RND 6E ABS 61 SIGN 7A MOD 4B SF A8 nn (SF IND "": Fn A8) CF A9 nn (CF IND "": Fn A9) FS_T AC nn (FS? IND "": Fn AC) FC_T AD nn (FC? IND "": Fn AD) FSC_T AA nn (FS?C IND "": Fn AA) FCC_T AB nn (FC?C IND "": Fn AB) COMB A0 6F PERM A0 70 FACT 62 GAMMA A0 74 RAN A0 71 SEED A0 73 LBL CF nn (LBL 00-14: 01-0F; LBL "": Cm mm Fn) (note that CE and CF are X<> nn and LBL nn, so that limits the possible values of mmm; the label name has an extra byte prepended which the 41C uses for key assignment) RTN 85 INPUT F2 D0 nn (INPUT IND: F2 EE nn; INPUT "": Fn C5; INPUT IND "": Fn CD) VIEW 98 nn (VIEW "": Fn 80; VIEW IND nn: Fn 88) AVIEW 7E XEQ Em mm nn (XEQ IND nn: AE nn (nn bit 7 set); XEQ "": 1E Fn; XEQ IND "": Fn AF) PROMPT 8E PSE 89 ISG 96 nn (ISG "": Fn 96; ISG IND nn: Fn 9E) DSE 97 nn (DSE "": Fn 97; DSE IND nn: Fn 9F) AIP A6 31 XTOA A6 6F AGRAPH A7 64 PIXEL A7 65 BEEP 86 TONE 9F nn (TONE IND "": Fn DF) MVAR Fn 90 (MVAR IND "": Fn 98 (ill (?))) VARMENU F2 F8 nn (IND only) (VARMENU "": Fn C1; VARMENU IND "": Fn C9) GETKEY A2 6E MENU A2 5E KEYG n/a KEYX n/a X_EQ_0 67 X_NE_0 63 X_LT_0 66 X_GT_0 64 X_LE_0 7B X_GE_0 A2 5F X_EQ_Y 78 X_NE_Y 79 X_LT_Y 44 X_GT_Y 45 X_LE_Y 46 X_GE_Y A2 60 PRSIGMA A7 52 PRP A7 4D (ill) PRV F2 D9 nn (IND only) (PRV "": Fn B1; PRV IND "": Fn B9) PRSTK A7 53 PRA A7 48 PRX A7 54 PRUSR A7 61 LIST A7 47 (ill) ADV 8F PRLCD A7 62 DELAY A7 60 PON A7 5E POFF A7 5F MAN A7 5B NORM A7 5C TRACE A7 5D SIGMAADD 47 SIGMASUB 48 GTO Dm mm nn (GTO 00-14: B1-BF; GTO IND nn: AE nn (nn bit 7 clear); GTO "": 1D Fn; GTO IND "": Fn AE) END Cm mm ?? (mmm < E00; ?? is not Fn (cuz that's LBL ""), but what?) NUMBER 0-9: 10-19; .: 1A; E: 1B; -: 1C; conseq num lines sep by NULL (00) STRING Fn, except when n > 0 and the next byte has bit 7 set (or at least is one of the special values that define HP-42S extensions with parameters) (but for the purpose of instruction length finding, it makes no difference: that is always n+1) RUN n/a SST n/a GTODOT F3 F2 nn nn (GTO . "": Fn F4) (ill) GTODOTDOT Fn F3 (TODO: shouldn't that be F1 F3?) (ill) STOP 84 NEWMAT A6 DA RUP 74 REAL_T A2 65 CPX_T A2 67 STR_T A2 68 MAT_T A2 66 DIM_T A6 E7 ASSIGNa n/a ASSIGNb n/a ASGN01 Fn C0 name 00 ASGN02 Fn C0 name 01 ASGN03 Fn C0 name 02 ASGN04 Fn C0 name 03 ASGN05 Fn C0 name 04 ASGN06 Fn C0 name 05 ASGN07 Fn C0 name 06 ASGN08 Fn C0 name 07 ASGN09 Fn C0 name 08 ASGN10 Fn C0 name 09 ASGN11 Fn C0 name 0a ASGN12 Fn C0 name 0b ASGN13 Fn C0 name 0c ASGN14 Fn C0 name 0d ASGN15 Fn C0 name 0e ASGN16 Fn C0 name 0f ASGN17 Fn C0 name 10 ASGN18 Fn C0 name 11 ON A2 70 OFF 8D KEY1G F3 E3 01 nn ("": Fn C3 01; IND "": Fn CB 01) KEY2G F3 E3 02 nn ("": Fn C3 02; IND "": Fn CB 02) KEY3G F3 E3 03 nn ("": Fn C3 03; IND "": Fn CB 03) KEY4G F3 E3 04 nn ("": Fn C3 04; IND "": Fn CB 04) KEY5G F3 E3 05 nn ("": Fn C3 05; IND "": Fn CB 05) KEY6G F3 E3 06 nn ("": Fn C3 06; IND "": Fn CB 06) KEY7G F3 E3 07 nn ("": Fn C3 07; IND "": Fn CB 07) KEY8G F3 E3 08 nn ("": Fn C3 08; IND "": Fn CB 08) KEY9G F3 E3 09 nn ("": Fn C3 09; IND "": Fn CB 09) KEY1X F3 E2 01 nn ("": Fn C2 01; IND "": Fn CA 01) KEY2X F3 E2 02 nn ("": Fn C2 02; IND "": Fn CA 02) KEY3X F3 E2 03 nn ("": Fn C2 03; IND "": Fn CA 03) KEY4X F3 E2 04 nn ("": Fn C2 04; IND "": Fn CA 04) KEY5X F3 E2 05 nn ("": Fn C2 05; IND "": Fn CA 05) KEY6X F3 E2 06 nn ("": Fn C2 06; IND "": Fn CA 06) KEY7X F3 E2 07 nn ("": Fn C2 07; IND "": Fn CA 07) KEY8X F3 E2 08 nn ("": Fn C2 08; IND "": Fn CA 08) KEY9X F3 E2 09 nn ("": Fn C2 09; IND "": Fn CA 09) VMEXEC n/a VMSTO n/a SIGMAREG 99 nn (SigmaREG IND "": Fn DB) SIGMAREG_T A6 78 CLD 7F ACOSH A0 66 ALENG A6 41 ALLSIGMA A0 AE AND A5 88 AOFF 8B AON 8C AROT A6 46 ASHF 88 ASINH A0 64 ATANH A0 65 ATOX A6 47 BASEADD A0 E6 BASESUB A0 E7 BASEMUL A0 E8 BASEDIV A0 E9 BASECHS A0 EA BEST A0 9F BINM A0 E5 BIT_T A5 8C BST n/a CORR A0 A7 COSH A0 62 CROSS A6 CA CUSTOM A2 6F DECM A0 E3 DELR A0 AB DET A6 CC DIM F2 EC nn (IND only) (DIM "": Fn C4; DIM IND "": Fn CC) DOT A6 CB EDIT A6 E1 EDITN F2 EF nn (IND only) (EDITN "": Fn C6; EDITN IND "": Fn CE) EXITALL A2 6C EXPF A0 A0 E_POW_X_1 58 FCSTX A0 A8 FCSTY A0 A9 FNRM A6 CF GETM A6 E8 GROW A6 E3 HEXM A0 E2 HMSADD 49 HMSSUB 4A I_ADD A6 D2 I_SUB A6 D3 INDEX F2 DA nn (IND only) (INDEX "": Fn 87; INDEX IND "": Fn 8F) INSR A0 AA INTEG F2 EA nn (IND only) (INTEG "": Fn B6; INTEG IND "": Fn BE) INVRT A6 CE J_ADD A6 D4 J_SUB A6 D5 LINF A0 A1 LINSIGMA A0 AD LN_1_X 65 LOGF A0 A2 MEAN 7C NOT A5 87 OCTM A0 E4 OLD A6 DB OR A5 89 PGMSLV F2 E9 nn (IND only) (PGMSLV "": Fn B5; PGMSLV IND "": Fn BD) PGMINT F2 E8 nn (IND only) (PGMINT "": Fn B4; PGMINT IND "": Fn BC) POSA A6 5C PUTM A6 E9 PWRF A0 A3 RCLEL A6 D7 RCLIJ A6 D9 RNRM A6 ED ROTXY A5 8B RSUM A6 D0 SWAP_R A6 D1 SDEV 7D SINH A0 61 SLOPE A0 A4 SOLVE F2 EB nn (IND only) (SOLVE "": Fn B7; SOLVE IND "": Fn BF) STOEL A6 D6 STOIJ A6 D8 SUM A0 A5 TANH A0 63 TRANS A6 C9 UVEC A6 CD WMEAN A0 AC WRAP A6 E2 X_SWAP CE nn (X<> "": Fn 86; X<> IND "": Fn 8E) XOR A5 8A YINT A0 A6 TO_DEC 5F TO_OCT 6F LEFT A6 DC UP A6 DE DOWN A6 DF RIGHT A6 DD PERCENT_CH 4D SIMQ n/a MATA A6 E4 (ill) MATB A6 E5 (ill) MATX A6 E6 (ill) GOTOROW A6 E0 (ill) GOTOCOLUMN n/a A_THRU_F n/a CLALLb A2 61 (ill) PGMSLVi n/a PGMINTi n/a VMSTO2 n/a VMSOLVE n/a MAX A6 EB MIN A6 EA FIND A6 EC To be added: W 1F Fn (TODO: what's this?) SPARE1 AF (TODO: what's this?) SPARE2 B0 (TODO: what's this?) XFCN Fn F1 (TODO: what's this?) (apparently, always says "Nonexistent") XROM A[0-7] nn (bits 2-0 of byte 1 plus bits 7-6 of byte 2 are the ROM ID; bits 5-0 of byte 2 are the instruction number. The instruction is displayed as XROM nn,mm with nn and mm in 2 decimal digits. When executed, always says "Nonexistent". Note: when decoding functions, the check for XROM should come *last*, because all the parameterless HP-42S extensions are encoded in XROM space. =============================================================================== */ const command_spec *cmdlist(int index) { return cmd_array + index; } free42-nologo-1.4.77/common/core_tables.h000644 000765 000024 00000033527 12110237247 020566 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_TABLES_H #define CORE_TABLES_H 1 #include "free42.h" #include "core_phloat.h" /************/ /* Commands */ /************/ /* These are indices into the cmdlist array, * declared below (except the negative ones!) */ #define CMD_NONE -1 #define CMD_CANCELLED -2 #define CMD_LINGER1 -3 #define CMD_LINGER2 -4 #define CMD_CLX 0 #define CMD_ENTER 1 #define CMD_SWAP 2 #define CMD_RDN 3 #define CMD_CHS 4 #define CMD_DIV 5 #define CMD_MUL 6 #define CMD_SUB 7 #define CMD_ADD 8 #define CMD_LASTX 9 #define CMD_SILENT_OFF 10 #define CMD_SILENT_ON 11 #define CMD_SIN 12 #define CMD_COS 13 #define CMD_TAN 14 #define CMD_ASIN 15 #define CMD_ACOS 16 #define CMD_ATAN 17 #define CMD_LOG 18 #define CMD_10_POW_X 19 #define CMD_LN 20 #define CMD_E_POW_X 21 #define CMD_SQRT 22 #define CMD_SQUARE 23 #define CMD_INV 24 #define CMD_Y_POW_X 25 #define CMD_PERCENT 26 #define CMD_PI 27 #define CMD_COMPLEX 28 #define CMD_STO 29 #define CMD_STO_DIV 30 #define CMD_STO_MUL 31 #define CMD_STO_SUB 32 #define CMD_STO_ADD 33 #define CMD_RCL 34 #define CMD_RCL_DIV 35 #define CMD_RCL_MUL 36 #define CMD_RCL_SUB 37 #define CMD_RCL_ADD 38 #define CMD_FIX 39 #define CMD_SCI 40 #define CMD_ENG 41 #define CMD_ALL 42 #define CMD_NULL 43 #define CMD_ASTO 44 #define CMD_ARCL 45 #define CMD_CLA 46 #define CMD_DEG 47 #define CMD_RAD 48 #define CMD_GRAD 49 #define CMD_RECT 50 #define CMD_POLAR 51 #define CMD_SIZE 52 #define CMD_QUIET 53 #define CMD_CPXRES 54 #define CMD_REALRES 55 #define CMD_KEYASN 56 #define CMD_LCLBL 57 #define CMD_RDXDOT 58 #define CMD_RDXCOMMA 59 #define CMD_CLSIGMA 60 #define CMD_CLP 61 #define CMD_CLV 62 #define CMD_CLST 63 #define CMD_CLRG 64 #define CMD_DEL 65 #define CMD_CLKEYS 66 #define CMD_CLLCD 67 #define CMD_CLMENU 68 #define CMD_CLALLa 69 #define CMD_TO_DEG 70 #define CMD_TO_RAD 71 #define CMD_TO_HR 72 #define CMD_TO_HMS 73 #define CMD_TO_REC 74 #define CMD_TO_POL 75 #define CMD_IP 76 #define CMD_FP 77 #define CMD_RND 78 #define CMD_ABS 79 #define CMD_SIGN 80 #define CMD_MOD 81 #define CMD_SF 82 #define CMD_CF 83 #define CMD_FS_T 84 #define CMD_FC_T 85 #define CMD_FSC_T 86 #define CMD_FCC_T 87 #define CMD_COMB 88 #define CMD_PERM 89 #define CMD_FACT 90 #define CMD_GAMMA 91 #define CMD_RAN 92 #define CMD_SEED 93 #define CMD_LBL 94 #define CMD_RTN 95 #define CMD_INPUT 96 #define CMD_VIEW 97 #define CMD_AVIEW 98 #define CMD_XEQ 99 #define CMD_PROMPT 100 #define CMD_PSE 101 #define CMD_ISG 102 #define CMD_DSE 103 #define CMD_AIP 104 #define CMD_XTOA 105 #define CMD_AGRAPH 106 #define CMD_PIXEL 107 #define CMD_BEEP 108 #define CMD_TONE 109 #define CMD_MVAR 110 #define CMD_VARMENU 111 #define CMD_GETKEY 112 #define CMD_MENU 113 #define CMD_KEYG 114 #define CMD_KEYX 115 #define CMD_X_EQ_0 116 #define CMD_X_NE_0 117 #define CMD_X_LT_0 118 #define CMD_X_GT_0 119 #define CMD_X_LE_0 120 #define CMD_X_GE_0 121 #define CMD_X_EQ_Y 122 #define CMD_X_NE_Y 123 #define CMD_X_LT_Y 124 #define CMD_X_GT_Y 125 #define CMD_X_LE_Y 126 #define CMD_X_GE_Y 127 #define CMD_PRSIGMA 128 #define CMD_PRP 129 #define CMD_PRV 130 #define CMD_PRSTK 131 #define CMD_PRA 132 #define CMD_PRX 133 #define CMD_PRUSR 134 #define CMD_LIST 135 #define CMD_ADV 136 #define CMD_PRLCD 137 #define CMD_DELAY 138 #define CMD_PON 139 #define CMD_POFF 140 #define CMD_MAN 141 #define CMD_NORM 142 #define CMD_TRACE 143 #define CMD_SIGMAADD 144 #define CMD_SIGMASUB 145 #define CMD_GTO 146 #define CMD_END 147 #define CMD_NUMBER 148 #define CMD_STRING 149 #define CMD_RUN 150 #define CMD_SST 151 #define CMD_GTODOT 152 #define CMD_GTODOTDOT 153 #define CMD_STOP 154 #define CMD_NEWMAT 155 #define CMD_RUP 156 #define CMD_REAL_T 157 #define CMD_CPX_T 158 #define CMD_STR_T 159 #define CMD_MAT_T 160 #define CMD_DIM_T 161 #define CMD_ASSIGNa 162 #define CMD_ASSIGNb 163 #define CMD_ASGN01 164 #define CMD_ASGN02 165 #define CMD_ASGN03 166 #define CMD_ASGN04 167 #define CMD_ASGN05 168 #define CMD_ASGN06 169 #define CMD_ASGN07 170 #define CMD_ASGN08 171 #define CMD_ASGN09 172 #define CMD_ASGN10 173 #define CMD_ASGN11 174 #define CMD_ASGN12 175 #define CMD_ASGN13 176 #define CMD_ASGN14 177 #define CMD_ASGN15 178 #define CMD_ASGN16 179 #define CMD_ASGN17 180 #define CMD_ASGN18 181 #define CMD_ON 182 #define CMD_OFF 183 #define CMD_KEY1G 184 #define CMD_KEY2G 185 #define CMD_KEY3G 186 #define CMD_KEY4G 187 #define CMD_KEY5G 188 #define CMD_KEY6G 189 #define CMD_KEY7G 190 #define CMD_KEY8G 191 #define CMD_KEY9G 192 #define CMD_KEY1X 193 #define CMD_KEY2X 194 #define CMD_KEY3X 195 #define CMD_KEY4X 196 #define CMD_KEY5X 197 #define CMD_KEY6X 198 #define CMD_KEY7X 199 #define CMD_KEY8X 200 #define CMD_KEY9X 201 #define CMD_VMEXEC 202 #define CMD_VMSTO 203 #define CMD_SIGMAREG 204 #define CMD_SIGMAREG_T 205 #define CMD_CLD 206 #define CMD_ACOSH 207 #define CMD_ALENG 208 #define CMD_ALLSIGMA 209 #define CMD_AND 210 #define CMD_AOFF 211 #define CMD_AON 212 #define CMD_AROT 213 #define CMD_ASHF 214 #define CMD_ASINH 215 #define CMD_ATANH 216 #define CMD_ATOX 217 #define CMD_BASEADD 218 #define CMD_BASESUB 219 #define CMD_BASEMUL 220 #define CMD_BASEDIV 221 #define CMD_BASECHS 222 #define CMD_BEST 223 #define CMD_BINM 224 #define CMD_BIT_T 225 #define CMD_BST 226 #define CMD_CORR 227 #define CMD_COSH 228 #define CMD_CROSS 229 #define CMD_CUSTOM 230 #define CMD_DECM 231 #define CMD_DELR 232 #define CMD_DET 233 #define CMD_DIM 234 #define CMD_DOT 235 #define CMD_EDIT 236 #define CMD_EDITN 237 #define CMD_EXITALL 238 #define CMD_EXPF 239 #define CMD_E_POW_X_1 240 #define CMD_FCSTX 241 #define CMD_FCSTY 242 #define CMD_FNRM 243 #define CMD_GETM 244 #define CMD_GROW 245 #define CMD_HEXM 246 #define CMD_HMSADD 247 #define CMD_HMSSUB 248 #define CMD_I_ADD 249 #define CMD_I_SUB 250 #define CMD_INDEX 251 #define CMD_INSR 252 #define CMD_INTEG 253 #define CMD_INVRT 254 #define CMD_J_ADD 255 #define CMD_J_SUB 256 #define CMD_LINF 257 #define CMD_LINSIGMA 258 #define CMD_LN_1_X 259 #define CMD_LOGF 260 #define CMD_MEAN 261 #define CMD_NOT 262 #define CMD_OCTM 263 #define CMD_OLD 264 #define CMD_OR 265 #define CMD_PGMSLV 266 #define CMD_PGMINT 267 #define CMD_POSA 268 #define CMD_PUTM 269 #define CMD_PWRF 270 #define CMD_RCLEL 271 #define CMD_RCLIJ 272 #define CMD_RNRM 273 #define CMD_ROTXY 274 #define CMD_RSUM 275 #define CMD_SWAP_R 276 #define CMD_SDEV 277 #define CMD_SINH 278 #define CMD_SLOPE 279 #define CMD_SOLVE 280 #define CMD_STOEL 281 #define CMD_STOIJ 282 #define CMD_SUM 283 #define CMD_TANH 284 #define CMD_TRANS 285 #define CMD_UVEC 286 #define CMD_WMEAN 287 #define CMD_WRAP 288 #define CMD_X_SWAP 289 #define CMD_XOR 290 #define CMD_YINT 291 #define CMD_TO_DEC 292 #define CMD_TO_OCT 293 #define CMD_LEFT 294 #define CMD_UP 295 #define CMD_DOWN 296 #define CMD_RIGHT 297 #define CMD_PERCENT_CH 298 #define CMD_SIMQ 299 #define CMD_MATA 300 #define CMD_MATB 301 #define CMD_MATX 302 #define CMD_GOTOROW 303 #define CMD_GOTOCOLUMN 304 #define CMD_A_THRU_F 305 #define CMD_CLALLb 306 #define CMD_PGMSLVi 307 #define CMD_PGMINTi 308 #define CMD_VMSTO2 309 #define CMD_VMSOLVE 310 #define CMD_MAX 311 #define CMD_MIN 312 #define CMD_FIND 313 #define CMD_XROM 314 /* Underhill's COPAN extensions */ #define CMD_OPENF 315 #define CMD_CLOSEF 316 #define CMD_READP 317 #define CMD_WRITEP 318 #define CMD_GETXY 319 #define CMD_PUTXY 320 #define CMD_CLRP 321 #define CMD_CLRD 322 #define CMD_APPD 323 #define CMD_GETN 324 #define CMD_PUTN 325 #define CMD_GETZ 326 #define CMD_PUTZ 327 #define CMD_DELP 328 /* Byron Foster's Bigstack extension */ #define CMD_DROP 329 /* iPhone hardware support */ #define CMD_ACCEL 330 #define CMD_LOCAT 331 #define CMD_HEADING 332 /* HP-41 Time Module & CX Time */ #define CMD_ADATE 333 #define CMD_ALMCAT 334 #define CMD_ALMNOW 335 #define CMD_ATIME 336 #define CMD_ATIME24 337 #define CMD_CLK12 338 #define CMD_CLK24 339 #define CMD_CLKT 340 #define CMD_CLKTD 341 #define CMD_CLOCK 342 #define CMD_CORRECT 343 #define CMD_DATE 344 #define CMD_DATE_PLUS 345 #define CMD_DDAYS 346 #define CMD_DMY 347 #define CMD_DOW 348 #define CMD_MDY 349 #define CMD_RCLAF 350 #define CMD_RCLSW 351 #define CMD_RUNSW 352 #define CMD_SETAF 353 #define CMD_SETDATE 354 #define CMD_SETIME 355 #define CMD_SETSW 356 #define CMD_STOPSW 357 #define CMD_SW 358 #define CMD_T_PLUS_X 359 #define CMD_TIME 360 #define CMD_XYZALM 361 #define CMD_CLALMA 362 #define CMD_CLALMX 363 #define CMD_CLRALMS 364 #define CMD_RCLALM 365 #define CMD_SWPT 366 #define CMD_SENTINEL 367 /* command_spec.argtype */ #define ARG_NONE 0 /* No argument */ #define ARG_VAR 1 /* Variable: num, stk, var, ind */ #define ARG_REAL 2 /* Variable: num, stk, var, ind; real scalars only */ #define ARG_NAMED 3 /* Variable: var, ind */ #define ARG_NUM9 4 /* num (0..9), ind */ #define ARG_NUM11 5 /* num (0..11), ind */ #define ARG_NUM99 6 /* num (0..99), ind */ #define ARG_COUNT 7 /* numeric-only (SIMQ, DEL, SIZE, LIST) */ #define ARG_LBL 8 /* Label: num, lclbl, global, ind */ #define ARG_CKEY 9 /* Key in custom menu */ #define ARG_MKEY 10 /* Key in programmable menu (KEYG/KEYX) */ #define ARG_PRGM 11 /* Alpha label (CATSECT_PGM) */ #define ARG_RVAR 12 /* Variable (real only) (MVAR, INTEG, SOLVE) */ #define ARG_MAT 13 /* Variable (matrix only) (EDITN, INDEX) */ #define ARG_OTHER 14 /* Weirdos */ /* command_spec.flags */ #define FLAG_NONE 0 /* Boring! */ #define FLAG_PRGM_ONLY 1 /* Only allowed in program mode (LBL, DEL, ...) */ #define FLAG_IMMED 2 /* Executes in program mode (DEL, GTO.nnn, ...) */ #define FLAG_HIDDEN 4 /* Cannot be activated using XEQ "NAME" (SIMQ, ...) */ #define FLAG_NO_PRGM 8 /* Cannot be programmed (SIMQ, MATA, ...) */ #define FLAG_NO_SHOW 16 /* Do not show after keytimeout1 */ /* Builtin cmd arg types */ /* These represent data types used when a command * is executing. Compare the ARG_ macros; those * represent the data types that a command is *capable* * of accepting, and are used during command entry. */ #define ARGTYPE_NONE 0 #define ARGTYPE_NUM 1 #define ARGTYPE_NEG_NUM 2 /* Used internally */ #define ARGTYPE_STK 3 #define ARGTYPE_STR 4 #define ARGTYPE_IND_NUM 5 #define ARGTYPE_IND_STK 6 #define ARGTYPE_IND_STR 7 #define ARGTYPE_COMMAND 8 /* For backward compatibility only! */ #define ARGTYPE_LCLBL 9 #define ARGTYPE_DOUBLE 10 #define ARGTYPE_LBLINDEX 11 typedef struct { unsigned char type; unsigned char length; int4 target; union { int4 num; char text[15]; char stk; int cmd; /* For backward compatibility only! */ char lclbl; } val; // This used to be a member of the 'val' union, but once I changed it // from 'double' to 'phloat', that was no longer possible. phloat val_d; } arg_struct; typedef struct { char name[12]; int name_length; int (*handler)(arg_struct *arg); uint4 hp42s_code; int argtype; int flags; } command_spec; const command_spec *cmdlist(int index); #endif free42-nologo-1.4.77/common/core_variables.cc000644 000765 000024 00000033064 12110237247 021416 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include "core_globals.h" #include "core_helpers.h" #include "core_display.h" #include "core_variables.h" // We cache vartype_real, vartype_complex, and vartype_string instances, to // cut down on the malloc/free overhead. This overhead is particularly painful // in the PalmOS ARM version, because it has to do an ARM-to-68K call for each // malloc or free, and that's a performance killer when running programs. // TODO: Pools may cause memory fragmentation. To fix, override malloc() and // realloc() with versions that empty the pools and retry before returning // NULL. typedef struct pool_real { vartype_real r; struct pool_real *next; } pool_real; static pool_real *realpool = NULL; typedef struct pool_complex { vartype_complex c; struct pool_complex *next; } pool_complex; static pool_complex *complexpool = NULL; typedef struct pool_string { vartype_string s; struct pool_string *next; } pool_string; static pool_string *stringpool = NULL; vartype *new_real(phloat value) { pool_real *r; if (realpool == NULL) { r = (pool_real *) malloc(sizeof(pool_real)); if (r == NULL) return NULL; r->r.type = TYPE_REAL; } else { r = realpool; realpool = realpool->next; } r->r.x = value; return (vartype *) r; } vartype *new_complex(phloat re, phloat im) { pool_complex *c; if (complexpool == NULL) { c = (pool_complex *) malloc(sizeof(pool_complex)); if (c == NULL) return NULL; c->c.type = TYPE_COMPLEX; } else { c = complexpool; complexpool = complexpool->next; } c->c.re = re; c->c.im = im; return (vartype *) c; } vartype *new_string(const char *text, int length) { pool_string *s; if (stringpool == NULL) { s = (pool_string *) malloc(sizeof(pool_string)); if (s == NULL) return NULL; s->s.type = TYPE_STRING; } else { s = stringpool; stringpool = stringpool->next; } int i; s->s.type = TYPE_STRING; s->s.length = length > 6 ? 6 : length; for (i = 0; i < s->s.length; i++) s->s.text[i] = text[i]; return (vartype *) s; } vartype *new_realmatrix(int4 rows, int4 columns) { vartype_realmatrix *rm = (vartype_realmatrix *) malloc(sizeof(vartype_realmatrix)); if (rm == NULL) return NULL; int4 i, sz; rm->type = TYPE_REALMATRIX; rm->rows = rows; rm->columns = columns; sz = rows * columns; rm->array = (realmatrix_data *) malloc(sizeof(realmatrix_data)); if (rm->array == NULL) { free(rm); return NULL; } rm->array->data = (phloat *) malloc(sz * sizeof(phloat)); if (rm->array->data == NULL) { /* Oops */ free(rm->array); free(rm); return NULL; } rm->array->is_string = (char *) malloc(sz); if (rm->array->is_string == NULL) { /* Oops */ free(rm->array->data); free(rm->array); free(rm); return NULL; } for (i = 0; i < sz; i++) rm->array->data[i] = 0; for (i = 0; i < sz; i++) rm->array->is_string[i] = 0; rm->array->refcount = 1; return (vartype *) rm; } vartype *new_complexmatrix(int4 rows, int4 columns) { vartype_complexmatrix *cm = (vartype_complexmatrix *) malloc(sizeof(vartype_complexmatrix)); if (cm == NULL) return NULL; int4 i, sz; cm->type = TYPE_COMPLEXMATRIX; cm->rows = rows; cm->columns = columns; sz = rows * columns * 2; cm->array = (complexmatrix_data *) malloc(sizeof(complexmatrix_data)); if (cm->array == NULL) { free(cm); return NULL; } cm->array->data = (phloat *) malloc(sz * sizeof(phloat)); if (cm->array->data == NULL) { /* Oops */ free(cm->array); free(cm); return NULL; } for (i = 0; i < sz; i++) cm->array->data[i] = 0; cm->array->refcount = 1; return (vartype *) cm; } vartype *new_matrix_alias(vartype *m) { if (m->type == TYPE_REALMATRIX) { vartype_realmatrix *rm1 = (vartype_realmatrix *) m; vartype_realmatrix *rm2 = (vartype_realmatrix *) malloc(sizeof(vartype_realmatrix)); if (rm2 == NULL) return NULL; *rm2 = *rm1; rm2->array->refcount++; return (vartype *) rm2; } else if (m->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *cm1 = (vartype_complexmatrix *) m; vartype_complexmatrix *cm2 = (vartype_complexmatrix *) malloc(sizeof(vartype_complexmatrix)); if (cm2 == NULL) return NULL; *cm2 = *cm1; cm2->array->refcount++; return (vartype *) cm2; } else return NULL; } void free_vartype(vartype *v) { if (v == NULL) return; switch (v->type) { case TYPE_REAL: { pool_real *r = (pool_real *) v; r->next = realpool; realpool = r; break; } case TYPE_COMPLEX: { pool_complex *c = (pool_complex *) v; c->next = complexpool; complexpool = c; break; } case TYPE_STRING: { pool_string *s = (pool_string *) v; s->next = stringpool; stringpool = s; break; } case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) v; if (--(rm->array->refcount) == 0) { free(rm->array->data); free(rm->array->is_string); free(rm->array); } free(rm); break; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) v; if (--(cm->array->refcount) == 0) { free(cm->array->data); free(cm->array); } free(cm); break; } } } void clean_vartype_pools() { while (realpool != NULL) { pool_real *r = realpool; realpool = r->next; free(r); } while (complexpool != NULL) { pool_complex *c = complexpool; complexpool = c->next; free(c); } while (stringpool != NULL) { pool_string *s = stringpool; stringpool = s->next; free(s); } } vartype *dup_vartype(const vartype *v) { if (v == NULL) return NULL; switch (v->type) { case TYPE_REAL: { vartype_real *r = (vartype_real *) v; return new_real(r->x); } case TYPE_COMPLEX: { vartype_complex *c = (vartype_complex *) v; return new_complex(c->re, c->im); } case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) v; vartype_realmatrix *rm2 = (vartype_realmatrix *) malloc(sizeof(vartype_realmatrix)); if (rm2 == NULL) return NULL; rm2->type = TYPE_REALMATRIX; rm2->rows = rm->rows; rm2->columns = rm->columns; rm2->array = rm->array; rm->array->refcount++; return (vartype *) rm2; } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) v; vartype_complexmatrix *cm2 = (vartype_complexmatrix *) malloc(sizeof(vartype_complexmatrix)); if (cm2 == NULL) return NULL; cm2->type = TYPE_COMPLEXMATRIX; cm2->rows = cm->rows; cm2->columns = cm->columns; cm2->array = cm->array; cm->array->refcount++; return (vartype *) cm2; } case TYPE_STRING: { vartype_string *s = (vartype_string *) v; return new_string(s->text, s->length); } default: return NULL; } } int disentangle(vartype *v) { switch (v->type) { case TYPE_REALMATRIX: { vartype_realmatrix *rm = (vartype_realmatrix *) v; if (rm->array->refcount == 1) return 1; else { realmatrix_data *md = (realmatrix_data *) malloc(sizeof(realmatrix_data)); if (md == NULL) return 0; int4 sz = rm->rows * rm->columns; int4 i; md->data = (phloat *) malloc(sz * sizeof(phloat)); if (md->data == NULL) { free(md); return 0; } md->is_string = (char *) malloc(sz); if (md->is_string == NULL) { free(md->data); free(md); return 0; } for (i = 0; i < sz; i++) md->data[i] = rm->array->data[i]; for (i = 0; i < sz; i++) md->is_string[i] = rm->array->is_string[i]; md->refcount = 1; rm->array->refcount--; rm->array = md; return 1; } } case TYPE_COMPLEXMATRIX: { vartype_complexmatrix *cm = (vartype_complexmatrix *) v; if (cm->array->refcount == 1) return 1; else { complexmatrix_data *md = (complexmatrix_data *) malloc(sizeof(complexmatrix_data)); if (md == NULL) return 0; int4 sz = cm->rows * cm->columns * 2; int4 i; md->data = (phloat *) malloc(sz * sizeof(phloat)); if (md->data == NULL) { free(md); return 0; } for (i = 0; i < sz; i++) md->data[i] = cm->array->data[i]; md->refcount = 1; cm->array->refcount--; cm->array = md; return 1; } } case TYPE_REAL: case TYPE_COMPLEX: case TYPE_STRING: default: return 1; } } int lookup_var(const char *name, int namelength) { /* TODO: memoize lookups. * Also make sure that purge_var updates the lookup cache! */ int i, j; for (i = 0; i < vars_count; i++) { if (vars[i].length == namelength) { for (j = 0; j < namelength; j++) if (vars[i].name[j] != name[j]) goto nomatch; return i; } nomatch:; } return -1; } vartype *recall_var(const char *name, int namelength) { int varindex = lookup_var(name, namelength); if (varindex == -1) return NULL; else return vars[varindex].value; } void store_var(const char *name, int namelength, vartype *value) { int varindex = lookup_var(name, namelength); int i; if (varindex == -1) { if (vars_count == vars_capacity) { vars_capacity += 25; vars = (var_struct *) realloc(vars, vars_capacity * sizeof(var_struct)); // TODO - handle memory allocation failure } varindex = vars_count++; vars[varindex].length = namelength; for (i = 0; i < namelength; i++) vars[varindex].name[i] = name[i]; } else { if (matedit_mode != 0 && string_equals(name, namelength, matedit_name, matedit_length)) { /* We're replacing the indexed matrix; need to reset I and J */ /* TODO: proper handling of modes 2 and 3 (edit & editn); this code * really only takes care of the mode 1 case, because it does not * deal with the contents of X, nor with leaving the editor if the * replacement value is not a matrix (or is that forbidden? Maybe * that should cause a "Restricted Operation" error... And that * means I'll have to fix store_var() so that it returns an error * code -- which it should anyway; right now it does not check for * memory allocation failure when growing the variables array, and * that is evil). */ if (value->type == TYPE_REALMATRIX || value->type == TYPE_COMPLEXMATRIX) matedit_i = matedit_j = 0; else matedit_mode = 0; } free_vartype(vars[varindex].value); } vars[varindex].value = value; update_catalog(); } int purge_var(const char *name, int namelength) { int varindex = lookup_var(name, namelength); int i; if (varindex == -1) return 0; free_vartype(vars[varindex].value); for (i = varindex; i < vars_count - 1; i++) vars[i] = vars[i + 1]; vars_count--; update_catalog(); return 1; } void purge_all_vars() { int i; for (i = 0; i < vars_count; i++) free_vartype(vars[i].value); vars_count = 0; } int vars_exist(int real, int cpx, int matrix) { int i; for (i = 0; i < vars_count; i++) { switch (vars[i].value->type) { case TYPE_REAL: case TYPE_STRING: if (real) return 1; else break; case TYPE_COMPLEX: if (cpx) return 1; else break; case TYPE_REALMATRIX: case TYPE_COMPLEXMATRIX: if (matrix) return 1; else break; } } return 0; } int contains_no_strings(const vartype_realmatrix *rm) { int4 size = rm->rows * rm->columns; int4 i; for (i = 0; i < size; i++) if (rm->array->is_string[i]) return 0; return 1; } int matrix_copy(vartype *dst, const vartype *src) { int4 size, i; if (src->type == TYPE_REALMATRIX) { vartype_realmatrix *s = (vartype_realmatrix *) src; if (dst->type == TYPE_REALMATRIX) { vartype_realmatrix *d = (vartype_realmatrix *) dst; if (s->rows != d->rows || s->columns != d->columns) return ERR_DIMENSION_ERROR; size = s->rows * s->columns; for (i = 0; i < size; i++) { d->array->is_string[i] = s->array->is_string[i]; d->array->data[i] = s->array->data[i]; } return ERR_NONE; } else if (dst->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *d = (vartype_complexmatrix *) dst; if (s->rows != d->rows || s->columns != d->columns) return ERR_DIMENSION_ERROR; if (!contains_no_strings(s)) return ERR_ALPHA_DATA_IS_INVALID; size = s->rows * s->columns; for (i = 0; i < size; i++) { d->array->data[2 * i] = s->array->data[i]; d->array->data[2 * i + 1] = 0; } return ERR_NONE; } else return ERR_INVALID_TYPE; } else if (src->type == TYPE_COMPLEXMATRIX && dst->type == TYPE_COMPLEXMATRIX) { vartype_complexmatrix *s = (vartype_complexmatrix *) src; vartype_complexmatrix *d = (vartype_complexmatrix *) dst; if (s->rows != d->rows || s->columns != d->columns) return ERR_DIMENSION_ERROR; size = s->rows * s->columns * 2; for (i = 0; i < size; i++) d->array->data[i] = s->array->data[i]; return ERR_NONE; } else return ERR_INVALID_TYPE; } free42-nologo-1.4.77/common/core_variables.h000644 000765 000024 00000003250 12110237247 021252 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef CORE_VARIABLES_H #define CORE_VARIABLES_H 1 #include "core_phloat.h" vartype *new_real(phloat value); vartype *new_complex(phloat re, phloat im); vartype *new_string(const char *s, int slen); vartype *new_realmatrix(int4 rows, int4 columns); vartype *new_complexmatrix(int4 rows, int4 columns); vartype *new_matrix_alias(vartype *m); void free_vartype(vartype *v); void clean_vartype_pools(); vartype *dup_vartype(const vartype *v); int disentangle(vartype *v); int lookup_var(const char *name, int namelength); vartype *recall_var(const char *name, int namelength); void store_var(const char *name, int namelength, vartype *value); int purge_var(const char *name, int namelength); void purge_all_vars(); int vars_exist(int real, int cpx, int matrix); int contains_no_strings(const vartype_realmatrix *rm); int matrix_copy(vartype *dst, const vartype *src); #endif free42-nologo-1.4.77/common/free42.h000644 000765 000024 00000012432 12110237247 017363 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef FREE42_H #define FREE42_H 1 #ifndef BCD_MATH #include #endif #define int2 short #define uint2 unsigned short #define int4 int #define uint4 unsigned int #if defined(WINDOWS) && !defined(__GNUC__) #define int8 __int64 #define uint8 unsigned __int64 #define LL(x) x##LL /* MSVC++ 6.0 lacks a few math functions that Free42 needs. * I've defined workarounds in mathfudge.c. NOTE: my versions * of isnan(), finite(), and isinf() are a bit lame -- I *think* * they handle infinities properly, but definitely not NaNs * (although NaNs shouldn't be much of a problem because the Free42 * code mostly tries to avoid them, rather than detect them after * the fact). */ #ifdef __cplusplus extern "C" { #endif int isnan(double x); int finite(double x); int isinf(double x); void sincos(double x, double *sinx, double *cosx); double asinh(double x); double acosh(double x); double atanh(double x); double expm1(double x); double log1p(double x); #ifdef _WIN32_WCE double hypot(double x, double y); #endif #ifdef __cplusplus } #endif #else #define int8 long long #define uint8 unsigned long long #define LL(x) x##LL /* NOTE: In my Linux build, all I have to do is DECLARE sincos(); glibc 2.3.3 * has it (for C99, I suppose) so I don't have to DEFINE it. On other Unixes * (e.g. MacOS X), it may not be provided by the standard libraries; in this * case, define the NO_SINCOS symbol, here or in the Makefile. * For the Palm build, we don't even need the declaration, since sincos() is * provided by MathLib. */ extern "C" void sincos(double x, double *sinx, double *cosx); //#define NO_SINCOS 1 #endif // the iPhone SDK does not define 'finite' so we create a macro wrapper #if !defined(BCD_MATH) && defined(IPHONE) #define finite(x) isfinite(x) #endif #define uint unsigned int /* Magic number and version number for the state file. * State file versions correspond to application releases as follows: * * Version 0: 1.0 first release * Version 1: 1.0.13 "IP Hack" option * Version 2: 1.0.13 "singular matrix" and matrix "out of range" options * Version 3: 1.0.16 "deferred_print" flag for NORM/TRACE printing * Version 4: 1.1 BCD conversion table no longer stored in state file * Version 5: 1.1 "raw text" option * Version 6: 1.1.8 GETKEY across power-cycle * Version 7: 1.1.12 FCN catalog assignments now HP-42S-compatible * Version 8: 1.1.14 F42 file format and "HP-42S byte counts" option removed * Version 9: 1.4 decimal version; removed IP Hack * Version 10: 1.4.16 persistent shared matrices * Version 11: 1.4.44 "Auto-Repeat" option * Version 12: 1.4.52 BIGSTACK (iphone only); * new BCDFloat format (Inf and NaN flags) * * ========== NOTE: BCD20 Upgrade in Free42 1.4.52 ========== * In version 1.4.52, I upgraded to a new version of BCD20, without realizing * that it uses a slightly different storage format (NaN and Inifinity are now * encoded using two flags in the exponent field, rather than using magical * exponent values; the exponent field was narrowed by 2 bits to accomodate * these flags). * I should have added new code to convert BCDFloat numbers from the old * format to the new at that time. Once I discovered this oversight, 1.4.52-54 * were already released. * In 1.4.55, I introduced code to convert old-style BCDFloat to new-style if * the state file version is less than 12, i.e. created by Free42 1.4.51 or * earlier. This means that 1.4.55 will interpret BCDFloat from all previous * versions correctly; however, any state file that has gone through the * transition from <= 1.4.51 Decimal to 1.4.52-54 Decimal may still be * corrupted, and the only way to be safe is to do CLALL and reload all * programs and data in that case. * * Version 13: 1.4.55 Dynamically sized BIGSTACK (iphone only) * Version 14: 1.4.63 Moved BIGSTACK DROP command from index 315 to 329, to fix * the clash with Underhill's COPAN extensions. The iPhone * version, when reading a state file with version 12 or 13, * scans all programs and renumbers DROP where necessary. * All other versions can ignore this version number change. * Version 15: 1.4.63 "Enable Extension" options for COPAN, BIGSTACK, ACCEL, * LOCAT, HEADING, and HP-41 Time * Version 16: 1.4.63 time and date format flags * Version 17: 1.4.65 iPhone "OFF enable" flag */ #define FREE42_MAGIC 0x466b3432 #define FREE42_VERSION 17 #endif free42-nologo-1.4.77/common/keymap2cc.cc000644 000765 000024 00000006607 12110237247 020317 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include #include #ifdef WINDOWS #define KEYMAP_CC "keymap.cpp" #define KEYMAP2CC "keymap2cpp" #else #define KEYMAP_CC "keymap.cc" #define KEYMAP2CC "keymap2cc" #endif FILE *out; void write_bytes(FILE *file) { int pos; int first = 1; int c; while ((c = fgetc(file)) != EOF) { int width = c < 10 ? 1 : c < 100 ? 2 : 3; if (first) { first = 0; fprintf(out, " "); pos = 4; } else if (pos + width > 74) { fprintf(out, ",\n "); pos = 4; } else { fprintf(out, ", "); pos += 2; } fprintf(out, "%d", c); pos += width; } } int main(int argc, char *argv[]) { FILE *inp; out = fopen(KEYMAP_CC, "w"); if (out == NULL) { fprintf(stderr, "%s: can't open output file \"%s\"\n", KEYMAP2CC, KEYMAP_CC); return 1; } fprintf(out, "/* %s\n" " * Contains the default key map for Free42.\n" " * This file is generated by the %s program from the keymap.txt file.\n" " * NOTE: this is a generated file; do not edit!\n" " */\n\n", KEYMAP_CC, KEYMAP2CC); fprintf(out, "#if defined(WINDOWS) && !defined(__GNUC__)\n"); fprintf(out, "/* Disable warning 'initializing' : truncation from 'const int ' to 'const char ' */\n"); fprintf(out, "#pragma warning(disable: 4305)\n"); fprintf(out, "/* Disable warning 'initializing' : truncation of constant value */\n"); fprintf(out, "#pragma warning(disable: 4309)\n"); fprintf(out, "#endif\n\n"); fprintf(out, "/***********************/\n"); fprintf(out, "/* Size of keymap file */\n"); fprintf(out, "/***********************/\n\n"); inp = fopen("keymap.txt", "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"keymap.txt\": %s (%d)\n", strerror(err), err); fclose(out); remove(KEYMAP_CC); return 1; } fseek(inp, 0, SEEK_END); fprintf(out, "long keymap_filesize = %d;\n\n\n", ftell(inp)); fclose(inp); fprintf(out, "/********************/\n"); fprintf(out, "/* Keymap file data */\n"); fprintf(out, "/********************/\n\n"); inp = fopen("keymap.txt", "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"keymap.txt\": %s (%d)\n", strerror(err), err); fclose(out); remove(KEYMAP_CC); return 1; } // TODO: If I put 'const' here, the symbol is not exported. Why? fprintf(out, "/*const*/ char keymap_filedata[] = {\n"); write_bytes(inp); fprintf(out, "\n};\n\n\n"); fclose(inp); fprintf(out, "/***********/\n"); fprintf(out, "/* The End */\n"); fprintf(out, "/***********/\n"); fclose(out); return 0; } free42-nologo-1.4.77/common/shell.h000644 000765 000024 00000026067 12110237247 017414 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef SHELL_H #define SHELL_H 1 #include "free42.h" /* shell_blitter() * * Callback invoked by the emulator core to cause the display, or some portion * of it, to be repainted. * * 'bits' is a pointer to a 1 bpp (monochrome) bitmap. The bits within a byte * are laid out with left corresponding to least significant, right * corresponding to most significant; this corresponds to the convention for * X11 images, but it is the reverse of the convention for MacOS and its * derivatives (Microsoft Windows and PalmOS). * The bytes are laid out sequentially, that is, bits[0] is at the top * left corner, bits[1] is to the right of bits[0], bits[2] is to the right of * bits[1], and so on; this corresponds to X11, MacOS, Windows, and PalmOS * usage. * 'bytesperline' is the number of bytes per line of the bitmap; this means * that the bits just below bits[0] are at bits[bytesperline]. * 'x', 'y', 'width', and 'height' define the part of the bitmap that needs to * be repainted. 'x' and 'y' are 0-based coordinates, with (0, 0) being the top * left corner of the bitmap, and x coordinates increasing to the right, and y * coordinates increasing downwards. 'width' and 'height' are the width and * height of the area to be repainted. */ void shell_blitter(const char *bits, int bytesperline, int x, int y, int width, int height); /* shell_beeper() * Callback invoked by the emulator core to play a sound. * The first parameter is the frequency in Hz; the second is the * duration in ms. The sound volume is up to the GUI to control. * Sound playback should be synchronous (the beeper function should * not return until the sound has finished), if possible. */ void shell_beeper(int frequency, int duration); /* shell_annunciators() * Callback invoked by the emulator core to change the state of the display * annunciators (up/down, shift, print, run, battery, (g)rad). * Every parameter can have values 0 (turn off), 1 (turn on), or -1 (leave * unchanged). * The battery annunciator is missing from the list; this is the only one of * the lot that the emulator core does not actually have any control over, and * so the shell is expected to handle that one by itself. */ void shell_annunciators(int updn, int shf, int prt, int run, int g, int rad); /* shell_wants_cpu() * * Callback used by the emulator core to check for pending events. * It calls this periodically during long operations, such as running a * user program, or the solver, etc. The shell should not handle any events * in this call! If there are pending events, it should return 1; the currently * active invocation of core_keydown() or core_keyup() will then return * immediately (with a return value of 1, to indicate that it would like to get * the CPU back as soon as possible). */ int shell_wants_cpu(); /* Callback to suspend execution for the given number of milliseconds. No event * processing will take place during the wait, so the core can call this * without having to worry about core_keydown() etc. being re-entered. */ void shell_delay(int duration); /* Callback to ask the shell to call core_timeout3() after the given number of * milliseconds. If there are keystroke events during that time, the timeout is * cancelled. (Pressing 'shift' does not cancel the timeout.) * This function supports the delay after SHOW, MEM, and shift-VARMENU. */ void shell_request_timeout3(int delay); /* shell_read_saved_state() * * Callback to read from the saved state. The function will read up to n * bytes into the buffer pointed to by buf, and return the number of bytes * actually read. The function returns -1 if an error was encountered; a return * value of 0 signifies the end of input. * The emulator core should only call this function from core_init(), and only * if core_init() was called with an argument of 1. (Nothing horrible will * happen if you try to call this function during other contexts, but you will * always get an error then.) */ int4 shell_read_saved_state(void *buf, int4 bufsize); /* shell_write_saved_state() * Callback to dump the saved state to persistent storage. * Returns 1 on success, 0 on error. * The emulator core should only call this function from core_quit(). (Nothing * horrible will happen if you try to call this function during other contexts, * but you will always get an error then.) */ bool shell_write_saved_state(const void *buf, int4 nbytes); /* shell_get_mem() * Callback to get the amount of free memory in bytes. */ uint4 shell_get_mem(); /* shell_low_battery() * Callback to find out if the battery is low. Used to emulate flag 49 and the * battery annunciator, and also taken into account when deciding whether or * not to allow a power-down -- so as long as the shell provides a functional * implementation of shell_low_battery(), it can leave the decision on how to * respond to sysNotifySleepRequestEvent to core_allows_powerdown(). */ int shell_low_battery(); /* shell_powerdown() * Callback to tell the shell that the emulator wants to power down. * Only called in response to OFF (shift-EXIT or the OFF command); automatic * power-off is left to the OS and/or shell. */ void shell_powerdown(); /* shell_random_seed() * When SEED is invoked with X = 0, the random number generator should be * seeded to a random value; the emulator core calls this function to obtain * it. The shell should construct a double in the range [0, 1) in a random * manner, using the real-time clock or some other source of randomness. * Note that distribution is not very important; the value will only be used to * seed the RNG. What's important that using shell_random_seed() guarantees * that the RNG will be initialized to a different sequence. This matters for * applications like games where you don't want the same sequence of cards * dealt each time. */ double shell_random_seed(); /* shell_milliseconds() * Returns an elapsed-time value in milliseconds. The caller should make no * assumptions as to what this value is relative to; it is only intended to * allow the emulator core make short-term elapsed-time measurements. */ uint4 shell_milliseconds(); /* shell_print() * Printer emulation. The first 2 parameters are the plain text version of the * data to be printed; the remaining 6 parameters are the bitmap version. The * former is used for text-mode copying and for spooling to text files; the * latter is used for graphics-mode coopying, spooling to image files, and * on-screen display. */ void shell_print(const char *text, int length, const char *bits, int bytesperline, int x, int y, int width, int height); /* shell_write() * * Callback for core_export_programs(). Returns 0 if a problem occurred; * core_export_programs() should abort in that case. */ int shell_write(const char *buf, int4 buflen); /* shell_read() * * Callback for core_import_programs(). Returns the number of bytes actually * read. Returns -1 if an error occurred; a return value of 0 signifies end of * input. */ int4 shell_read(char *buf, int4 buflen); /* shell_get_bcd_table() * shell_put_bcd_table() * shell_release_bcd_table() * shell_bcd_table_struct * * On platforms where computing the BCD conversion table is slow, these * functions should be implemented so they save the table in persistent * storage. On other platforms, they can be no-ops. * shell_get_bcd_table() returns NULL if no BCD table image was found. */ typedef struct { double pos_huge_double; double neg_huge_double; double pos_tiny_double; double neg_tiny_double; int2 max_pow2; int2 min_pow2; uint4 pos_pow2exp_offset; /* Offsets are from the end of this struct */ uint4 neg_pow2mant_offset;/* pos_pow2mant_offset is implicitly 0 */ uint4 neg_pow2exp_offset; } shell_bcd_table_struct; shell_bcd_table_struct *shell_get_bcd_table(); shell_bcd_table_struct *shell_put_bcd_table(shell_bcd_table_struct *bcdtab, uint4 size); void shell_release_bcd_table(shell_bcd_table_struct *bcdtab); #if defined(ANDROID) || defined(IPHONE) /* shell_get_acceleration() * shell_get_location() * shell_get_heading() * * These functions were added to support the iPhone's accelerometer, GPS, and * compass. Shells on platforms that do not provide this functionality should * return 0; a return value of 1 indicates success (though not necessarily * accuracy!). * * The units used here match those on the iPhone; implementations on other * platforms should transform their units to match iPhone conventions. This * probably won't be an issue in practice, since the iPhone's units match * established international standards, the one exception being acceleration, * which it expresses in units of Earth gravities rather than the standard * m/s^2. TODO: what is the exact conversion factor used by the iPhone? * * shell_get_acceleration: x, y, z in g's (see above). Looking at the device * in portrait orientation, positive x points to the right, positive y points * up, and positive z points toward the user. * shell_get_location: lat and lon are in decimal degrees, with N and E * positive; lat_lon_acc, elev, and elev_acc are in meters. TODO: what is * elev relative to? * shell_get_heading: mag_heading and true_heading are in decimal degrees, * along the y axis (using the same coordinate system as described above), * with 0 being N, 90 being E, etc., acc is in decimal degrees, and x, y, and z * are magnetic deviation (again using the same coordinate system as above), * in microteslas, but normalized to the range -128..128. TODO: What's this * "normalization"? I hope they mean "clipped", because otherwise you wouldn't * have a unit, yet they claim the unit is microteslas. */ int shell_get_acceleration(double *x, double *y, double *z); int shell_get_location(double *lat, double *lon, double *lat_lon_acc, double *elev, double *elev_acc); int shell_get_heading(double *mag_heading, double *true_heading, double *acc, double *x, double *y, double *z); #endif /* shell_get_time_date() * * Get the current time and date. The date should be provided formatted as * YYYYMMDD, and the time should be provided formatted as HHMMSSss (24-hour). * The weekday is a number from 0 to 6, with 0 being Sunday. */ void shell_get_time_date(uint4 *time, uint4 *date, int *weekday); #ifdef DEBUG void logtofile(const char *message); void lognumber(int4 num); void logdouble(double num); #endif #endif free42-nologo-1.4.77/common/shell_loadimage.cc000644 000765 000024 00000034316 12110237247 021550 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include #include #include #include "shell_skin.h" typedef struct { unsigned char *pixels; SkinColor *cmap; int depth; int width, height, bytesperline; } SkinPixmap; static SkinPixmap pixmap; static int read_byte(int *n) { int c = skin_getchar(); if (c == EOF) return 0; *n = c; return 1; } static int read_short(int *n) { int c1, c2; c1 = skin_getchar(); if (c1 == EOF) return 0; c2 = skin_getchar(); if (c2 == EOF) return 0; *n = c1 + (c2 << 8); return 1; } int shell_loadimage() { SkinPixmap *pm = &pixmap; SkinColor *lcmap = NULL; int sig; int info; int background; int zero; int has_global_cmap; int bpp; int ncolors; int size; int mono; int invert; int i, j, type, res; unsigned char *ptr; if (!read_byte(&sig) || sig != 'G' || !read_byte(&sig) || sig != 'I' || !read_byte(&sig) || sig != 'F' || !read_byte(&sig) || sig != '8' || !read_byte(&sig) || (sig != '7' && sig != '9') || !read_byte(&sig) || sig != 'a') { fprintf(stderr, "GIF signature not found.\n"); return 0; } if (!read_short(&pm->width) || !read_short(&pm->height) || !read_byte(&info) || !read_byte(&background) || !read_byte(&zero) || zero != 0) { fprintf(stderr, "Fatally premature EOF.\n"); return 0; } has_global_cmap = (info & 128) != 0; bpp = (info & 7) + 1; ncolors = 1 << bpp; size = 0; /* Bits 6..4 of info contain one less than the "color resolution", * defined as the number of significant bits per RGB component in * the source image's color palette. If the source image (from * which the GIF was generated) was 24-bit true color, the color * resolution is 8, so the value in bits 6..4 is 7. If the source * image had an EGA color cube (2x2x2), the color resolution would * be 2, etc. * Bit 3 of info must be zero in GIF87a; in GIF89a, if it is set, * it indicates that the global colormap is sorted, the most * important entries being first. In PseudoColor environments this * can be used to make sure to get the most important colors from * the X server first, to optimize the image's appearance in the * event that not all the colors from the colormap can actually be * obtained at the same time. * The 'zero' field is always 0 in GIF87a; in GIF89a, it indicates * the pixel aspect ratio, as (PAR + 15) : 64. If PAR is zero, * this means no aspect ratio information is given, PAR = 1 means * 1:4 (narrow), PAR = 49 means 1:1 (square), PAR = 255 means * slightly over 4:1 (wide), etc. */ pm->cmap = (SkinColor *) malloc(256 * sizeof(SkinColor)); // TODO - handle memory allocation failure if (has_global_cmap) { for (i = 0; i < ncolors; i++) { int r, g, b; if (!read_byte(&r) || !read_byte(&g) || !read_byte(&b)) { fprintf(stderr, "Fatally premature EOF.\n"); goto failed; } pm->cmap[i].r = r; pm->cmap[i].g = g; pm->cmap[i].b = b; } } else { for (i = 0; i < ncolors; i++) { int k = (i * 255) / (ncolors - 1); pm->cmap[i].r = k; pm->cmap[i].g = k; pm->cmap[i].b = k; } } /* Set unused colormap entries to 'black' */ for (i = ncolors; i < 256; i++) { pm->cmap[i].r = 0; pm->cmap[i].g = 0; pm->cmap[i].b = 0; } if (ncolors == 2) { /* Test for true black & white */ if (pm->cmap[0].r == 0 && pm->cmap[0].g == 0 && pm->cmap[0].b == 0 && pm->cmap[1].r == 255 && pm->cmap[1].g == 255 && pm->cmap[1].b == 255) { mono = 1; invert = 0; } else if (pm->cmap[0].r == 255 && pm->cmap[0].g == 255 && pm->cmap[0].b == 255 && pm->cmap[1].r == 0 && pm->cmap[1].g == 0 && pm->cmap[1].b == 0) { mono = 1; invert = 1; } else mono = 0; } else mono = 0; if (mono) { pm->depth = 1; pm->bytesperline = ((pm->width + 31) >> 3) & ~3; } else { pm->depth = 8; pm->bytesperline = (pm->width + 3) & ~3; } size = pm->bytesperline * pm->height; pm->pixels = (unsigned char *) malloc(size); // TODO - handle memory allocation failure memset(pm->pixels, pm->depth == 1 ? background * 255 : background, size); while (1) { int whatnext; if (!read_byte(&whatnext)) goto unexp_eof; if (whatnext == ',') { /* Image */ int ileft, itop, iwidth, iheight; int info; int lbpp; int lncolors; int interlaced; int h; int v; int codesize; int bytecount; short prefix_table[4096]; short code_table[4096]; int maxcode; int clear_code; int end_code; int curr_code_size; int curr_code; int old_code; int bits_needed; int end_code_seen; if (!read_short(&ileft) || !read_short(&itop) || !read_short(&iwidth) || !read_short(&iheight) || !read_byte(&info)) goto unexp_eof; if (itop + iheight > pm->height || ileft + iwidth > pm->width) { fprintf(stderr, "Image position and size not contained within screen size!\n"); goto failed; } /* Bit 3 of info must be zero in GIF87a; in GIF89a, if it * is set, it indicates that the local colormap is sorted, * the most important entries being first. In PseudoColor * environments this can be used to make sure to get the * most important colors from the X server first, to * optimize the image's appearance in the event that not * all the colors from the colormap can actually be * obtained at the same time. */ if ((info & 128) == 0) { /* Using global color map */ lcmap = pm->cmap; lbpp = bpp; lncolors = ncolors; } else { /* Using local color map */ lbpp = (info & 7) + 1; lncolors = 1 << lbpp; lcmap = (SkinColor *) malloc(256 * sizeof(SkinColor)); // TODO - handle memory allocation failure for (i = 0; i < lncolors; i++) { int r, g, b; if (!read_byte(&r) || !read_byte(&g) || !read_byte(&b)) goto unexp_eof; lcmap[i].r = r; lcmap[i].g = g; lcmap[i].b = b; } if (pm->depth != 24) { int newbytesperline = pm->width * 4; int v, h; unsigned char *newpixels = (unsigned char *) malloc(newbytesperline * pm->height); // TODO - handle memory allocation failure for (v = 0; v < pm->height; v++) for (h = 0; h < pm->width; h++) { unsigned char pixel, *newpixel; if (pm->depth == 1) pixel = (pm->pixels[pm->bytesperline * v + (h >> 3)] >> (h & 7)) & 1; else pixel = pm->pixels[pm->bytesperline * v + h]; newpixel = newpixels + (newbytesperline * v + 4 * h); newpixel[0] = 0; newpixel[1] = pm->cmap[pixel].r; newpixel[2] = pm->cmap[pixel].g; newpixel[3] = pm->cmap[pixel].b; } free(pm->pixels); pm->pixels = newpixels; pm->bytesperline = newbytesperline; pm->depth = 24; } } interlaced = (info & 64) != 0; h = 0; v = 0; if (!read_byte(&codesize) || !read_byte(&bytecount)) goto unexp_eof; maxcode = 1 << codesize; for (i = 0; i < maxcode + 2; i++) { prefix_table[i] = -1; code_table[i] = i; } clear_code = maxcode++; end_code = maxcode++; curr_code_size = codesize + 1; curr_code = 0; old_code = -1; bits_needed = curr_code_size; end_code_seen = 0; while (bytecount != 0) { for (i = 0; i < bytecount; i++) { int currbyte; int bits_available; if (!read_byte(&currbyte)) goto unexp_eof; if (end_code_seen) continue; bits_available = 8; while (bits_available != 0) { int bits_copied = bits_needed < bits_available ? bits_needed : bits_available; int bits = currbyte >> (8 - bits_available); bits &= 255 >> (8 - bits_copied); curr_code |= bits << (curr_code_size - bits_needed); bits_available -= bits_copied; bits_needed -= bits_copied; if (bits_needed == 0) { if (curr_code == end_code) { end_code_seen = 1; } else if (curr_code == clear_code) { maxcode = (1 << codesize) + 2; curr_code_size = codesize + 1; old_code = -1; } else { unsigned char expanded[4096]; int explen; if (curr_code < maxcode) { if (maxcode < 4096 && old_code != -1) { int c = curr_code; while (prefix_table[c] != -1) c = prefix_table[c]; c = code_table[c]; prefix_table[maxcode] = old_code; code_table[maxcode] = c; maxcode++; if (maxcode == (1 << curr_code_size) && curr_code_size < 12) curr_code_size++; } } else if (curr_code == maxcode) { /* Once maxcode == 4096, we can't get here * any more, because we refuse to raise * curr_code_size above 12 -- so we can * never read a bigger code than 4095. */ int c; if (old_code == -1) { fprintf(stderr, "Out-of-sequence code in compressed data.\n"); goto done; } c = old_code; while (prefix_table[c] != -1) c = prefix_table[c]; c = code_table[c]; prefix_table[maxcode] = old_code; code_table[maxcode] = c; maxcode++; if (maxcode == (1 << curr_code_size) && curr_code_size < 12) curr_code_size++; } else { fprintf(stderr, "Out-of-sequence code in compressed data.\n"); goto done; } old_code = curr_code; /* Output curr_code! */ explen = 0; while (curr_code != -1) { expanded[explen++] = (unsigned char) code_table[curr_code]; curr_code = prefix_table[curr_code]; } for (j = explen - 1; j >= 0; j--) { int pixel = expanded[j]; if (pm->depth == 8) pm->pixels[pm->bytesperline * (itop + v) + ileft + h] = pixel; else if (pm->depth == 24) { unsigned char *rgb = pm->pixels + (pm->bytesperline * (itop + v) + 4 * (ileft + h)); rgb[0] = 0; rgb[1] = lcmap[pixel].r; rgb[2] = lcmap[pixel].g; rgb[3] = lcmap[pixel].b; } else { /* VERY inefficient. 16 memory accesses * to write one byte. Then again, 1-bit * images never consume very many bytes * and I'm in a hurry. Maybe TODO. */ int x = ileft + h; int index = pm->bytesperline * (itop + v) + (x >> 3); unsigned char mask = 1 << (x & 7); if (pixel) pm->pixels[index] |= mask; else pm->pixels[index] &= ~mask; } if (++h == iwidth) { h = 0; if (interlaced) { switch (v & 7) { case 0: v += 8; if (v < iheight) break; /* Some GIF en/decoders go * straight from the '0' * pass to the '4' pass * without checking the * height, and blow up on * 2/3/4 pixel high * interlaced images. */ if (iheight > 4) v = 4; else if (iheight > 2) v = 2; else if (iheight > 1) v = 1; else end_code_seen = 1; break; case 4: v += 8; if (v >= iheight) v = 2; break; case 2: case 6: v += 4; if (v >= iheight) v = 1; break; case 1: case 3: case 5: case 7: v += 2; if (v >= iheight) end_code_seen = 1; break; } if (end_code_seen) break; } else { if (++v == iheight) { end_code_seen = 1; break; } } } } } curr_code = 0; bits_needed = curr_code_size; } } } if (!read_byte(&bytecount)) goto unexp_eof; } if (lcmap != pm->cmap) free(lcmap); lcmap = NULL; } else if (whatnext == '!') { /* Extension block */ int function_code, byte_count; if (!read_byte(&function_code)) goto unexp_eof; if (!read_byte(&byte_count)) goto unexp_eof; while (byte_count != 0) { for (i = 0; i < byte_count; i++) { int dummy; if (!read_byte(&dummy)) goto unexp_eof; } if (!read_byte(&byte_count)) goto unexp_eof; } } else if (whatnext == ';') { /* Terminator */ break; } else { fprintf(stderr, "Unrecognized tag '%c' (0x%02x).\n", whatnext, whatnext); goto failed; } } done: if (lcmap != NULL && lcmap != pm->cmap) free(lcmap); if (pm->depth == 24) { free(pm->cmap); pm->cmap = NULL; } if (pm->depth == 1) { free(pm->cmap); pm->cmap = NULL; if (invert) for (i = 0; i < size; i++) pm->pixels[i] = ~pm->pixels[i]; } /* Successfully read the image. Now, write it out: */ if (pm->depth == 1) type = IMGTYPE_MONO; else if (pm->depth == 8) type = IMGTYPE_COLORMAPPED; else /* pm->depth == 24 */ type = IMGTYPE_TRUECOLOR; res = skin_init_image(type, 256, pm->cmap, pm->width, pm->height); if (res) { ptr = pm->pixels; for (i = 0; i < pm->height; i++) { skin_put_pixels(ptr); ptr += pm->bytesperline; } skin_finish_image(); } /* Done writing; delete the image and return. */ if (pm->cmap != NULL) free(pm->cmap); free(pm->pixels); return res; unexp_eof: fprintf(stderr, "Unexpected EOF.\n"); goto done; failed: if (lcmap != NULL && lcmap != pm->cmap) free(lcmap); if (pm->cmap != NULL) { free(pm->cmap); pm->cmap = NULL; } if (pm->pixels != NULL) { free(pm->pixels); pm->pixels = NULL; } return 0; } free42-nologo-1.4.77/common/shell_loadimage.h000644 000765 000024 00000002225 12110237247 021404 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef SHELL_LOADIMAGE_H #define SHELL_LOADIMAGE_H 1 /* NOTE: This function obtains the image file data using skin_getchar() and * skin_rewind(), and it writes the in-memory pixmap using skin_init_image(), * skin_putpixels(), and skin_finish_image(). * These functions should be declared in shell_skin.h. */ int shell_loadimage(); #endif free42-nologo-1.4.77/common/shell_spool.cc000644 000765 000024 00000025205 12110237247 020757 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef ANDROID #include #include "shell_spool.h" #include "core_main.h" #endif int hp2ascii(char *dst, const char *src, int srclen) { const char *esc; unsigned char c; int s, d = 0; for (s = 0; s < srclen; s++) { c = src[s]; if (c >= 130 && c != 138) c &= 127; switch (c) { /* NOTE: this code performs the following 12 translations * that are not ASCII, but seem to be widely accepted -- * that is, they looked OK when I tried them in several * fonts in Windows and Linux, and in Memo Pad on the Palm: * * 0: 247 (0367) divide * 1: 215 (0327) multiply * 8: 191 (0277) upside-down question mark * 17: 181 (0265) lowercase mu * 18: 163 (0243) sterling * 19: 176 (0260) degree * 20: 197 (0305) Aring * 21: 209 (0321) Ntilde * 22: 196 (0304) Aumlaut * 25: 198 (0306) AE * 28: 214 (0326) Oumlaut * 29: 220 (0334) Uumlaut * * Two additional candidates are these: * * 26: 133 (0205) ellipsis * 31: 149 (0225) bullet * * I'm not using those last two because support for them is not * as good: they exist in Times New Roman and Adobe Courier * (tested on Windows and Linux, respectively) and on the Palm, * but are missing from Windows Fixedsys (the default Notepad * font, so that is a concern!) and X11 lucidatypewriter and * fixed. * Note that 133 and 149 are both in the 128-159 range, that * is, the Ctrl+Meta range, which is unused in many fonts. * Eventually, I should probably support several translation * modes: raw, pure ASCII (only emit codes 32-126 and 10), * non-pure as below, and more aggressive non-pure (using the * ellipsis and fatdot codes, and maybe others). Then again, * maybe not. :-) */ case 0: esc = "\367"; break; case 1: esc = "\327"; break; case 2: esc = "\\sqrt"; break; case 3: esc = "\\int"; break; case 4: esc = "\\gray1"; break; case 5: esc = "\\Sigma"; break; case 6: esc = ">"; break; case 7: esc = "\\pi"; break; case 8: esc = "\277"; break; case 9: esc = "<="; break; case 11: esc = ">="; break; case 12: esc = "!="; break; case 13: esc = "\\r"; break; case 14: esc = "v"; break; case 15: esc = "->"; break; case 16: esc = "<-"; break; case 17: esc = "\265"; break; case 18: esc = "\243"; break; case 19: esc = "\260"; break; case 20: esc = "\305"; break; case 21: esc = "\321"; break; case 22: esc = "\304"; break; case 23: esc = "\\angle"; break; case 24: esc = "E"; break; case 25: esc = "\306"; break; case 26: esc = "..."; break; case 27: esc = "\\esc"; break; case 28: esc = "\326"; break; case 29: esc = "\334"; break; case 30: esc = "\\gray2"; break; case 31: esc = "\\bullet"; break; case '\\': esc = "\\\\"; break; case 127: esc = "|-"; break; case 128: esc = ":"; break; case 129: esc = "y"; break; case 138: esc = "\\LF"; break; default: dst[d++] = c; continue; } while (*esc != 0) dst[d++] = *esc++; } return d; } #ifndef ANDROID void shell_spool_txt(const char *text, int length, file_writer writer, file_newliner newliner) { if (core_settings.raw_text) writer(text, length); else { char buf[1000]; int d = hp2ascii(buf, text, length); writer(buf, d); } newliner(); } typedef struct { int codesize; int bytecount; char buf[255]; short prefix_table[4096]; short code_table[4096]; short hash_next[4096]; short hash_head[256]; int maxcode; int clear_code; int end_code; int curr_code_size; int prefix; int currbyte; int bits_needed; int initial_clear; int really_done; int height; } gif_data; static gif_data *g; int shell_start_gif(file_writer writer, int provisional_height) { char buf[29]; char *p = buf, c; int width = 143; int height = provisional_height; int i; /* NOTE: the height will be set to the *actual* height once we know * what that is, i.e., when shell_finish_gif() is called. We populate * it using the maximum height (as set in the preferences dialog) so * that even incomplete GIF files will be viewable, just in case the * user is impatient and wants to take a peek. */ /* GIF Header */ *p++ = 'G'; *p++ = 'I'; *p++ = 'F'; *p++ = '8'; *p++ = '7'; *p++ = 'a'; /* Screen descriptor */ *p++ = width & 255; *p++ = width >> 8; *p++ = height & 255; *p++ = height >> 8; *p++ = (char) 0xF0; *p++ = 0; *p++ = 0; /* Global color map */ *p++ = (char) 255; *p++ = (char) 255; *p++ = (char) 255; *p++ = 0; *p++ = 0; *p++ = 0; /* Image Descriptor */ *p++ = ','; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = width & 255; *p++ = width >> 8; *p++ = height & 255; *p++ = height >> 8; *p++ = 0x00; /* Write GIF header & descriptors */ writer(buf, 29); /* Initialize GIF encoder */ if (g == NULL) { g = (gif_data *) malloc(sizeof(gif_data)); if (g == NULL) return 0; } g->codesize = 2; g->bytecount = 0; g->maxcode = 1 << g->codesize; for (i = 0; i < g->maxcode; i++) { g->prefix_table[i] = -1; g->code_table[i] = i; g->hash_next[i] = -1; } for (i = 0; i < 256; i++) g->hash_head[i] = -1; g->clear_code = g->maxcode++; g->end_code = g->maxcode++; g->curr_code_size = g->codesize + 1; g->prefix = -1; g->currbyte = 0; g->bits_needed = 8; g->initial_clear = 1; g->really_done = 0; g->height = 0; c = g->codesize; writer(&c, 1); return 1; } void shell_spool_gif(const char *bits, int bytesperline, int x, int y, int width, int height, file_writer writer) { int v, h; g->height += height; /* Encode Image Data */ for (v = y; v < y + height || (v == y && height == 0); v++) { int done = v == y && height == 0; for (h = 0; h < 143; h++) { int new_code; unsigned char hash_code; int hash_index; int pixel; if (g->really_done) { new_code = g->end_code; goto emit; } else if (done) { new_code = g->prefix; goto emit; } if (h < width) pixel = ((bits[bytesperline * v + (h >> 3)]) >> (h & 7)) & 1; else pixel = 0; /* Look for concat(prefix, pixel) in string table */ if (g->prefix == -1) { g->prefix = pixel; goto no_emit; } /* Compute hash code * TODO: There's a lot of room for improvement here! * I'm getting search percentages of over 30%; looking for * something in single digits. */ { unsigned long x = (((long) g->prefix) << 20) + (((long) pixel) << 12); unsigned char b1, b2, b3; x /= 997; b1 = (unsigned char) (x >> 16); b2 = (unsigned char) (x >> 8); b3 = (unsigned char) x; hash_code = b1 ^ b2 ^ b3; } hash_index = g->hash_head[hash_code]; while (hash_index != -1) { if (g->prefix_table[hash_index] == g->prefix && g->code_table[hash_index] == pixel) { g->prefix = hash_index; goto no_emit; } hash_index = g->hash_next[hash_index]; } /* Not found: */ if (g->maxcode < 4096) { g->prefix_table[g->maxcode] = g->prefix; g->code_table[g->maxcode] = pixel; g->hash_next[g->maxcode] = g->hash_head[hash_code]; g->hash_head[hash_code] = g->maxcode; g->maxcode++; } new_code = g->prefix; g->prefix = pixel; emit: { int outcode = g->initial_clear ? g->clear_code : g->really_done ? g->end_code : new_code; int bits_available = g->curr_code_size; while (bits_available != 0) { int bits_copied = g->bits_needed < bits_available ? g->bits_needed : bits_available; int bits = outcode >> (g->curr_code_size - bits_available); bits &= 255 >> (8 - bits_copied); g->currbyte |= bits << (8 - g->bits_needed); bits_available -= bits_copied; g->bits_needed -= bits_copied; if (g->bits_needed == 0 || (bits_available == 0 && g->really_done)) { g->buf[g->bytecount++] = g->currbyte; if (g->bytecount == 255) { char c = g->bytecount; writer(&c, 1); writer(g->buf, g->bytecount); g->bytecount = 0; } if (bits_available == 0 && g->really_done) goto data_done; g->currbyte = 0; g->bits_needed = 8; } } if (done) { done = 0; g->really_done = 1; goto emit; } if (g->initial_clear) { g->initial_clear = 0; goto emit; } else { if (g->maxcode > (1 << g->curr_code_size)) { g->curr_code_size++; } else if (new_code == g->clear_code) { int i; g->maxcode = (1 << g->codesize) + 2; g->curr_code_size = g->codesize + 1; for (i = 0; i < 256; i++) g->hash_head[i] = -1; } else if (g->maxcode == 4096) { new_code = g->clear_code; goto emit; } } } no_emit:; } } data_done: ; } void shell_finish_gif(file_seeker seeker, file_writer writer) { char c; /* Flush the encoder and write any remaining data */ shell_spool_gif(NULL, 0, 0, 0, 0, 0, writer); if (g->bytecount > 0) { c = g->bytecount; writer(&c, 1); writer(g->buf, g->bytecount); } c = 0; writer(&c, 1); /* GIF Trailer */ c = ';'; writer(&c, 1); /* Update the 'height' fields in the header, now that at last * we know what the final height is */ seeker(8); c = g->height & 255; writer(&c, 1); c = g->height >> 8; writer(&c, 1); seeker(26); c = g->height & 255; writer(&c, 1); c = g->height >> 8; writer(&c, 1); /* All done! */ } void shell_spool_exit() { if (g != NULL) { free(g); g = NULL; } } #endif free42-nologo-1.4.77/common/shell_spool.h000644 000765 000024 00000005432 12110237247 020621 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #ifndef SHELL_SPOOL_H #define SHELL_SPOOL_H 1 #ifndef ANDROID #include "free42.h" typedef void (*file_seeker)(int4 pos); typedef void (*file_writer)(const char *text, int length); typedef void (*file_newliner)(); #endif /* hp2ascii() * * Converts characters outside the range 32-126 and 10 to their ASCII * or extended ASCII equivalents; substitutes escape sequences for characters * for which no reasonable equivalents exist, and for all character codes * above 130. */ int hp2ascii(char *dst, const char *src, int srclen); #ifndef ANDROID /* shell_spool_txt() * * Shell helper that writes plain text to a file, optionally translating * HP-42S characters to their closest ASCII equivalents or escape sequences. * The actual I/O is all done through callbacks so that this routine is OS * independent. */ void shell_spool_txt(const char *text, int length, file_writer writer, file_newliner newliner); /* shell_start_gif() * * Shell helper for writing bitmaps to GIF files. * This call writes the GIF header (except for the length, which isn't known * yet) and initialized the GIF encoder. * Returns 1 on success; 0 if it failed because of a memory allocation * failure. */ int shell_start_gif(file_writer writer, int provisional_height); /* shell_spool_gif() * * Shell helper that writes bitmaps to a GIF file. * The actual I/O is all done through callbacks so that this routine is OS * independent. */ void shell_spool_gif(const char *bits, int bytesperline, int x, int y, int width, int height, file_writer writer); /* shell_finish_gif() * * Shell helper for writing bitmaps to GIF files. * This call flushes the GIF encoder, and writes the final image height to the * GIF header. After this call, the caller should close the output file. */ void shell_finish_gif(file_seeker seeker, file_writer writer); /* shell_spool_exit() * * Cleans up spooler's private data. Call this just before application exit. */ void shell_spool_exit(); #endif #endif free42-nologo-1.4.77/common/skin2cc.cc000644 000765 000024 00000016510 12110237247 017767 0ustar00thomasstaff000000 000000 /***************************************************************************** * Free42 -- an HP-42S calculator simulator * Copyright (C) 2004-2013 Thomas Okken * * 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, see http://www.gnu.org/licenses/. *****************************************************************************/ #include #include #include #ifdef WINDOWS #define SKINS_CC "skins.cpp" #define SKIN2CC_CONF "skin2cpp.conf" #define SKIN2CC "skin2cpp" #else #define SKINS_CC "skins.cc" #define SKIN2CC_CONF "skin2cc.conf" #define SKIN2CC "skin2cc" #endif FILE *out; void write_bytes(FILE *file) { int pos; int first = 1; int c; while ((c = fgetc(file)) != EOF) { int width = c < 10 ? 1 : c < 100 ? 2 : 3; if (first) { first = 0; fprintf(out, " "); pos = 4; } else if (pos + width > 74) { fprintf(out, ",\n "); pos = 4; } else { fprintf(out, ", "); pos += 2; } fprintf(out, "%d", c); pos += width; } } int main(int argc, char *argv[]) { FILE *conf; FILE *inp; char line[256]; char skinname[100][256]; char skinfile[100][256]; int nskins = 0, i; out = fopen(SKINS_CC, "w"); if (out == NULL) { fprintf(stderr, "%s: can't open output file \"%s\"\n", SKIN2CC, SKINS_CC); return 1; } fprintf(out, "/* %s\n" " * Contains the built-in skins for Free42 (Unix & Windows).\n" " * This file is generated by the %s program,\n" " * under control of the %s file, which is\n" " * a list of skin descriptions and their user-visible names;\n" " * each entry consists of the skin name on one line, followed\n" " * by the user-visible name on the next line.\n" " * The skins consist of two files, .layout (the layout \n" " * description), and .gif (the skin bitmap); these files \n" " * are looked for in ../.\n" " * NOTE: this is a generated file; do not edit!\n" " */\n\n", SKINS_CC, SKIN2CC, SKIN2CC_CONF); #ifdef WINDOWS fprintf(out, "#ifndef __GNUC__\n"); fprintf(out, "/* Disable warning 'initializing' : truncation from 'const int ' to 'const char ' */\n"); fprintf(out, "#pragma warning(disable: 4305)\n"); fprintf(out, "/* Disable warning 'initializing' : truncation of constant value */\n"); fprintf(out, "#pragma warning(disable: 4309)\n"); fprintf(out, "#endif\n\n"); #endif conf = fopen(SKIN2CC_CONF, "r"); if (conf == NULL) { int err = errno; fprintf(stderr, "Can't open \"%s\": %s (%d).\n", SKIN2CC_CONF, strerror(err), err); fclose(out); remove(SKINS_CC); return 1; } while (1) { int len; if (fgets(line, 256, conf) == NULL) break; len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[--len] = 0; strcpy(skinfile[nskins], line); if (fgets(line, 256, conf) == NULL) break; len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[--len] = 0; strcpy(skinname[nskins], line); nskins++; } fclose(conf); fprintf(out, "/****************************************/\n"); fprintf(out, "/* Number of skins defined in this file */\n"); fprintf(out, "/****************************************/\n\n"); fprintf(out, "int skin_count = %d;\n\n\n", nskins); fprintf(out, "/**************/\n"); fprintf(out, "/* Skin names */\n"); fprintf(out, "/**************/\n\n"); fprintf(out, "const char *skin_name[] = {\n"); for (i = 0; i < nskins; i++) fprintf(out, " \"%s\"%s\n", skinname[i], i < nskins - 1 ? "," : ""); fprintf(out, "};\n\n\n"); fprintf(out, "/*************************************/\n"); fprintf(out, "/* Sizes of skin layout descriptions */\n"); fprintf(out, "/*************************************/\n\n"); // TODO: If I put 'const' here, the symbol is not exported. Why? fprintf(out, "/*const*/ long skin_layout_size[] = {\n"); for (i = 0; i < nskins; i++) { char fname[1024]; strcpy(fname, "../"); strcat(fname, skinfile[i]); strcat(fname, ".layout"); inp = fopen(fname, "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"%s\": %s (%d)\n", fname, strerror(err), err); fclose(out); remove(SKINS_CC); return 1; } fseek(inp, 0, SEEK_END); fprintf(out, " %d%s\n", ftell(inp), i < nskins - 1 ? "," : ""); fclose(inp); } fprintf(out, "};\n\n\n"); fprintf(out, "/****************************/\n"); fprintf(out, "/* Skin layout descriptions */\n"); fprintf(out, "/****************************/\n\n"); for (i = 0; i < nskins; i++) { char fname[1024]; strcpy(fname, "../"); strcat(fname, skinfile[i]); strcat(fname, ".layout"); inp = fopen(fname, "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"%s\": %s (%d)\n", fname, strerror(err), err); fclose(out); remove(SKINS_CC); return 1; } fprintf(out, "static const unsigned char skin%d_layout_data[] = {\n", i); write_bytes(inp); fprintf(out, "\n};\n\n"); fclose(inp); } fprintf(out, "/*const*/ unsigned char *skin_layout_data[] = {\n"); for (i = 0; i < nskins; i++) fprintf(out, " (unsigned char *) skin%d_layout_data%s\n", i, i < nskins - 1 ? "," : ""); fprintf(out, "};\n\n\n"); fprintf(out, "/*************************/\n"); fprintf(out, "/* Sizes of skin bitmaps */\n"); fprintf(out, "/*************************/\n\n"); // TODO: If I put 'const' here, the symbol is not exported. Why? fprintf(out, "/*const*/ long skin_bitmap_size[] = {\n"); for (i = 0; i < nskins; i++) { char fname[1024]; strcpy(fname, "../"); strcat(fname, skinfile[i]); strcat(fname, ".gif"); inp = fopen(fname, "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"%s\": %s (%d)\n", fname, strerror(err), err); fclose(out); remove(SKINS_CC); return 1; } fseek(inp, 0, SEEK_END); fprintf(out, " %d%s\n", ftell(inp), i < nskins - 1 ? "," : ""); fclose(inp); } fprintf(out, "};\n\n\n"); fprintf(out, "/****************/\n"); fprintf(out, "/* Skin bitmaps */\n"); fprintf(out, "/****************/\n\n"); for (i = 0; i < nskins; i++) { char fname[1024]; strcpy(fname, "../"); strcat(fname, skinfile[i]); strcat(fname, ".gif"); inp = fopen(fname, "rb"); if (inp == NULL) { int err = errno; fprintf(stderr, "Can't open \"%s\": %s (%d)\n", fname, strerror(err), err); fclose(out); remove(SKINS_CC); return 1; } fprintf(out, "static const unsigned char skin%d_bitmap_data[] = {\n", i); write_bytes(inp); fprintf(out, "\n};\n\n"); fclose(inp); } fprintf(out, "/*const*/ unsigned char *skin_bitmap_data[] = {\n"); for (i = 0; i < nskins; i++) fprintf(out, " (unsigned char *) skin%d_bitmap_data%s\n", i, i < nskins - 1 ? "," : ""); fprintf(out, "};\n\n\n"); fprintf(out, "/***********/\n"); fprintf(out, "/* The End */\n"); fprintf(out, "/***********/\n"); fclose(out); return 0; } free42-nologo-1.4.77/common/skin2cc.conf000644 000765 000024 00000000064 12110237247 020324 0ustar00thomasstaff000000 000000 skins/Standard Standard skins/Ehrling42sl Realistic