From b3b5f4bb333e1928415bc7c925557934e81e19df Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Fri, 5 Mar 2021 15:13:16 -0500 Subject: [PATCH] commit bash-20210301 snapshot --- CWRU/CWRU.chlog | 42 +- MANIFEST | 11 + builtins/exec.def | 2 +- builtins/ulimit.def | 9 + doc/bash.0 | 10 +- doc/bash.1 | 14 +- doc/bashref.info | 297 ++++---- doc/bashref.texi | 21 + doc/version.texi | 6 +- examples/shellmath/LICENSE | 677 +++++++++++++++++ examples/shellmath/README.md | 166 +++++ examples/shellmath/assert.sh | 85 +++ examples/shellmath/faster_e_demo.sh | 68 ++ examples/shellmath/image.png | Bin 0 -> 48779 bytes examples/shellmath/runTests.sh | 124 ++++ examples/shellmath/shellmath.sh | 1068 +++++++++++++++++++++++++++ examples/shellmath/slower_e_demo.sh | 55 ++ examples/shellmath/testCases.in | 142 ++++ examples/shellmath/timingData.txt | 42 ++ subst.c | 2 +- support/shobj-conf | 4 +- tests/builtins.right | 7 +- tests/builtins.tests | 4 +- 23 files changed, 2700 insertions(+), 156 deletions(-) create mode 100644 examples/shellmath/LICENSE create mode 100644 examples/shellmath/README.md create mode 100644 examples/shellmath/assert.sh create mode 100755 examples/shellmath/faster_e_demo.sh create mode 100644 examples/shellmath/image.png create mode 100644 examples/shellmath/runTests.sh create mode 100644 examples/shellmath/shellmath.sh create mode 100755 examples/shellmath/slower_e_demo.sh create mode 100644 examples/shellmath/testCases.in create mode 100644 examples/shellmath/timingData.txt diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 024e33c1..6361eea5 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -9662,6 +9662,46 @@ doc/bashref.texi - replaced a number of uses of @var with a mixture of @env and @dfn to better match up with the texinfo standards -doc/{bash/1,bashref.texi} +doc/{bash.1,bashref.texi} - clarify some aspects of the coproc description, especially the use of NAME and when it's optional + + 3/1 + --- +subst.c + - read_comsub: fix off-by-one error in mbrtowc that causes a read one + character past the end of buf. Report and fix from + Platon Pronko in + https://savannah.gnu.org/patch/?10035 + + 3/3 + --- +builtins/ulimit.def + - ulimit_builtin: Posix compatibility: if the last command specified + by an option does not have an option argument, but there is an + operand remaining after all the options are parsed, treat the + operand as an argument to that last command. From an austin-group + discussion and a Geoff Clare suggestion back in November, 2020. + Austin Group interpretation 1418 + +examples/shellmath + - a package of shell functions to perform floating-point math entirely + in bash. Contributed by Michael Wood . Available + at https://github.com/clarity20/shellmath + + 3/4 + --- +support/shobj-conf + - darwin: take out the -arch-only option in SHOBJ_XLDFLAGS and + SHOBJ_ARCHFLAGS; no longer needed + +doc/{bash.1,bashref.texi} + - coprocesses: suggested changes from rms@gnu.org; recommend the + `coproc NAME { commands; }' form as the simplest and most flexible + + 3/5 + --- +builtins/exec.def + - exec_builtin: set last_command_exit_value before calling exit_shell + so any exit trap gets the right value for $?. From Matthew Bauer + via https://savannah.gnu.org/patch/?10039 diff --git a/MANIFEST b/MANIFEST index 4c4eefe8..27af76b6 100644 --- a/MANIFEST +++ b/MANIFEST @@ -22,6 +22,7 @@ examples/startup-files d examples/misc d examples/loadables d examples/loadables/perl d +examples/shellmath d include d lib d lib/glob d @@ -864,6 +865,16 @@ examples/startup-files/bashrc f examples/misc/aliasconv.sh f examples/misc/aliasconv.bash f examples/misc/cshtobash f +examples/shellmath/LICENSE f +examples/shellmath/README.md f +examples/shellmath/assert.sh f +examples/shellmath/faster_e_demo.sh* f +examples/shellmath/image.png f +examples/shellmath/runTests.sh f +examples/shellmath/shellmath.sh f +examples/shellmath/slower_e_demo.sh* f +examples/shellmath/testCases.in f +examples/shellmath/timingData.txt f tests/README f tests/COPYRIGHT f tests/test-glue-functions f diff --git a/builtins/exec.def b/builtins/exec.def index cbcb641a..cc10278a 100644 --- a/builtins/exec.def +++ b/builtins/exec.def @@ -253,7 +253,7 @@ failed_exec: FREE (command); if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0)) - exit_shell (exit_value); + exit_shell (last_command_exit_value = exit_value); if (args) strvec_dispose (args); diff --git a/builtins/ulimit.def b/builtins/ulimit.def index fb844184..f6e6572b 100644 --- a/builtins/ulimit.def +++ b/builtins/ulimit.def @@ -428,6 +428,15 @@ ulimit_builtin (list) } } + /* POSIX compatibility. If the last item in cmdlist does not have an option + argument, but there is an operand (list != 0), treat the operand as if + it were an option argument for that last command. */ + if (list && list->word && cmdlist[ncmd - 1].arg == 0) + { + cmdlist[ncmd - 1].arg = list->word->word; + list = list->next; + } + for (c = 0; c < ncmd; c++) if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE) return (EXECUTION_FAILURE); diff --git a/doc/bash.0 b/doc/bash.0 index 259e225f..d3964a5e 100644 --- a/doc/bash.0 +++ b/doc/bash.0 @@ -521,6 +521,14 @@ SSHHEELLLL GGRRAAMMMMAARR command or a compound command (see above). _N_A_M_E is a shell variable name. If _N_A_M_E is not supplied, the default name is CCOOPPRROOCC. + The recommended form to use for a coprocess is + + ccoopprroocc _N_A_M_E { _c_o_m_m_a_n_d [_r_e_d_i_r_e_c_t_i_o_n_s]; } + + This form is recommended because simple commands result in the copro- + cess always being named CCOOPPRROOCC, and it is simpler to use and more com- + plete than the other compound commands. + If _c_o_m_m_a_n_d is a compound command, _N_A_M_E is optional. The word following ccoopprroocc determines whether that word is interpreted as a variable name: it is interpreted as _N_A_M_E if it is not a reserved word that introduces @@ -6416,4 +6424,4 @@ BBUUGGSS -GNU Bash 5.1 2021 February 28 BASH(1) +GNU Bash 5.1 2021 March 4 BASH(1) diff --git a/doc/bash.1 b/doc/bash.1 index 8205056c..5ef5d4f6 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Sun Feb 28 16:42:54 EST 2021 +.\" Last Change: Thu Mar 4 15:52:34 EST 2021 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2021 February 28" "GNU Bash 5.1" +.TH BASH 1 "2021 March 4" "GNU Bash 5.1" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -958,6 +958,16 @@ command (see above). \fINAME\fP is a shell variable name. If \fINAME\fP is not supplied, the default name is \fBCOPROC\fP. .PP +The recommended form to use for a coprocess is +.RS +.PP +\fBcoproc\fP \fINAME\fP { \fIcommand\fP [\fIredirections\fP]; } +.RE +.PP +This form is recommended because simple commands result in the coprocess +always being named \fBCOPROC\fP, and it is simpler to use and more complete +than the other compound commands. +.PP If \fIcommand\fP is a compound command, \fINAME\fP is optional. The word following \fBcoproc\fP determines whether that word is interpreted as a variable name: it is interpreted as \fINAME\fP if it is not a diff --git a/doc/bashref.info b/doc/bashref.info index db8191e2..cd943692 100644 --- a/doc/bashref.info +++ b/doc/bashref.info @@ -2,9 +2,9 @@ This is bashref.info, produced by makeinfo version 6.7 from bashref.texi. This text is a brief description of the features that are present in the -Bash shell (version 5.1, 28 February 2021). +Bash shell (version 5.1, 4 March2021). - This is Edition 5.1, last updated 28 February 2021, of 'The GNU Bash + This is Edition 5.1, last updated 4 March2021, of 'The GNU Bash Reference Manual', for 'Bash', Version 5.1. Copyright (C) 1988-2021 Free Software Foundation, Inc. @@ -27,10 +27,10 @@ Bash Features ************* This text is a brief description of the features that are present in the -Bash shell (version 5.1, 28 February 2021). The Bash home page is +Bash shell (version 5.1, 4 March2021). The Bash home page is . - This is Edition 5.1, last updated 28 February 2021, of 'The GNU Bash + This is Edition 5.1, last updated 4 March2021, of 'The GNU Bash Reference Manual', for 'Bash', Version 5.1. Bash contains features that appear in other popular shells, and some @@ -1059,6 +1059,7 @@ had been terminated with the '&' control operator, with a two-way pipe established between the executing shell and the coprocess. The syntax for a coprocess is: + coproc [NAME] COMMAND [REDIRECTIONS] This creates a coprocess named NAME. COMMAND may be either a simple @@ -1066,12 +1067,26 @@ command (*note Simple Commands::) or a compound command (*note Compound Commands::). NAME is a shell variable name. If NAME is not supplied, the default name is 'COPROC'. - If COMMAND is a compound command, NAME is optional. The word -following 'coproc' determines whether that word is interpreted as a -variable name: it is interpreted as NAME if it is not a reserved word -that introduces a compound command. If COMMAND is a simple command, -NAME is not allowed; this is to avoid confusion between NAME and the -first word of the simple command. + The recommended form to use for a coprocess is + + coproc NAME { COMMAND; } + +This form is recommended because simple commands result in the coprocess +always being named 'COPROC', and it is simpler to use and more complete +than the other compound commands. + + There are other forms of coprocesses: + + coproc NAME COMPOUND-COMMAND + coproc COMPOUND-COMMAND + coproc SIMPLE-COMMAND + +If COMMAND is a compound command, NAME is optional. The word following +'coproc' determines whether that word is interpreted as a variable name: +it is interpreted as NAME if it is not a reserved word that introduces a +compound command. If COMMAND is a simple command, NAME is not allowed; +this is to avoid confusion between NAME and the first word of the simple +command. When the coprocess is executed, the shell creates an array variable (*note Arrays::) named NAME in the context of the executing shell. The @@ -12007,137 +12022,137 @@ D.5 Concept Index  Tag Table: -Node: Top897 -Node: Introduction2817 -Node: What is Bash?3033 -Node: What is a shell?4147 -Node: Definitions6685 -Node: Basic Shell Features9636 -Node: Shell Syntax10855 -Node: Shell Operation11881 -Node: Quoting13174 -Node: Escape Character14478 -Node: Single Quotes14963 -Node: Double Quotes15311 -Node: ANSI-C Quoting16589 -Node: Locale Translation17846 -Node: Comments19001 -Node: Shell Commands19619 -Node: Reserved Words20557 -Node: Simple Commands21313 -Node: Pipelines21967 -Node: Lists24924 -Node: Compound Commands26719 -Node: Looping Constructs27731 -Node: Conditional Constructs30226 -Node: Command Grouping41797 -Node: Coprocesses43272 -Node: GNU Parallel45542 -Node: Shell Functions49843 -Node: Shell Parameters57063 -Node: Positional Parameters61496 -Node: Special Parameters62398 -Node: Shell Expansions65622 -Node: Brace Expansion67749 -Node: Tilde Expansion70474 -Node: Shell Parameter Expansion73095 -Node: Command Substitution88224 -Node: Arithmetic Expansion89579 -Node: Process Substitution90511 -Node: Word Splitting91631 -Node: Filename Expansion93575 -Node: Pattern Matching96124 -Node: Quote Removal100114 -Node: Redirections100409 -Node: Executing Commands109983 -Node: Simple Command Expansion110653 -Node: Command Search and Execution112607 -Node: Command Execution Environment114985 -Node: Environment117971 -Node: Exit Status119634 -Node: Signals121306 -Node: Shell Scripts123273 -Node: Shell Builtin Commands126285 -Node: Bourne Shell Builtins128323 -Node: Bash Builtins149254 -Node: Modifying Shell Behavior179404 -Node: The Set Builtin179749 -Node: The Shopt Builtin190162 -Node: Special Builtins205074 -Node: Shell Variables206053 -Node: Bourne Shell Variables206490 -Node: Bash Variables208594 -Node: Bash Features241252 -Node: Invoking Bash242265 -Node: Bash Startup Files248278 -Node: Interactive Shells253381 -Node: What is an Interactive Shell?253791 -Node: Is this Shell Interactive?254440 -Node: Interactive Shell Behavior255255 -Node: Bash Conditional Expressions258768 -Node: Shell Arithmetic263345 -Node: Aliases266289 -Node: Arrays268902 -Node: The Directory Stack274911 -Node: Directory Stack Builtins275695 -Node: Controlling the Prompt278663 -Node: The Restricted Shell281611 -Node: Bash POSIX Mode284205 -Node: Shell Compatibility Mode295478 -Node: Job Control302134 -Node: Job Control Basics302594 -Node: Job Control Builtins307596 -Node: Job Control Variables312996 -Node: Command Line Editing314152 -Node: Introduction and Notation315823 -Node: Readline Interaction317446 -Node: Readline Bare Essentials318637 -Node: Readline Movement Commands320420 -Node: Readline Killing Commands321380 -Node: Readline Arguments323298 -Node: Searching324342 -Node: Readline Init File326528 -Node: Readline Init File Syntax327787 -Node: Conditional Init Constructs348325 -Node: Sample Init File352521 -Node: Bindable Readline Commands355645 -Node: Commands For Moving356849 -Node: Commands For History358900 -Node: Commands For Text363693 -Node: Commands For Killing367342 -Node: Numeric Arguments370375 -Node: Commands For Completion371514 -Node: Keyboard Macros375705 -Node: Miscellaneous Commands376392 -Node: Readline vi Mode382076 -Node: Programmable Completion382983 -Node: Programmable Completion Builtins390763 -Node: A Programmable Completion Example401458 -Node: Using History Interactively406705 -Node: Bash History Facilities407389 -Node: Bash History Builtins410394 -Node: History Interaction415402 -Node: Event Designators419022 -Node: Word Designators420376 -Node: Modifiers422136 -Node: Installing Bash423947 -Node: Basic Installation425084 -Node: Compilers and Options428342 -Node: Compiling For Multiple Architectures429083 -Node: Installation Names430776 -Node: Specifying the System Type431594 -Node: Sharing Defaults432310 -Node: Operation Controls432983 -Node: Optional Features433941 -Node: Reporting Bugs444741 -Node: Major Differences From The Bourne Shell445935 -Node: GNU Free Documentation License462785 -Node: Indexes487962 -Node: Builtin Index488416 -Node: Reserved Word Index495243 -Node: Variable Index497691 -Node: Function Index513588 -Node: Concept Index527098 +Node: Top887 +Node: Introduction2797 +Node: What is Bash?3013 +Node: What is a shell?4127 +Node: Definitions6665 +Node: Basic Shell Features9616 +Node: Shell Syntax10835 +Node: Shell Operation11861 +Node: Quoting13154 +Node: Escape Character14458 +Node: Single Quotes14943 +Node: Double Quotes15291 +Node: ANSI-C Quoting16569 +Node: Locale Translation17826 +Node: Comments18981 +Node: Shell Commands19599 +Node: Reserved Words20537 +Node: Simple Commands21293 +Node: Pipelines21947 +Node: Lists24904 +Node: Compound Commands26699 +Node: Looping Constructs27711 +Node: Conditional Constructs30206 +Node: Command Grouping41777 +Node: Coprocesses43252 +Node: GNU Parallel45915 +Node: Shell Functions50216 +Node: Shell Parameters57436 +Node: Positional Parameters61869 +Node: Special Parameters62771 +Node: Shell Expansions65995 +Node: Brace Expansion68122 +Node: Tilde Expansion70847 +Node: Shell Parameter Expansion73468 +Node: Command Substitution88597 +Node: Arithmetic Expansion89952 +Node: Process Substitution90884 +Node: Word Splitting92004 +Node: Filename Expansion93948 +Node: Pattern Matching96497 +Node: Quote Removal100487 +Node: Redirections100782 +Node: Executing Commands110356 +Node: Simple Command Expansion111026 +Node: Command Search and Execution112980 +Node: Command Execution Environment115358 +Node: Environment118344 +Node: Exit Status120007 +Node: Signals121679 +Node: Shell Scripts123646 +Node: Shell Builtin Commands126658 +Node: Bourne Shell Builtins128696 +Node: Bash Builtins149627 +Node: Modifying Shell Behavior179777 +Node: The Set Builtin180122 +Node: The Shopt Builtin190535 +Node: Special Builtins205447 +Node: Shell Variables206426 +Node: Bourne Shell Variables206863 +Node: Bash Variables208967 +Node: Bash Features241625 +Node: Invoking Bash242638 +Node: Bash Startup Files248651 +Node: Interactive Shells253754 +Node: What is an Interactive Shell?254164 +Node: Is this Shell Interactive?254813 +Node: Interactive Shell Behavior255628 +Node: Bash Conditional Expressions259141 +Node: Shell Arithmetic263718 +Node: Aliases266662 +Node: Arrays269275 +Node: The Directory Stack275284 +Node: Directory Stack Builtins276068 +Node: Controlling the Prompt279036 +Node: The Restricted Shell281984 +Node: Bash POSIX Mode284578 +Node: Shell Compatibility Mode295851 +Node: Job Control302507 +Node: Job Control Basics302967 +Node: Job Control Builtins307969 +Node: Job Control Variables313369 +Node: Command Line Editing314525 +Node: Introduction and Notation316196 +Node: Readline Interaction317819 +Node: Readline Bare Essentials319010 +Node: Readline Movement Commands320793 +Node: Readline Killing Commands321753 +Node: Readline Arguments323671 +Node: Searching324715 +Node: Readline Init File326901 +Node: Readline Init File Syntax328160 +Node: Conditional Init Constructs348698 +Node: Sample Init File352894 +Node: Bindable Readline Commands356018 +Node: Commands For Moving357222 +Node: Commands For History359273 +Node: Commands For Text364066 +Node: Commands For Killing367715 +Node: Numeric Arguments370748 +Node: Commands For Completion371887 +Node: Keyboard Macros376078 +Node: Miscellaneous Commands376765 +Node: Readline vi Mode382449 +Node: Programmable Completion383356 +Node: Programmable Completion Builtins391136 +Node: A Programmable Completion Example401831 +Node: Using History Interactively407078 +Node: Bash History Facilities407762 +Node: Bash History Builtins410767 +Node: History Interaction415775 +Node: Event Designators419395 +Node: Word Designators420749 +Node: Modifiers422509 +Node: Installing Bash424320 +Node: Basic Installation425457 +Node: Compilers and Options428715 +Node: Compiling For Multiple Architectures429456 +Node: Installation Names431149 +Node: Specifying the System Type431967 +Node: Sharing Defaults432683 +Node: Operation Controls433356 +Node: Optional Features434314 +Node: Reporting Bugs445114 +Node: Major Differences From The Bourne Shell446308 +Node: GNU Free Documentation License463158 +Node: Indexes488335 +Node: Builtin Index488789 +Node: Reserved Word Index495616 +Node: Variable Index498064 +Node: Function Index513961 +Node: Concept Index527471  End Tag Table diff --git a/doc/bashref.texi b/doc/bashref.texi index 7dd87c04..d5fca9b9 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -1230,6 +1230,7 @@ had been terminated with the @samp{&} control operator, with a two-way pipe established between the executing shell and the coprocess. The syntax for a coprocess is: + @example coproc [@var{NAME}] @var{command} [@var{redirections}] @end example @@ -1241,6 +1242,26 @@ or a compound command (@pxref{Compound Commands}). @var{NAME} is a shell variable name. If @var{NAME} is not supplied, the default name is @code{COPROC}. +The recommended form to use for a coprocess is + +@example +coproc @var{NAME} @{ @var{command}; @} +@end example + +@noindent +This form is recommended because simple commands result in the coprocess +always being named @code{COPROC}, and it is simpler to use and more complete +than the other compound commands. + +There are other forms of coprocesses: + +@example +coproc @var{NAME} @var{compound-command} +coproc @var{compound-command} +coproc @var{simple-command} +@end example + +@noindent If @var{command} is a compound command, @var{NAME} is optional. The word following @code{coproc} determines whether that word is interpreted as a variable name: it is interpreted as @var{NAME} if it is not a diff --git a/doc/version.texi b/doc/version.texi index eee3f332..a972d7cb 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2021 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Sun Feb 28 15:12:34 EST 2021 +@set LASTCHANGE Thu Mar 4 15:49:19 EST 2021 @set EDITION 5.1 @set VERSION 5.1 -@set UPDATED 28 February 2021 -@set UPDATED-MONTH February 2021 +@set UPDATED 4 March2021 +@set UPDATED-MONTH March 2021 diff --git a/examples/shellmath/LICENSE b/examples/shellmath/LICENSE new file mode 100644 index 00000000..e3bf3bb7 --- /dev/null +++ b/examples/shellmath/LICENSE @@ -0,0 +1,677 @@ +Shellfloat is copyright (c) 2020 by Michael Wood. +================================================================================ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/examples/shellmath/README.md b/examples/shellmath/README.md new file mode 100644 index 00000000..1b472561 --- /dev/null +++ b/examples/shellmath/README.md @@ -0,0 +1,166 @@ +# Shellmath +Introducing decimal arithmetic libraries for the Bash shell, because +they said it couldn't be done... and because: + +. + +![image info](./image.png) + +## Quick-start guide +Download this project and source the file `shellmath.sh` into your shell script, +then fire away at the shellmath API! + +The ___basic___ API looks like this: +``` + _shellmath_add arg1 arg2 [...] argN + _shellmath_subtract arg1 arg2 # means arg1 - arg2 + _shellmath_multiply arg1 arg2 [...] argN + _shellmath_divide arg1 arg2 # means arg1 / arg2 +``` + +The ___extended___ API introduces one more function: +``` + _shellmath_getReturnValue arg +``` + +This function optimizes away the need for ___$(___ subshelling ___)___ in order to capture `shellmath`'s output. +To use this feature, just be sure to set `__shellmath_isOptimized=1` at the top +of your script. (You can find an example in `faster_e_demo.sh`.) + +Operands to the _shellmath_ functions can be integers or decimal +numbers presented in either standard or scientific notation: +``` + _shellmath_add 1.009 4.223e-2 + _shellmath_getReturnValue sum + echo "The sum is $sum" +``` +Addition and multiplication are of arbitrary arity; try this on for size: +``` + _shellmath_multiply 1 2 3 4 5 6 + _shellmath_getReturnValue sixFactorial + echo "6 factorial is $sixFactorial" +``` +Subtraction and division, OTOH, are exclusively binary operations. + +## The demos +For a gentle introduction to `shellmath` run the demo `slower_e_demo.sh` +with a small whole-number argument, say 15: +``` +$ slower_e_demo.sh 15 +e = 2.7182818284589936 +``` + +This script uses a few `shellmath` API calls to calculate *e*, the mathematical +constant also known as [Euler's number](https://oeis.org/A001113). The argument +*15* tells the script to evaluate the *15th-degree* Maclaurin polynomial for *e*. +(That's the Taylor polynomial centered at 0.) Take a look inside the script to +see how it uses the `shellmath` APIs. + +There is another demo script very much like this one but *different*, and the +sensitive user can *feel* the difference. Try the following, but don't blink +or you'll miss it ;) +``` +$ faster_e_demo.sh 15 +e = 2.7182818284589936 +``` + +Did you feel the difference? Try the `-t` option with both scripts; this will produce +timing statistics. Here are my results +when running from my minGW64 command prompt on Windows 10 with an Intel i3 Core CPU: +``` +$ for n in {1..5}; do faster_e_demo.sh -t 15 2>&1; done | awk '/^real/ {print $2}' +0m0.055s +0m0.051s +0m0.056s +0m0.054s +0m0.054s + +$ for n in {1..5}; do slower_e_demo.sh -t 15 2>&1; done | awk '/^real/ {print $2}' +0m0.498s +0m0.594s +0m0.536s +0m0.511s +0m0.580s +``` + +(When sizing up these timings, do keep in mind that ___we are timing the +calculation of e from its Maclaurin polynomial. Every invocation of either +script is exercising the shellmath arithmetic subroutines 31 times.___) + +The comment header in `faster_e_demo.sh` explains the optimization and shows +how to put this faster version to work for you. + +## Runtime efficiency competitive with awk and bc +The file `timingData.txt` captures the results of some timing experiments that compare +`shellmath` against the GNU versions of the calculators `awk` and `bc`. The experiments +exercised each of the arithmetic operations and captured the results in a shell variable. +The result summary below shows that `shellmath` is competitive with `awk` and runs faster +than `bc` in these experiments. (One commenter noted that the differences in execution speed +can be partially explained by the fact that `shellmath` and `awk` use finite precision +whereas `bc` uses arbitrary precision. Another factor in these measurements is the need to +subshell 'awk' and 'bc' to capture their results, whereas 'shellmath' writes directly to +the shell's global memory.) + +Here are the run times of `shellmath` as a percentage of the `awk` and `bc` equivalents: +``` + versus awk versus bc + Addition: 82.2% 40.6% + Subtraction: 95.9% 50.5% + Multiplication: 135.9% 73.3% + Division: 80.3% 43.2% +``` + +Astute observers will note the experiments provide approximations to the sum, difference, +product, and quotient of *pi* and *e*. Unfortunately I did not gain insight as to which +of these values, if any, are +[transcendental](https://en.wikipedia.org/wiki/Transcendental_number#Possible_transcendental_numbers). + +You can find a deeper discussion of shellmath's runtime efficiency +[here](https://github.com/clarity20/shellmath/wiki/Shellmath-and-runtime-efficiency). + +## Background +The Bash shell does not have built-in operators for decimal arithmetic, making it +something of an oddity among well-known, widely-used programming languages. For the most part, +practitioners in need of powerful computational building blocks have naturally opted +for *other* languages and tools. Their widespread availability has diverted attention +from the possibility of *implementing* decimal arithmetic in Bash and it's easy to assume +that this ***cannot*** be done: + ++ From the indispensable _Bash FAQ_ (on _Greg's Wiki_): [How can I calculate with floating point numbers?](http://mywiki.wooledge.org/BashFAQ/022) + *"For most operations... an external program must be used."* ++ From Mendel Cooper's wonderful and encyclopedic _Advanced Bash Scripting Guide_: + [Bash does not understand floating point arithmetic. Use bc instead.](https://tldp.org/LDP/abs/html/ops.html#NOFLOATINGPOINT) ++ From a community discussion on Stack Overflow, _How do I use floating point division in bash?_ + The user's [preferred answer](https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash#12722107) + is a good example of _prevailing thought_ on this subject. + +Meanwhile, + ++ Bash maintainer (BDFL?) Chet Ramey sounds a (brighter?) note in [The Bash Reference Guide, Section 6.5](https://tiswww.case.edu/php/chet/bash/bashref.html#Shell-Arithmetic) + by emphasizing what the built-in arithmetic operators ***can*** do. + +But finally, a glimmer of hope: + ++ A [diamond-in-the-rough](http://stackoverflow.com/a/24431665/3776858) buried elsewhere + on Stack Overflow. + This down-and-dirty milestone computes the decimal quotient of two integer arguments. At a casual + glance, it seems to have drawn inspiration from the [Euclidean algorithm](https://mathworld.wolfram.com/EuclideanAlgorithm.html) + for computing GCDs, an entirely different approach than `shellmath`'s. + +Please try `shellmath` on for size and draw your own conclusions! + +## How it works +`shellmath` splits decimal numbers into their integer and fractional parts, +performs the appropriate integer operations on the parts, and recombines the results. +(In the spirit of Bash, numerical overflow is silently ignored.) + +Because if we can get carrying, borrowing, place value, and the distributive +law right, then the sky's the limit! As they say--erm, as they ___said___ in Rome, + + Ad astra per aspera. + +## And now... +You can run your floating-point calculations directly in Bash! + +## Please see also: +[A short discussion on arbitrary precision and shellmath](https://github.com/clarity20/shellmath/wiki/Shellmath-and-arbitrary-precision-arithmetic) diff --git a/examples/shellmath/assert.sh b/examples/shellmath/assert.sh new file mode 100644 index 00000000..bc4122ea --- /dev/null +++ b/examples/shellmath/assert.sh @@ -0,0 +1,85 @@ +#!/bin/env bash +############################################################################### +# Internal test engine functions +############################################################################### + +RED='\033[0;31m' +GREEN='\033[0;32m' +NO_COLOR='\033[0m' + +function _shellmath_assert_returnCode() +{ + _shellmath_assert_functionReturn -c "$@" + return $? +} + +function _shellmath_assert_returnString() +{ + _shellmath_assert_functionReturn "$@" + return $? +} + +function _shellmath_assert_functionReturn() +{ + if [[ $# -lt 2 ]]; then + echo "USAGE: ${FUNCNAME[0]} [-c] returnStringOrCode functionName [ functionArgs ... ]" + echo " By default, asserts against the string output by the function." + echo " Use -c to assert against the numeric return code instead." + return "${__shellmath_returnCodes[FAIL]}" + fi + + if [[ "${1,,}" == '-c' ]]; then + mode=RETURN_CODE + shift + else + mode=RETURN_STRING + fi + + expectedReturn="$1" + func="$2" + shift 2 + + args=("$@") + + # Exercise the function in optimized mode; it will run faster by avoiding + # subshelling. This also suppresses dumping of function output to stdout. + __shellmath_isOptimized=${__shellmath_true} + "$func" "${args[@]}" + returnCode=$? + __shellmath_isOptimized=${__shellmath_false} + + # Fetch the return value(s) + local numReturnValues + declare -a actualReturn + _shellmath_getReturnValueCount numReturnValues + if ((numReturnValues == 1)); then + _shellmath_getReturnValue actualReturn[0] + else + # Multiple returns? Join them into one string + local _i evalString="_shellmath_getReturnValues" + for ((_i=0; _iPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>Dz930NK~#8N?cE2s zEmv7L@SPj{>|FsBE2tC|L=Y(vgrL*_ffzbmIw6z*NgzOgkWfQ_1VRb*(k~cF=nxV* zNL2v^3n+@ccTwk?-}3&4btkjiIs2S@%KV>aoxS(WD(`yN+g6!5hczuGBSwtaHrR9E zbHs=d+XSPh9Wi1P@jw6bKjrhV!w&1dgWs3B)TIvmnoR!tzyEu3_~D0_>wR=eD^p*8 z6@t3*Y0%*R{_p>mM%3f`qDa}4S*G1S)2ShpXZtp|16H9})ef2XtuJ`O@M4&CcRUip?O6Qpdg zmO6s9&|ITmeu%b3%0NF~jiGv`q0XD{{ql92L-*?6Ft2>RL-Gz?hoO6ray9B}DHGJ! z`}M#g<@#`M=sFDD%e7hMfU-*RM1d(-5XA=yE$@H+=YLNA`JexJ@{j-ckEMRs{@@S( zVDeXg^;eT4k34enBR}#ZrM|!WyT9v{^*g@fJ0{=o4c}1e2|A_y&ENdZ$shgEA5H%4 z-~O#s{*B-GjgxQx_HQo@Ys9B(-}imrH~E%t`IgDQ{_DS%hX3oo{;MQJBJThFzyDW= zk>9605&~TNkN^0OlJ{G`^;^p);@n4=q;2)d@1FbzNG$jNzwhOx@JIdVa9=$>Gw6T$mw!3=?(hEY(x1Qo`@b*W( zBm6*Xc;L71>Q@#m=mMYGR90OX8+r8$t?vE9Km0@Ixm{PkHjRfo+VC5l%7XmpO$fqGPdjK*xBSYH*Gz{sO-zvZi%+~>p0%9Wk^2>*4jU|uof%=k1Qik9|+KDh}M|+@cWhL!lK4xKp zI#L(L)^tcI7sRM&2W`HCvhriTHst%R@A|IN4~*5GdXz&?283{hX1~>!f%ID&?yFxP z{l+(fs=f#*L=e zT9wz9?~?rPDU<$&7QexFZD=o_uBpp+`OpV`t5@G4M%y)g(O%Ttv$)Ehma#N=;U1kl3 zC>3ge@W>Om2l9vbbh3u~?)g*?go(MXTsr7CxF?V6I;%|bORl+B=MPQl@m(I*Q!WFS z0mWR`Qdiw4(58Fx`JVPtf9UgD8<0NK^+pJCJ#~b(d{?LZxdstP$#-Stb6;KwLT40D z-dwMd{OU`4@)~n=Dhv9n4cFxhFY0#GkujB*{M2;T@VkFa8Tnn;j_=@mAdiG6@}*45 zB@Y^tCp^sk@D|*2-S7H7q%HlEFYVT4{SI$Z&h@%|wBbeF7QUs7`wTgqN`8*?qaXd~ z%Rt@Fy4^sz)RDYt1L`t9hsx((+E7ov2X)!H z&oze14WXuaBQez9jmBEqj4W&>or}U)@z4z4gCG3h$psf&FgfR(b0#;t+08n-%U_*l z1!%rP5dzo!mVD1ORx<)Sgu48ypsu>Up}Lc&&fCvd-wSfBe?92mpO?4)S*Jc`<*UC# zeLvUx*M`!f`PQ3)p?e#dmTfZ_x_78k=n8oF7?&96PHF2xxj7Hf{5}>b8_ZIEaIdp4 zReSZdluzDVtA7X6ekb+os_T#--;+1r>ok(MQ@+e^QM;E%{xiX{hhb%R7W2T539zg4Az} z{^U>oWb&EMd}i|TkAHmfd%yR4WqZoFt_6MC`~9eC2vVk>cNXX+#if@VY08(?V_)n8 z*~feQ;~zh{`qi&q3f0;@1m)`0*m&qLPkBkcY>^;j9^|k0!PGl#^|Nkw7D6iqEDNja zuX)XDio$BoKBRuA(|7xp-~avJKlz~_`k`)X;(Na5d&*}GtAc&239h5fJ%d!E=5fmT z9l=1)qCA)fZK+pX1U=hQ_MNl%sBQVuX2v>j&AlNgn?9r+zt!h^`s!Z2#RCkZ6nQWv zSvl00pFjEC-~HXPzy1S1@B?MHb+~;wL(q<{@;d zSITdx?lU=e4PCD5i@cJy=Cwyx^y`#UPMPfAzrU;=+wxH!iqdN21~<4tsm$Kcb*^)r za&sPHsqEX@qswm%hJijY_e)>;Qn7ZJr6cKh2ET^R{`PPGcJXGmpLAONf%?#@?g)D7 zu-9eaW2FKeju7?*Vjg(dQ%^m$SZaO!>Q}#7==t97{oc|JdVcz+e|mDe+ug3~&pqyO zkAwOp&->o@zRA_DcC}K@z1;ugCqG#{`4z8t#Uexjnw8ThJjp(2PGBkT#L*A`@DG@F<9D?%@OaYaXEAn947Y!z=R3 zC%?MzmKXemU-*SmCUtz_3tuQ6(sj<|r7wNy+T3M4=tyfKGj59LT;; zeRMq0{aPn#zVu-nqO|%+Y0(;o(<$u{M;uW)^u#AVadM4oT%%MvO9+%fAcno}b+4OT zeDTF)kU=MY{^x(bDE~FDdCk(XSH0?0ljl9}c_rUvE_0c3R80P#`l+8P0|=O+UDu!X zw5Lt}`mg_b@{DIZqvUm3D@-$Z=HKZ2xu5&F$yKg$mE!H(-?wjHp@{-f0{IC9fnjmw zGkC786_0qtBPMTq+uI5=9WNtfc!wV^dC5zPg1KjKfI#ch&-~2K6#9PkSAVq#YXtjI zk9ySPEpK_tQkR8d>}9^v2u8Yw-0*IgG$*U z@4hI|$grYMA1IeTp^8CGb$jKLFfBDN_K6%9}UNQOOKmOxlAdWru*vZ2m{_vuhANarrCPy80R2dgK zq6~w2)vI2$$f|ymTM^nU#f2w4;R&VRbl}#vzIDgTPAd(LMrRREP!NKVyn_z&94e-W zyo+?%%U*W!`q#g{oU%Ik`n1F&)CY|L8U;EnEb_$?P0~n;wi$K0uIiRRK zcLo{l+CeX2G9bdtl=I2MQ+8$QL69$DFJb-ghd+Ga&ZO{-a8aCRJ?mM;lR7|d{eiV6 zIIOqGM*r4=(C!m$SGv-bCJ%k+L(3;VqM0YV)1B^Al=QvteedMUU;c8rZtTsq(9P?Z z{(R^|A1bun>t6RN`6wbV2-mvSwTkldR15*(Wt=I!P&fP{hdd?^Ux1_*uzTuWD#xp>yv)qoAyOQ zq71SsvS1`Qrg=cGy8Gxnt(nhG%ath3H4%)xx2o)7_ZF+;*M*M!ENUcf;2 zd4pJ9d2Vx?+Y}2NMOG)UR5~7d2(SD;)!`GvDItZH2W^B;TM{97*~?y5jDo(YzxGCb z<(UWc=o^?<8E9Too35i>9|#xbBF@`5+o4-XkIJTk9Dqwg7?@CWeCxKIFfMNT*cW2Eoo zEd87Y%F0n(|N7T29$KC1zWL2>ULq5oo^I%$dSYZi8|1<#8qhf_q7CF%hdxW_mb%dn z(P^lUTxTIJyzs&jlJPhyhJ2=SpNLauz9TgAUiEo6fq)973|JsPhOnl}Q&Ywul-u_bcE4w^IP->rXa?G(psc) z4;}d6yQzXOQrLgynP(P5qc03O0~*>1z7Y4ex4mug)X#a&bBfZEE764Mj4+&1Q&Nhc zE_}led^4U9UMB_G(vGtAbx+Wkj-X$-4ny*QZgm@LUY)Vg5B<~^zZp9`vL29eGOp+Z z`IHqZImFl8DD3(tU2H@eY{I>C={`5i@dJqZY`Fiv^E zL>=aB&2KD%1ZdF72!pbcc}#0wDYRSXG>th(ORxl`-j%M}a z9f43UIw9dtO(P?u|H%vbWPsI4*zxi`?|DzjuaEM%h8Kj3Le~PZN-#LK`|f}L`xl;B z{16FQxfB}eEAU$AB2>am2)+7BNeHHSr7%+XjQ$8;;Jfk^l)*AJ5Fv@6L~tS+p{NJN zDVO|=7Wpui+R4-^a+d^AQgX)_QY?8zGZakQ=GBzS^&x~__w`X;eeqq=u5rOP21eVw ztx)(ApZG*^7@k$P9^;yRdf;8Tc`(M>S7&6wwW72Xgdl*MeIzp`@DzTtD2PUod8-vV z*4dl}p@h-kf;tH^&nL_PM6;A0@MR3%{qA=c#;TKWpa*k+Fez){MYzi%TTrq(Xg*|q z6#7{~N{d$EnDEU!*aC~|*>cc_z7Q<}WiSyv=1FKpb3mE#%6!klj8$+8M-g0Q^mhnW z7|qYoOL#$hQBXo`KE%7Dds*5S0U8N84`|WExKbDkJ{D4hl!TJ-QlNOc058yGyoHVw zjG+-OTg0j(Dsmw)wEC@I_~W;_tq{A$Tce9%7=q)s!i)Vj3Ay`gaYGIlMFUn3ywSyjmNjVLK`G+u^$Dr`?VQkHWWe^1- zWV|Up)b*fKc!VwsW~LItJc4ANW@VG2>p%MR6Rj39z<>%Bc>=~yL?W;H35I*-(#kDM z`=US>IUuMvyx|SYBVrb!L_`iLP}nG;{oKe$fG#u>X8hnOc^4rmMX845fygUPqALf%*kjW75W<@x2UBFZGP4qLMzLN`KE#o3z^7fJI3>AJD zLkTTp3H@kC59kZyONLF;0FTgY;nfx)-qie>267)cfza70EidE~5q*@5Z9&ADZ(x>C z2H^YpqCEO6uOuq7LT(J@V-Oi+f^Jcaa^a7565T|etS{gTnUL_?7K`19j4jz= z%$qBaWXZWhT z-tWi&9R9c#zSz`u!-5EAC={ad-F=B?!+1;k@*)VMg#`$Xr^8UI$HE%rSvX;XgAcBS zkgO83lv$-yhXoPy8w$pvx-LY+cp(oj?UTYMl-5CmO9&_qx-c9q@pOyE|K{ zFO+o%fX5-!@e~F^2umo0Mud=ubRdB6u=F{6pbU@9``{zG7#(y|41y`7(kJyYfV{Ww zwtDa|GzH7_Wf}-^`l&4*1zi-)R$3O#7%jp|kwh0FBeYUlbugNu457BM)PIXVj5_a2 znUxhmaRT_{^pbY>@84g>nLHV1V`}`>YwYlxOsGRXd!`QR$nRdZ_ta%NB+?>x)?$R* zoC9MaA7zVz5AiSjz;9#4o0Cbu)nO}AM8&}14cX0M4iSca;g50_T{#ZQ?b@vN$PwP4 zS-+w$ozmK8C5Rg8D{T#VcvGAE!Z+56HKlk4mI6~GVKBwX8o&WGpw~0t^-v5E6{1%=#H+TNKG_U|3C| zo;jbvcMpGf3C1Bt4PCDLPHAK0@Jr}~R#A!tAV!Ww$5)Dr2l7!Ip_jVlkLS{+q>c1H zgS!?`L~Ss-j3NF?lu;PUsPb48!IVU4tkl7F=I;M zY!S()ALL7aogh*M&nT&c-xlrEqdeY-w!$O*Cu@uh-r+sk)x&tm%Trn-VGPIygDKyT zEQH5oQGLpAFvfurl2_ZC_r33Zi(yqC<3^qt6z!rJKeI4}K1M9YrG|d!gZde1GN8X1 z*G_4LEr_0{v<8o`=%h}YZy=5r750T$1_VL`ghoxvhN!WBwtFj;T)sf$%+QH9gWyUwCA7ObE$1Vmp9Gz|532!u)*eMg4~ zL%pCaH1Q^`VYdA0X55s)Bik$bi+93yhKL6t7_KuW?|8>MiU0`3)uC=Y&MGAO!#`FT z@3f%|EZQH4@xxo~GLWt@j+Bv*;v0iP@ChIRHMVFcVCJC&*mwCQTZ6(xQ3IG?GTMZg z0!X3|QwWDe^i!MoN)8+@(QoBA27D!7IsR&%>>Bwn<_v(jc20CLj_4F#>koP4MB+4z zhdpO?kp*qcf`|>@c}w$a{O3(gA1ERET{C?VEr>E0H{+S;kOLB7=@;YMAE(s8xeO`o zL90#??sxG0t2PbA=;3t-(N4kT8|PK!^YgL>PfESgdsxq<|n2Xy=zP z2aW1Oi*^%|v37*t08nCvf`I88X7GM!B9!RI$XP~e2-=d@0O&Wb28m5ps&AUZ-ye2^wRJ7D4z-z!^95X{*@i z)edE(oRr8I>knG-U${>hco0fJ$<=4AL)aRH1!Wi|Moa2LKjr74cx%89^;tX;F6t+P zZJ~-x_|3E7gM=o$M3+xvfxe87Pcp=_a|)tN`}z24PE5h30Z+*gLz-3OEJ6WV!OAqA zp#|;XFON(fjhA~F?;&tL_?)u&>^!YdMg}o?lwy_;6AFrfF%}AhJ{T4yVO?2ILa8yt zsShFMV^Xe3`7T`Gwb3F}BtTIT9*S^Va9|KvJVK0V7){V!M+=X{YiI{Op#js?MWF}{ zFG=~e=lY_MSAPjP&wCMoS-ElgDR5M@r}(kH_dS5NPIE(SThFPT^3Ox-X`43mZrU*Dz?G)Z(|X^gRosKxs=wE0jYrcC zg4e?yxbk(h7P4tQWcgeV@JWA$c;((u+imF9v>{W|&fS~V+a9<&4E45(c;MI9(M|=t zL9?_0ZHU;k9<4d~(uH>|LFK#8 z8uBd?vazDPEGrqXJS?PeF!i_eci+>lPvE6^uAxweg)}$wxgPKc`ZHuL-5Y9~H?oJ# z;>U8p!!>6<>_WB3_#C3QwKZplzg8p|5l`&o>Ko`-=c=Xq4)*8S80$R?a>7 zR)N-24yGZ)JOp`TfQC?d;HK3#llt{~pS6OLrehc=tq1Cvr}(QDzve3iR&a(VEpfZa^iwuNDEEdf3;M$zkF>MVAvTH+hs7IaC%54PF<~$4u zQTm_M7a&Mq^O-U}X9;EMj9}*bsxYtL)7m2&wF0af8Fwr2FMs*V%R*CM=>jrJrfXTL zd1bYqez;%DsPFQ~t8P0Z_L!a3c5d4`ua#-rKenJK_@XcsL>GNiHy&%RylzGYhZfxQ zx*0352(5{eO=oSzIE-xt^SRG`?%a8R{ZmY{XX{lVfzIow`sA_Ns}1#;$Jt)6I*m{S zBR;Eo=tNt+iV5;z3OYl-`VMrl+#gbgRl(k(tr&B1E5hg)5@v_nvqEcS+X}7yI9n|E zA=X zi$YtrdF(0X&gb%i;$c0mKIq*~KR}J(iV_OK# z^rn7CR42hIFaQe7sth9t0s)nO8gh?^Wnl=7fpY*oo&t215a+-=Aefc00dCoxpg9g_ z@dfj8#Lv>Xd%{A?=Y+%7l{&Ryz%a@{*xpel#$uI7Un!qcQU=YL=$empyY>_VU z1NgSDgK1+w4cfPN>X3sl-PVM90sZ>t=&5n&D=kBn{d|T*{#hP-t-yC8_7SMxcyok<4#^507O=VH2p~?-a1r*jl`G$~t=E}T}qhCS>gKCvCAS?u2 zTk?2CjJ;jf43h{NhC8HfQ{O46EaE6bfrT|ZF+q2_#cx7FKp7W8it*lGWpjJg&{wn& z0E6#jlC~vQowuPpfN$tyl=Q*C`mL-}H5Qfhg_090bsIolnc*{#6r9po9jB=HLvg%G z(?J(!VDX5u;;F+K_O{h$@I@*@Jqjn9$q~$Dpq}ub7a_>#Vj!b<`hXvdsW$aRIiK<{ zPUzM@3LlwSHAE{OU@;!rFa{zN{Lu$8Z9Et@hJ?~kYT>lVlLCrv(99@OWHPLr{c=yX z=_gq?&Z5NZ!Pb3>QJTl=@GmlskIG7L36~?&v`SURau6_rX5cB-kaf;AgNi|fj+b=$ zL?;8@MQDyd4C5VmAqy*rG@!AHR$ZMm!5}ocuWmwz=>*(!+ zbdWMbbgu=ehoTsGN7N{bP>MHX*@axZA;F=Pgg~ENV|@uVLC8@)yjHL8`pO7-caw6w z69Ey0I0R1!86|Tmip>kSPI&OmA$+ULgi)Ot<#5r2h+##z4^PU8@|jfZyi z&!>L*R9@)Fi%<@EBdn`J>JzOHe8)@i$=H)2N(BNn39=_##WGLTU*W7g217r6mnfCKP&~@mN5+Lw5@q01c%nU@ z`phUg736V1!sfvQ?cimE6EcpHy6`Q6wG2WRB~^!cnh?p6i*>7gd zDTHgQMEXT8$d6E#LTXpvDWo>#BX=S>JQLD8Nk?{s%O0+0kSKJYSAJ(a)v26lhs?WI zr$rIDRG)JBEx}+-b%s6=D160iaeu5pjg%*}1koUwzhQy_wvwkzzg$8_b?P*z$2G#q zn&%o|coHa&E}qPubL!`Lz3)iqh8}6C{q-O;@wSXe7AAyJA_rcOK(n?4gb)){V8zi) zh}1(k&@YecfdSN4N{XkHGs13MgqR`;!l#eCg0T{*;3rztsg3X~pUXlyv@zV;VJx(3 z@kI24R_)+D!(@sS&#tUQp^cY_tu`#{MLn**lN}L^2t-IOvOzP?h|ho?ip?N8`_A)P zG$OO6T;85xQA&R^)_wWW4&#}pq-x%#&x;^NE$!rY%6CeOfZUt~6(LT@K`?8f3e+Vb z98)mE!E9bgc)Nf9{^EV2n6X0flDrPZ1?@#?37`29W>a8wo6oV-5%`2LQE*V7xh)S% zp@fwbXBq1^4`Hf4iUx#YIWTTc$?HG;=}#|;Ls-#_E$DyUa%YsZOi@%I=Wb7cN&BbX5Qwa@=LJ=r!zf9^uyd-~=+ct*p ztQMhDXFLciXOInIZFwoI!Oi=jtR(xigr zX?GQf0U(GJS05=MLr8HvePngn9G3xOcnA@o8DF!iYhF)qSbSDHiWz!&0rbo7X&hT> z_=zW0uX#Mv5eASD@f7$YGQtD%d*$}++gHlQdn_tk$!nZ=PqdgPnm3ylGRnMdcx!9P zmWna0g_tlJR|+F40r%Gfb!CwWGA{UlA7sSX7!$PHu8M5x2Zd%}0G;S#bp2NeJ0A?A zsg5^x@Y*%BC9=W)gwt#3PmFWMJD)+f5T$a$9)?bX!nB^};0ajT{_11CBiel(W!VXv zb||5D66+i*>>O|uR)|ZOEp+ifz*8G|UW6c{F-aM86BgS#EG0!@4S6%GnuL;v^@e-P z`5DL|r2ln)7DdYHm-f8ejd19%PpjA7PDofOVmz(9gautz()9r!l+*X{LBD`uz;gme zxzVbxcxdI=!jmu^Jyt#mtA1My!+%~vegce^@I(GZ`kj2{u)MVIjF9Wvwbcd-JJ_{8*D1&A|ER;}G9$G&oTVv*^ zEIrgTQ8dadkF5ntFEq2YVM{_e^`gZ+_2VnLtX4B_HtWsVL;%Wx{EL7bNQsCTQe(mp z^W=a4_6hhh2RMQCV zOW~&|joeaTQIn~FcI5%O!5~=lGWLGY(y1XR0~DUFL@%`?b))1W>n_7AC?v0Z2xVz? zs7~nsORS^j1Xhh%U;}GK&bAMtC=v$BryP%?4y$|t;b+Y41OTl>*Qw#N{?xbnFn1#@#4gAtS3Ti6ix@TF* zg~K4~WjM4&G3A#h?IogMz@RVS@-0IfdC*^{luQSBO?8tevV$Mm19NanC}I(r$!AW^ zNrVov5P3iceldPl%jLmqpXAObg(G`-6N9}hQYLg7H|>TOoziNQ6>@VL6vzPUh{`18 z5GfHtzvV-;PfRdx<7F&hS-lemTFo_o!^{whA!v!G#RSP<@Wkd|wo`1%W3>61_e$}$ z-i6G=@+=fa{S1Wz2o%KLt-Vg3Awpe4U(iF?`WAOlWN-lH!}WvKO7ZW{xfFHTIOnlqcTD7z3px>@ilpM-~8Yr25(r+*h8#cHS3Xg{}@q za1@NTMK|&#kGu>eI)%Py(YvIj87$d2ELP>F~Y8os2!Dn+X-ooNbRy?&s_@+T0^ivsL zN*{Qv@XMGP1N7swair8_!k8;d*~yJ@r2wW#=AiK^Nf2RGZW_XO?P=SBr>KfNS_@$y zML14|;fMT^XpQp9gXj8A(Oq*NpS`o%xEN1{iBU4Hwh={NyWd)*rtM(UHqf*K+_cKpv-n1UMyS7#+-_GNkcHj=8AwYBM5u0{i-n7Q8X~WmjzjnIN9DD4s%~3}k z)wG7D&~p6o#}|IIhN*iS#zhxh)Es%_k6Ck&6#JOS^Cm?xC1pW53*CsM<3*?jc}UQ zn3p$;^nvfUz3pwAHex90=%bHrT8B~IqA~=sE`Mu$n>Kps__Zv^b{hpWts^OI8DFV; z%0~#vl6YY@Yt72kmZOiBh~jO9Z>$q5%iJpCqKVGRo6i$@bekj(1_T z)vZo_4}EAc4ztESATPpmYYe6XHS|w=q8eelsR{4IV~7sOhjz6uBsJdlw~e`ei3Z4) zh{S5F5LYy|D98zfx8Wtad=8C=WIUuAWXD+f1ozRMI{NtmU+9GPGlq*II)evZH0Mnx zB1Ub(VXS}*&iZ1I$CxO}A|U(-QeFqri3r>xK%o4DF=fWyjLztV&Tg8Sf}v0DEi}F z_jw5NYZv;wcD+ANFpck^zFFSJh~H%`Hj@Gc^N zvk-cR=mTxyrB8W=`saGehaOOFUj2(;h|fcq)>gm#DrlO8@XU!rb7os}_I$1Pz@9X0 zFj=xF-Xa9mc{WR7p{8$Io9WNceb?OY3$Q-$7^_fNQ1>ilm`2-BGLTY@c|0dvb-B8~ z6h6W@tB!e?S6-Wdr{Q@&=|@t2*NU+o`e4!W)7t7^o0e}?zyk;$!keJ`#=;3>5=w?? z^I-5L<(2W7-_wvXv!MK}I{HZa!92z;!+7H68-S$YTT9exH>- zLZVWe=~fA?+Gg?$I!ItFO2})!jCa?5A?5%x)NSQaUUdvDa@0VB4rT>A2t$)VU9Z`r&>~9~T3kyVi?mk*t=cq~vCd$x5A4xeLA6k5fz44y z^=k|Nt-@N&F@AVR$-PMdUmTG{hXo%>k*N?1t}grYR-)HJ?>wMIJ61#O?K*Ulg}y$I z>?6Oi#5A4@l z^){aJg1mTV+(Efa!B)w$@XNst{qUrbIjvQFxRje3C1^SwA(Uo8M<}bU1OuTV6J_Pp zVH*IGZF1ZAm*48F{N7MOX(_gY4;Y0x2?5-*?L!dv?%iAN5jF$D0$Vf#Ovfx6-nMxN zp3^9xJ*&qi^XS89#=(IKZxx_`i!fTdnCG;N{$n2UF0C2fjOltXycF zhgz5j1Vy#7&(L|`+<_8@5gcZ4e%LmNw(-jOZf!aHnsIQn(ZL1VTKJ28pUUWm_V6TO z_(mYIGcek(;ht?W2Twd*|HoyIb?NM8NU|LJk^3>ZD$RlVw0H<^g6m{u5LhTEf$oQ=LvRC+0-XIv8EKzF)Wdzj!nayqVUjFZ48hO%@N(V>V41H}?5Y2P&=WsW!UI;^gG(Lw2RTyGHw zn;gM(Bn=Z;cLvL2c04v^2K{rs)qxJ-AFn`o9Dk!|4)!xPgf?`}0>R(`@ewZw8^M&! zcReW2=sDs>uy`b%U4FubzuIzSl49^0p|ub74~_LIq0fw%P@F*)H3-@9lHul6^#fnf ziJpX^6pJ$v3Hppoh#JTv?+LzBBD``KLr94)#@%=0XWX^T$a5(8hqk)^@;J8~e(|t8 zHRnP(89B6ZSi){&ALG3W=tt;5Gw6c^dvXz>h{TX6UmvcqiaNleop#!3B?Km5%4kfI zkFXdZil%Og;=s1SAzYNfgW4Y960=iYLdfEwA;KMNi#FES?|u9B6~S~r_gGm2=9=)x z6FoefbEGUPFDx-K9%$D7G9XMCN$?1Z`J{m&APf^jK@cdn`Wc}7>wf%j!iw_7TMY3R zv>~aNa5#UeZ=j6+iD(!Niir<|87-zH?h{H-2jd|R1Df%Xr%uyoStZhEN+LWBO&(+r zp%8q)XGR$9Je)G34$3NQ#=rDIpN$0tlF#^)HSf}r*TW={2{MJR= zH=wMgd!a*H23aT{x)*^qW85OfSGNqjRt3t*G(gvViI!I|}4Itv>!5jiBH1r#>?wQ{T0fo)xjf4bK41^Us z-+2Pw62hdkDSR^^R`@6%p#;{j1}iB<(Vj&ngW}ODg1`!=P4gVrF_$4>R0yy3Fal=v zcN%y(eGn0-JN;*<2o|pZu1o3{=6cJAvQ{K{0JO{kp;8wfTZjR?kD=h%2`GWiTu>CC z41NRtGEn%=sJKp1D2eeA-IyOI|3(Ad=n;WYF#WixfrChm?BScbm6x=!YN(f7 z8(;KBrVcU>v0~Gpk_ZhCrc<$A!UfE*>PvYs&8HChg)e+zSu6sIRRb~V@mQJpC*dMc z28pM@STGnYr0YUW){i2v*xDuJQ6!#PSx}z`?!9isz9&JIPa^c`&mxc)D4%(KA`0cr z&-8^-q(1$MVPkA89#O25PCBVr?9|ss42uC{AP8R{9@fAM9$a7LHzou;!l-?G)27u| z2GxIx_x@<}bcUA@+RNPx)Ura!gvQ_j6^6~5Nc0QM_-PRjFYuM&VO%J#IwWK6(>Tb7 zpZKjE@~8hEPvm4=i_qh#s05!9tqmcviLV}{@zxY{l4lEhre2~x%B%iWgFb3MbOd|c z&~cxbpi@JT2Vn%s9LKMdwjv}$Vgj`z| zo&e$MRE}a=^fACfATS8RbN76w%oJ9gEG5fM*?l(;7ebj!6P#(vGYu>`r4^Qip+Z?! zU%LdWw%+=t4N7K#gpv`+Spox$kBGtIQlLFB|5qQ6L|`SJhrko=%!B2(z{8jt3;bmK ztkx4ciehfI83?b0?7s7?rZU>M3Qk7I4&E_FjCW*=JTP?l8-q*nIRVMI^UQeVtO3O$ z%j5*F@jrsk$WvT0VIe6BKMTf!;b;8t-ikh+qTMu%;Zq0sTrZ*C~jEndZSm8KyILm|A)-)gDFCZPl-vZ#b4(8(w+Adpo=hp>-u3j+Z? z>Xr<)fwu^tULm!GFJFvW>^%;%51G*i-4CW{FF|4v}x0PmY|uN z<0Be|gdsvE58nA40;5IY`cnkP%66LQBrC3xu@N5YU&^>nNYO`l8BMF|gqh%(V-m>C zgh+)_Qa*~t*zvyJS7i%_vA4!Sz8D`e!Z=wFVr*;`*$!iLjj?_)R1{f$b&xad@TL?E zKO(2bjM5q#^He;FF`R}dq_I-g+d*srF@)&1!0LTx^l4Qf{~22}2c2djij_`yF-?#V zD6F#AYOfR1l~JiW2qPwAtWW|oUDHtv$0U|e{um4l(;h2IPrxQ1VZg`zPMjFLJe-zkIgJP0U*XBl6DfL6cJ z?Q>S&<<+*&RUo_~KcWcGC$tOMy&}kziUN`~Mpzj#VGQ@~-8(t;)KklrpEZk2)9}NX zFi^G<8EI=P>at~Htn@X!CLhWsl3N5BEA9D2pE0+E>BWJjYqsJ*TWjT6^uoBPBMBc| z+mp(purkkr`>Z*uLSZpkh?-C_bqH}O5Fw$62r*Esqt#S%0t*qW7dYvI(G<(vHj7pS z9K*rD@YX_F9S{!MUZH^YZiOh6SI4~jN83C;?-xcGSc(enqjwqL7utAwo|?A^D5Ciy zBc-1PQ3z(SOg>Qo4<{0#a0Y(Xz#0>MBfy!b>WdKD;SS0eA&lY?e!`T+EB6_agp~3{ zd1F*kM-TxqHjED=tzCUzFVbHMCrZGpg!01n?AHq2$qyc*gE10iQ4+FaUBI@CMM?c7 zW5!o~6p@nSg}jV|#Us-veU;RgwTVR(Hwue?_<%p!!$WkFH+6ckLh`%67KG3GVtk^T zp`q;4svI+gZHo+yiU=s>6Dpdd3vC?z652RQMnG*n2*2c4KP9t3Kya=63R@*}0`pBm zsPil;nlP1;nLB`i5ppvS6xW;vqbR1WB<&DZtE0A~)GJgJ$}kH0hUw_UbOwUA%Rnvy z{lGIq$D2{glsAA5ns~_?rLx*+uCGswo6ww)5hR9V+Q3F>+|wsQYcWcD#~*)u;iI`! z=Cgrvcqe7?N*#n3&+HjH&&>N6Gak$0l!GW)Kuf#&L&=4ighw6dT`wYd##S^&AQJ-X zYYe;V=ZvmT z13_4YF&a;Oyd_1a^vW9$*M*$6eRx@ek~Y-KLbCP-%{|tiVmi@6k$Dn>$1)3t39U_h zgKE)?wa%Gm$J>NW_J%1?!mCALpyU@aI=~?m=NVE5CR0fL!yCe$-%jOd)5@s6Ol#M5 z2@maC3#s+j{EaXWLJjn@ctRn}jqw!U@dA`l2i{X` zp`Z3*WYXSRplk%65wZ|Pe$eV3z8Fumn8x6rGXa#;xGBrH$Jpuv`EV_fNAjm1**-E| zV&p9v;uC{TVHpC4CZ_exI9S6G;TR9?iRi3dmMP2f`hZYPa;OeI|tSQ>uH&6uKpfV5?-L++qKA?jIW*qwbLN~*q?>vV^ z6`tQGVO*rm)a8>k*AJ0_$izQ5<|*nn^PK|9OMnSK-s>|%nFlyLSRwHW>LL{Qg{MF; z2r{8$=*-z-cr%u(K#VK}i-7^W)dz}Z@sNB;432xICg_phG>jLtwjlZ-QyJIr22Uui zsEv&Ju51h^UXsD&??an-gC2`sq8JgK=mc-$aICLr8>ZE@2pAglF|6k2>Zd2@P;d8Q zH(rsa6>3_QbaR;j(om`J0I{l-X4O%L5On|(LF~T1)ZxBG1l9&q2(3B|5TS}9YTI?f z&6@Hg&i@id*VSWQiRnB$C9|KoO#k}O7K6fz8uUKWe| zf;{-QFNxCnFvSoZQCyAzOs||-&opZm@K0Oj_B=E`(ig^svtfA9yb2zn6~9dj?U2w( zi@1yobV_T^#cEjDU6s;8eh)#Jpe|bjgUpRtM~ngG`n08ep^U@>Q>I#gVF$qH%9`sjfIh7az*^~_2JH|~7*aOMWRE`)6B^M_(?N-O zV04OV2>TH9IXtPyEB#%hZ~gIBF4vS_WNa2el-5+!{GQU=ZoM)8K) z94ha(>x&@eLzmCgw`?D0A!E&OYEv{JJl9^~L%G{R#tF)Odwp#l>U?#% zd@jmV^rOxEHDOm>-wF`?KBK2-ca4sFht8V{>oR6L!DL)b;iqrJ{H5#-YBgJgD}Ptzm>FP z3S^@Qo`bYZ&H}RpLL2umPU6l@^xe1goiG(NZoxpkwFO6*Ywl;kB%zLL^Soo)w~}ae z&r-RzEjU@NwSbf7XH!?or0oN2==$usg$r})ykBV+QeK^aZgVof6WaBWwo_&v1(WVaNBlB5-Uj?kJJOALp8*^IWGIUDo(1QW617v%pW=x1RfDR`3 z*NWyD>7>fCRil%bVt+JS3pypA`fcC%Z951vq8L_5^IctpD+U1bF+?3h0~STM+V-R; zJ*k|pMFT~(-Gc@z$ys$&524bg!Bwa25w!d6KI38`#zKhycj{jHkhuoPZvb=p3$5~5 z93g!2S@ESPf%{;gj6um@%mZ2(Oxq1SkrQLKI3)d5FCnsr==CV_>xcT$pgr3Mwt2Gp zIt}VF-VRrIpx;7JExh_o!JOeXZnl{4z@nkP<1PME0EW{^F){;AGRd=d@7_X>`qWLv z&`wCzWt&4AzN^c1TZ#Iree_X&1_AFwpX-jc`e$2Kc3sC83p!}9M~^0+oS~P8Y>-Pd z;G5NIa!wc2tvzyP=feIqgF>mCcUPB%QarG=W{xfYS`q#-ZXTp?T-QQzj9xc$myuw) zfp9m&1m7tr0n}+vJJ>{ahyru$u_0C+gqpBA)UWfbB!y6?!F70>g`!C6cl_|xuYPq| z{?Cy=LWUMhw7}pL5bq!B-3R(4f{1}S$TIP|gv#k3%H-U!rE_@*GbMMbOId?o)I3aA~*!b?y#LS)}i&KL^H_oU3ABDOc#MnjP$u%haM(w5 zqDuep%qIoI<9J^N9KZEN8T8re;XTzIWuc%H0Y4dEN=?})$m1XX`0|LQK0Cy~2=3dr zugDs2q#k_517+O98>i36v^FT5c5MfuMIPr9jJvuS3H_3np>W3C-a466ukpA2=xH|F zaFkiy+9mh;zzC6X*Vl^3xiQBtd{H-=r7lJ?*ci*2OaqICk-V*qdV5F&^$vHqL+La| zdvO~jp~&_yDN8_5c}~_T!iedVU)?OB4m)My+^$ev1i(;W=8(byhPqD}Svf-EFoS`1 z7L;NVblX{8(-L8Hq|X2wAQ1+IV7b-Da1cDqqO^ohltA(IA2UHek^`V5zl> zC;EoJj$k_0$OE9m-lVeApl9n_Q&>zcL}&ixr~1{UU47fXe}7RLbh$>E$N~d^ccGg{HI9@|I~3j6h^ELJ<KHrBKrpqlP?~K{!<^ zT+lhh>Kwu`kP*P7z%^yG6;KWgrmzH=2j+nYG>nfR-?jnaVNg7_ zrGEcol=85&u2CqA_Z={d=f*I7^XW7VBjlW^Q#ksjjT}>?@a9cG*wL;(goXDPW-~<2 zLW>@RjLOafh1Vu4&WNGQSUDZ#-~sRH^b+VVWkY`qOZaWvA^@{M;Eb<2oH{d2sK+tD zZ(+BCISh4jw;rIKFbZe2iIxas z_>F&P;f>`XXc@h#RN*@nsymVG%<+yKv~Eco_mT14_rs5 z=^A5J(@kl$K@reS<{8u+GgXHvg!UY(mCqE5(vT;HiwrRo##p|A^9mS zYs}N4Ddo*)gjm`oARde`Xc(kjmS10Z$~<1B9(_?4sNcZ*6qcEb3$4?xP?ts4XFSq2 zJ`f;BD8cXavF76}$fpg17)4jN?+lAM8D2VIFH9u3Xy?h+1H563j8_D`4_;k9;VzmI zt6MOh=JVa{xY|bz0!z+d|G^cH{ zhiAr&y!mbFmFWyQbk16N{YD3)A}<-I@aS{DhC0bqzOPqtQ(j%+N!@1mU-MW<$nSZ` zU7n2D$;DXXb2%n{bQCao3bDiS4)LiujqV*x>ES}&>qx(qrm zO2H^3%Ss^(s(L_v!bXuee~6q= zHp#jO<*-=jjJY-`y*mA-sQ&e;H4~2zS^%{2OJ*n|S&~G17M#HUJ<>=0Cr@>V%)^Te zj01URw2iO(c;>jKDUx-7^qr?At7xJ;%Ho6eDX~=ZOY(g^NO^js?z9(l>g#6xIS5cpTVUhI!%Cifp}U>HwY&A=4hVO@Hn5r@ybz)0n31*v5*Xg5R11l z-xAL9t`?urY(QgV0<@X83iZ7^*#H|jR#sp1*>AAg%scbmJcYjK2Vuwq=ztd#o$yeq zSmZ@Ppa_C_I6-DWQU`jp%ZNCii_iGQ`w?&gw|DQ}@=jiJY6d1_G!N=lUp%RH36HkX zl<{|;0_vN2rNuE@B^KbG{p@EK8swF1=TTtODQ|eNs3Y_i_WJEQeo;6Pi!}g?c^2Mm z_gE#j!b^_Sr(J6dgxnOtDzZ9wOZ>n$o}DZJK2cP3Fro}911_KT>^O*8EJ`wX7QQT; z8B;6q@;mH7u8cX}GAI^2U-Y6E6lJ9xcCBoSBI6t7jnXD{|22=28KhXHMS&44 zfA8~bRQj*Mp_0Gkw@W`47#U8cyWfn zQy>)6!ii^cg-_Z;FIq5()u#9sOne%wcu)B#Eu*6ygQNeRwWQ>^M}Xy3AAw-u84?dm z*MdI}se_O*VrcbjEuqVNM?J2iQ@Dq(XyUQtkm04VBGKo7;s~Qe!_3giDuD_$OeDpH3mGcd-^WFa+K74G)V68knT}B3w0Ea zw>KW-lEK9*Nq-n@o>R#0H`>)fIUS<07%H!YQRBu~tD8LIDLTm$`tTpk!gj{W;-+V7 zwV74lwZM1+eru;*L6-Jp;0YHi-1k27pqvir7-kSii6VmKbe%HEXPq#MAecKM3NVs& zwb#g+O5U=NXI__uP7@H!<2iY3Rxw*0Rw2>IyE8WWj}Jhp2@6k57<`ZLD{FvRdxK>` zf<;x{wuX5aA=bC7+^UzL5zHv9{EI@HKq*;u-qQAsv2suS5&_n4hSMhj7p_N{^qYR+11sowTlbr%#a~-Z45JX&A`&kj;~<0$4HPqq2t2Pk$R*yOgK-xv3G>n8J4LpQ z>609jVOyTOx_(%o(iYz9kF7yETBdN!4sauGW60a|c45CgOROLZk7${sc zkgS*0Sz(%FK4=du7>toRVSwgA9!5ksiupn$N=1k$U;2qA^mxEp z1dv4!_gFT`KuS!mn%GJrAY_?*6xALA?^#i`+%6d@FO8Ba=O zOq^~q-a=Bp@zX0|Lv3cD!{2chn)VepiGGfc)@I}DgPzOgX|_+;$S zoc&^Q%1DS%Or?yy6?unVL`>>1_6z`-M7w@+Mx2cZjx0G;<^jz9sL>b|k|1jT^~^C>IF<~d1(Us&Q(KkB))`K7@zU&8}}B@ASEgnX1% zcpVTR9>+aOt>59@A_(0Cmv9&-`^#Q_CNwvf^j^l|gXbcS_4(Xj>j*ZagWYz8QN8ji;t5c4N>VDJgIkQB3A^Qk<`m;smu^_h1V2v(dj>1@D207VHH$@|)jN3XCb;k^MCw(1}6%nNHrpV2IYw#r*? z%+KR-4RtU`Sudav1VoA_V2s??uD+SyQ3m4zi?*lV+LIUrMwVeU4!n|a&HgQ~js|i- zXn8_P%ga$bCz#O6KnicsnNS@)5}u-&!t0}L9pSJ8C}fA>WQZ9Mbr>&G8^6&={x|~; z0AEc}4@kOtD!g_K>aVaOhk`GELT2cmowVjh`JU>=WagnmM;ZuOv1 zTgJ_S5!XPy`bdxn8zIj1W!eN|fTt9daN-j|W*8}y`U!EOi%cBL z|9~ES!y~-INAjW_0?Q}@rPFVrGtbJ9h^8p3F>*a)jlZS`A_Q`1k&66i$9>9f&hHw! z7&tsaTc8i>)HhL#wmCH8Ew2bcbf{0{LEYrd+KjS(FUxB%ZpxW*ng)p+@QY5tVQq#+ z2JE|A0V9Q1ee6 z1Ra0WGmp26Annwsg#@1oEx|Mf^}R)*4MNXA5h6SiR^k(75}DyI?`6D2D4CKNYyCFv z6qFGrE7k-kBZF2$Tk_(KPy9tUMGU_pTZzheL_F1&6l7c(95hF1fdO$}el$>8oGhPH%sJSv3+VV1dvItXJ@XvyzI!8-Gp z^Ar{c2%5CBXur`ily{cmEJObyK)3Nqn%3?r5dQa5crb0O#Gpi}b(AjqIAQwyT<1~R`FMe17y z{ehSTw4iGlEJOcRL-^lM^Y|rCU@ZM6W4y_- zx*iK9O{jGgk&6oNs!+FyxJ9fMTBjKqb*XbLz?fO>*X@RpYk_*!E4YUm8LKD|zL=Bs z6(&5B;!Sii`$Cv@A&guL)YC7E=4nDagp8F`3;C?1Pr=Yw5A}H(GFBF* z9OO`630PK@yf3Xj%xfZjm2%5h}NFspC3=U=x3T0R} z+gF6pRzj3Fz&zVSRtrJ+g0WUdmub@$f^9b2OF7&=4Z`g5@4(JK9$RMQ6Kh+ z9WQkF!IqBk)E@fx?%iA7F5&uC1{os{7KR=0aH7yDt#0+W)#8R=I_t{{dYy@7`v~!7 zf#PTnq0V#Kz-LJ6ts-D`42UzS1_+(@TAh}$B}8Zp5D+2;fHCrvi@L&)8U!T5NI*U0 z;D{rh*hhByMw@6PNVa=eZROBxncuR!BW?AT!4R~sFNB?N@dWnuY7qC_KlhCK!#aA@D0C>EgqwTvfFPB@X7X!KG+TM=$~@-biH+g!LXO@UYx;Z%J`y) zJmR~v)C^6%dVDDv0Fw<&lw-4j4v9cvve3zZAzq>|z+zKo0%~uW0@))L4zc789#8_C z-j2CgJfw4hHR?B@y51vwB!X9s> zPoXtJ>o?^lD2_%t-GoPk+o2i!reKVjeSCE}>12!;tGsMYpU9FuU1MoaT7S{cILPOu zn06UjN-pB?{4G9sjFO^xBFuiU@iQ*^$Kwipoka3oTgC#^ryYG@bQy0(M*s2Am^$3T zpd=#Ls?ZPMT%_nm(7Bvg+eN~(jf6p=w1k0x@{k4w(S*pS0|Tz-aW#rd$ysMd=$yq> z)@HuYljU{DgK+Rr-sNM^Fh@SZPuUHu`Y4PoDFcir&b|^ol{m0-Q+n|p*B9NTic=oN{=3Gr4Qh{x_)3`!+ zC3A$_I8-1w3ELdO6GBB{b$}34Fv39a2@S6*?8zLIRpym^3ZW>N`HT>a^(MeP3c&)3 zOj!a#VsKq&l?kcSFxu4yr6nA^hZLc%1!x}hPg!$w3D8b?36*{ma&_vvPc)#3GB6;7 zn@~HTf@Wc}e`P76AlQ<1fp}PXDK&4zXelG+9A$eVkl~#Fyu<9#;M2mJ=V>|)M46YkT2|pMhAvAhp0Fy6D z# z(;#zePcvEPaP~PKRD088mGXO2iRY+2h;Q!CusT`95M^$-)uE4g9e$_3ozm8WaVTIQ z&_I9M!VkqydCeKPCSa#MJ2$SCwT3BgrWRnLxhp%VX+Ai;n1e*(xS)!Pz(k`0^X3pU~HV@CR~Is3vN8BP@2;E ztq%N<4{vy|8rq@M42`IT((0QOg$msw3Py~Aqf6cDG|!Yz|HFfnF%IYzf~()Oh2jgZ zjg2Q%EL2%UQ^r)q6oxS}XLa73GE!I(lXU`KP>o(zo!j z5B)S9(hizM4i^0ueP!*T1fwJc#(-gTO<(o7<p-gYVkpZF%3kse*CRKl$*4pwT4SAl|{K_?o;#0`XW7xXj z5zw;?pU@z`y7Wy{zzdlJG6vcd5eOHB!#oy+m50zWPCQ~3c`U@CiNaejb1i(*7KQiF zyiXp8C!}~nPl+O_OFh~%7bYZD+?BKHDzaceZ1Z?AI)i9V&45@qV+_nojf3dMnBb*p z4&x+*Hx@i5-fB<8VzG;HqGXh<1{(B(aS(MeWUdLR^_P4YN8=epz!_TxgNKzTJeRft z&?5!;j-MV;71EP^#?e{@gO@q{;q64fJWp#d^B{2MgJEJS+^7zRp@hc6@B~Cy#`94Q z$|r<0XhJ0xlhRsTGAIOOmLF z%wQSdObNh!<46e9gD3c;uX*GSKk$eW*v4VJ^2i}RQFh}%;dp39M*DcBemtU}ysSe2 zR`$Kk!@fDAh+ciBxQsIItS^+8qd*H@7()uEKV$;0!Efyt7i$#ZoBkMA2F(2$gx|GH zGt|}3vuZ?+r=hP$eoOdk9Oxi}Xa%3VF?RY$7AS3u)8Xwzy(q2B28MPEvsgDw(%Cdt z3um8z^?=Z*hqWdc!Y~3TeA6L9lm!gJVDN1Z5O({k6o+RPUZI{tjKHJ7(H(Ru@Z00DIkGmY&Z+X1JseS6jyybG=oAe zjkEE>i$opD7%P#9m14593K^XTbfU4Qx!-QC`xL2|2I;@Pi>5?1Hm}00|=q z72yT65?XcX1EE*mi6$PLQ0Tb(l#=)2A@rFbP#&HJgzDx^gx9Jw8e{nh2v1K4SwJ2S z`hzqJF;oPeXC-6|k3qEfhE8Mk4k5jBfaz-}<2s#z3@U zjI&rrS;-#b>=m>kNIb?v#@g7sr!UYcEelBVEsL|T=31;XYes>&Z;aA-lo&CDX1$7= z&JjFx5K>3#2noRuq8UgX;DxO$V~Pb0mOmZ`Z5Sd1BS<`iO??7q;CV-N2*+43gKovr zd`Vk`nWwXT#QLkx0tn^T*Ys1L2_7L;R#=xlEQ%OGd^La+(1H=qF4RQ_`U$kYn!htJ z?&+hl6vyB~q7h2X^H8QJio7BYiinSdL0$_}#+-0VXcD?o0(9Z8lS+()alkjx2!+d< z1MvL#g}25}C{77QN}?k5k{#c*?PL`?7(Ddrw{-{JS{;mW3j~|6PuO^O*=ph`qbZczInk5UeL4( zT-s~}sA)UWw4H9+c}{cOamO{S0yeFJHSIt(r=50M(^|GtulieNHmw&e{n3Xu+-puh z{q*LmU;S#KK^xj@1)^y~lcrUe(!W-i%l$rDp>JBnYTCg$aAi59&#jO*x4-@EoAb^) zuUt<*wcjdWxds_0eb8Uy(Hfzq6_5itnx-|fWelJ9yytcO2K{>HJKtIQ-FnrgH3Uto zga>_ZjbGC$N7H(`Qbxbu_{KLjtpR9SqtvuUp=p)0X(OAaRhDjSw9(GHn&Xc@zVKB3 z)=)OBpmj3S8r7zacnZ(d+X`$M>zY3NZw*V+Mnui^uYdifHHL*2{b(IWbEiAqsc8ke z&?}**4Y8Znh?P2OwBEOAr+ZCnhzqTLhu<54)~OX*&_({*D5z--U-{k|<07-I5o%gz z*xc@Rw`I2GduuGd zeNy|PEb)3_0m;8t;2F(%`rr+C3(NPyVE>MRMdPW27cJ9@XSHyY(@||7yNDprANTZc z8Sl6e&}XZ&yr@tSS^+NeK4EK(R`5zc^w%DyxoOTogNO+Kgp9n5zN<@Lg`W6gO+XYQ z#E+Nd9pn{)HvB5Mg91VW@XG#eY&sxem&}t*S&gNTyxLwv{RRz zHbyD45r~|aMtHNRX@N0Gn4WgSuk^<~Dc5G9ue4da>3WWl6a}+=uPfGAh;WU9noIa@ zeqzo8Vdz?+EdtnASkOM&417Av5>rs0Xp)pg*EB?7A$_ZnKIe0zp^daxqfb{LuRi3& zSnZAR#vG2v?1Kj|Z}h1S^`~w1;p33PLTHh18gJ!EQYVCm=&GX{*M{nGUmNZxFNDUl z?{}T1p{`@q2ptwgE%NcE@#^}EpOQSG+r9jrMR&l9TZ@Px>lFUL{WAE%EDa4I4N3?Q z?y&M!8WGLP4-LdBF*FEaUherE$`{o4X64&V=)de(z@JnJY{ z)6G?_fTJxDMV*!f9n2?Z%7Zh;sjE}kWSrNS>k$}B?e_K@h!7^|$cW9swDFmRwUilg zN#LU`CHt%voji!ZBj(@&uqQ(8Y*2?czgTiGDiu)=JVw6@iD>|aE=C{$UPH8EuC1ndaiz*(MZEME-)>|QL zunUvtDgB5M+YwP%Tb;If>|U75iZ(X_=Cs<-Zfz7grG+S3Jn1~B%&8VnX*iihu_$sr+hTPHVGW#zkGdPH^8g`JM3< z-`CGjj*2^CCm?d{7^jHR;*-NWo*-Jqp`t#bGj;()zWv3mgFJ1psIpO5Ft>fc10GNm zm#6iRvdv~EX-35zv7Hh5wO-@Av$vhtllAT~&*?(upsPakD(4S!C}-5O!ah!I-_kw>~9IX~^SI3A(% z(gX5s#TYhHVIgx`i&1E~M0ji^7!XalmQ|)GuDvBn>;K$3E9pH>C!BCXdBn)MSPNFp zqe2)n)Yh`?j~KC5khLW82o_UCjL&$+GbYb}{`1S*I4G>sNkwrsSYd%4(v?nG+cu@{ zd0J2IcqY)3CZ6d_#$})$Whk(x8@yG*B-&FR4pW=w%9H(I;ds1n+LNNpAm4})8wK(g zS&Qrmhdod3-*Jc#AMuDsl=T|tw&mF@nRG(Y!k2e~*`>+x)UI`tU8~13CYD|gFhdyO z#o(0HL%!+#Z%IrxYXi-J;;q1suH zcfUOy<;^AjQ-g&jui~LJ-u6+S{oO2N)i^p4z3d*%^PK9dU;XOIgCG3h@+y;65Qq_! zNa4iES zA6o13=$3rX^kKx-fpM$HsUG7XV?6{Rux%V~F!8)`jwM=bvXJVTZS_KZ_IInb=5%k5WQ*B z{j?q9sE@9tueH*J4>h0UTQBXvgL?NA=irb(S@TI@6IptoUIa*CeUE~K?n40!P`tba zFeeY4yh2N-w3f8Jg~E%SDebBe!GwBXFvV|H&mj74#mrp79Gf@BWG_GSP=XL%JyDiC zf2$sQS`^%GM=~k5J!IDi2N)19Yh!^eiZK$B_PvpTF|hKj9dvlg!gWT&0IN$L*QX6; zu1C=Qu7$q-J*|#aq2_xH{HST7I73Pph3liP(^yEZ$(uI&pU|gg=y#3!9-7g^=-W4? zh{lRXV({HFMy?rK+l}%WM~X{U$b-451tsrZvUS7g=V=;at4;FJr){&LeOsvBqG>9L zX6Tew?C!;9_FBDYY9mn(OqddjTi-JXS@t#%=Da>Bw1IP9G9Z-9AR0VnSgDjHQ0kSm zV}J~f@9GhfQ(8$L{Gj;mYtuC^ZzGUAx_aed7=-WYlgGS=p`hT(%ZDEIG7Q=w81ibD zHD?4ukN)cq#4u#>AqbypVT#hI7b0-!A3;X9>*!WCd293qR33e>=D>jZj`tJ-&%=45aVsq;#UN(5h}_LHowmZ%G~MH%@r$TD}`2ifO!Z zSOI^dq-fP1dTOJy72sdkILu)oEQdKdrS&c!Z{PE-n~hf5A!J~JFwJ!X8-`|(U5~I5 z7{WtQeK&YM<&)$ApM;Jgs>|b4291y@laSq@QaWwAMmPzZyxJkO9+yf;9|Iw2S2^@a zc&jc#6yxA}+JgvD`X;|?@=nh)hU6ppw6uX@7%V=V@}H%z+o+u8-(v`=d(7D zy|F(FQCpz{8H}19f?-hd9rPi21Nk#30U?pVKpJrQlrxw<`-4e1eah>*@&r!kMX>#L zox;{ij7CC6SP6_eP)?_)3(OT2# z?F+7XUF_bydrKSgc`J$kGU#l%tw73(XWCXD;0b!Qp)D!>axeV~ua%SVK^|?PIpxvB zgSw_Y21fmUQ$BUbE7d%4UkZO}$mbq}PmnTO8J^Yk`XGD6qBuCL9jpcetQnfwc*8N4ieaoBP7aOMWwLHkPV@wdWN9x(B(5@ zG6ZxckNiRE=$F+u^gzniP)FJwDi7h!c7&}buXS>CGM;u%m=^)i@ge4HCDeo9d*}`A z^|fh8{u;CL524PV?>_O7H;EU_^*$&;!Zr#fY~&qz7RemOUblra&n&aF79(79&)XQy z0nG)4ki3$V{}dqPRj0amDc9AL{{!n)x$4rs`tsiy%wN46+>6gmm$ai#;QmmmF?2sE zSmt{Dy${#xv98BIX*+{_7(?>Z_36O9Vs&HDW-)1}U}!Lx>30|?fBk(L>h|X4?IRwk z|G6H-So;T0ye!3J9E7%BW?*5`(LMj=0KE)MJbFKcZ~5N8U#IkYDBsXE$oG`pEf8g8 z-6Q=kJStaO$dG|d8nHG+_!h;GK-P7{%lVXYul|I(Oi=&Mcb}Bdc-s%>St%@wZvWES zhJ_#t;KJw4hZ267Y8em-Qb91Jb@U$#$Htb~>m3L>u+XVd&TxV~E$=-kDG?f3wH)88R z#CyC-@r?i&) zd00vKG|p*vCUQDTXzUoK|HCA@vRdr89EF~C@@d58A_pt_bi)&*&V(Y;R0nBNQ zUDBS+a|m)g&rU!(sDF7v!)jPKQ&zE<<``zXJQQE@GtpZ~T zA`Jhw#ak&yY;8n-`de{(qQwr1`@R#PKR~HGX0t zjRG%=@Wou&TMGRXZN~sD@bIugZm)S!|Ei3`h}{V?M4qC&eiykqaK9|jM9^f|u1bq; z{jh!IP`uF9yOD$#>64d~>8E_lKzWL9Kl@$pdROtZ3BAE9Tl}*mFW}m){2Q@#z{7Gd zS&WMEc1kNe&cTZ9jC73Gqp&*b6`=Ma<7q9xP}*e_HwvphFYorB)EI>rI2O?sisOLh z%dU?Yu~tM6om7kL1)b8mNr+`g480ve3H9NNU;JX(-tgwZ8{Y7S2k!pgeQzq%M~nz_ zWT%y0UhQ~o3*NatV#Hbz*>n=Beqez7cADwNp;lCc zuGd59gOHV%b+E$ID_$E~dsN{vrPAklp0-xl7rp33+e#WSVwG^lfSgK^?>!OR{ELf20ZL`1_fwzx zRH0+7p!61r{5uQbtCw+G9AcDWoG6$VdQ;k2o)&W?Zzz1lD_$`<>#Va1<0z_sR7P=Q z;Ou$w=JlcO5hGRsI!Q(o!O_+3L=>TRfIcrr-b~m!5q5gw#6NuU?=2||%d#k9g}ge& z-mKTCc#0+7J5T->OEt%v%Y!){i+bfNUs?WJJ?5#)0|#D&CTy{A2Uoh%m5NtaM%fV~ zRt0h^0`@8>@>k?JiV*?aY*@~=mnx*?9UV;I-58Q(LS=a+=ZvlDT7dFeP+*YEV<{Eo zV-)mTa;-)b)`Q0s_LHXH)IUM;ra}i4@)Bun3X4rIln%1|G6U_8tlmsJ4eJrwZKkAAdRCy$(25m%Qm);tz;+R?*I3u;7RZ5R2c zQ|F$0ZuxFr;W4%oPdu@#JX6#h;^uXoWfnHaaEutSUc{l0CkmVG*mCVao+6k$2=^P| z-Dn_;LOHyjg(6t+dF+M^5}lLP+@D*u;t z5wDdxgx2qS-}_2vEi7gTKwl_r{X~oZSA!9$KMe7(yk~Fy>qmJgy1my2J?KH@A1T?|4!P$PC1BAX69Mec&`8zGKMmT9vT7p%TUX}t!4_3wym*wEyyM=Z-}K_+_rV-sf^- zcxr4W|3_?Ha11$MLB>jou<*2Q`rItU%L^}s=e}Fup)@=$+Gcqh_jH1Xx$o-aMZJkIA+k2T(c;A~esOu}nS2(q?s&&LmKA6WgYe2Ky9F)nn=3D~)uT*O zUr}9StjxK+m-sWE`OHCQn@8*(kOeS3>+9s6SagCN;oK}x4#;x0IkhmB!cyFMh~PV} z=Xp9QONl#}97T>p>NX(x)}x4qq4-Rf48+uY_h2k|7lvK1ts z_AEf5A02%q3^D+ae3ClM;XNUA;e{7YUiZ4!m8XWh8pmF6!uY!Uh~0t6h(%g*LI#pL zPfGxkakCK*f7#1kwj8>rXdb7tLL0>%0wuC2MCmLb*)R3q%yqQx2gReb4qJeZJ2559 z<;^TQ39m7S=e*I4Zd4vJ@Gl^1g>fHvKi_$83sH;>T0(2h$NIZahnKdOY>P`cZvOnV zr#-DakNeoiKDO*xQ(_@EdhOBGeHpPkK$hYppq7EM5C!oTwUBNW%s++4K5ea-lM-xm z+gU)n1*kkCCoFa{Nr!6%qG0Mj^2j60HK6De_3X3HE}=EAtBjLQUf%6f+3=@EwtH+V znL{h*cM{;E?>wbYJl@paFD3QRfd#HJ&pflxFB)L5Ttm}i9`l%P9zI0Fh@Fkd&k#CK z3r<7v2Ad6@Fb5WRx@%nH8YOJxiFiYp)&bA8nga@HDLZf7&&y)*zUHY3cdbxA{_&45 zPwm8WG8$gqO&MXDvgFmPyt*=hb}^fxTG(QE7>9lP_DvrC@Q0UoGd=Eck1PKJ*5M7F z^zL`Rd!gfHFMHYKdCz;^6DEu?GQ! zq_cRSE_LL18OYD*neX!8LT>M3GB2j6PHNeP5_SuNooA+~Pk!=~C&wOpY*|4^=VtSM z#MVUgl1z{_AloGo*7{<;Z#F^)LJ2T0L;>FT#y6I|s(469G(hS6V`TTN@@9ot>ccgQ zMpwDYRSF}N2}3A~HZ4B!*7CYH4NhK}OEUmGIC{*f9mjm|gCAV#5#E0K)1NNkx^vYQ zmUvy>^xpTr_vGqVzj}!-436?>nAh%z-3M=3Nz_GFq)xhd42J}K4hj86g3$7KLSo@C z0d_#*YFE2j5qxt&${b}i{{t(&PB&3RUW^AE@>F$Md4gCoIO?dQCPy4`L|69^a?k3s zdKdv--pk#1)H~hjPUVe^Jg#Vg7q#H^oaa2J%%`K^!e=R#RK1(c^AXzv)-@cRB|AYU zDTLigA1MO9S>W}omI{$s6+$f3CFu5Ec_(Lp33zDZ*=*79!d~&hqs@cz6r6&nhvNAz zB<3B>hliBD24%Bd#K>4QqMWCka!Lt*c~12(Ca-zTYsv!#wy=~5-TIa^6xC>7eg92wR@eXy=BN@vDX=#|n5$ZOwV0E9V8xZk>Ch-_FewO^kFS?E@eWr|kM65NdfL8Y)(ka-$HYo6&%_`JWc)2m7; zg1PQsxmMwvi~4>efJL-(t-NKJbCE(t4M>+@;(n%;>VH4y|U$V+B*+P0E^=Ik1y z6@`nZ zT_n;-`IJSk@;u`^-tmrd4AWjMC6$+gXZqnF2kYsYa_aCnq0?199mTx%wXa=5Y*@B$ zBep-H8=MpQ@NZgkroKCSY;`IM6ye=yr0od2g`9+`QCf#B%puJac`D(o|CdAH&1cO` z*Af+IJ1OY@o`s>Zn9Imm@pcy48zJBO-uITX)M$0g(pHa@4q*QcyS5o$ycVV)xJKD?<=(Y8zd zEUaV!iC1;3(cUaCrHr|(klDRuk-C#?(|9#11w(h9k#%nfb=kmoN=D&$OT5fA3t$d! zSZy{B*H7Oqws~Nmm-U#RcIPdKjo2L^1NQ#O19>u4B2S&C6~^(lJl%Q~7ATdKRZ38Y zqC=o0=wLX^>j1&7Nt71Rj`;Z=FFbAAWFBHnz}69O`1;qszA(stNzaO{KA_3!9fMJq zn+6XiSdcO&71B5rCCs(j%^2Epa-32=E7y2FD(Hx5AcKs4t&^R!m}gN~O0fzrD>=;X z7@}osm?rduR@)YBJl&JmsK(Q|&jZ=FtoU<+p%sFM|4 zRX~$#7*;xzcJyf3q!E!AAdR4;beDjLAV`dq7>$685TskW6-M{y?q+~A8zFsmcDIXd zH~+rxeV?>EHlmL*z)EAz&l_6(ptNvY+O0gj>I3pLN#mh2mp1kuUL5U8zmQ9uF=d$m~jl^PDwHaOx9&EvCFmVJC7>Tfc(_M1{UsEg= zLON_ThK?i4`AD3I{g{Kq?r$&yKgZ>;5#-UA*c(~&ax_f(9ip6KI*^#eZi=7h&wyO! z$#jW6pAwW;gR#Tux;lr#&rD!Md7n2<8I6pHGaMG#Sr^O*T;H@`@kV?_fA`~x5~u>m zXFTgt9HRt`^I}|4B9y58;nA-5mSSdUfR-bmH--jnv<5Z}45vSJzi5{2bY0rbK zShOI^D*NSr9l-9RV7s?Q?<{2thpfxm#QQmEL}S%i4$icH@tB=Nayh5u#VB^HODm8L zgD^XV8Qve}kV0WSt>h*ds)tWxw&=Q=1Z+d@MfHEQF~yj(}H_T>Aopp7hg^-}2++I3DSpqBCB&;H#MS3VoWzoK_w zQj^x%QT-*kmXWb~$8fC-hzEozq zRiiO}rS2f~p-z#x%7@a9fm>g4JXl18?ci4yzgbel6Lv`nB6*}rl@m;Czml;-A#@<4 ztkCyoq$|Tf&XT(5I}U*Im(P9eZ*}K&4gB0+LkfF$hzek|s^SWRpQsG{Y)27H>DjKe zW2(ZX$w&X6)?xA*%glMTpZizclBlLs^?9x-yo|VGyl8w9^g2U#GiJ8baXMjkTSvfX zPqMjI?lmmg$jb7iolJfu&_i{vK)fuN;%RHtvr%5yTV{3RliJ<0b>Vx$Kb@s zk2+rY#V=oaD-7%K(9Fgo*aCGbn)OG@gh_@sBtRAEOVOt*n8sxUNTmMzegwxw0sRoA zB#V}&U`4+C=R87(7#O9|flhWKE9h0cDYre%&m0NX;}C90wwES(qRyfSg+X+6`i2vA zhBa#L`s_$)mD@iJb1guO4SA+TTA6xDS$VQ+(Gk7|DeJ`RWpaBX4hfhxv`bvsT__XmF3P34ujw9`I~gMe zBTDoiDevO8lVnl{qLq&klie;+Yb-7F{E#Dzifn4~uOTF{9VZ~hlu zplwJ=wRoD3%p7VGVnO`482zJBSKova^e_*`$k%POqZ6aa*h1MveyVzEl(}?Ni)2dZ z&HF0iLqs!emLpyY1L5>+ct4%QR{!BHNFa(2NG}TMzb=tEmmQNZ=#cul_Rx@n1va+b zMQA)WR}Mq*7h14UG6MrwT+ zs5dT~tck$rpCwv5P`1B;JMEm8bK+%5nIO47QGCq9<+B*ONBiOFu{&JQzXQG(aPk3x zFC{Ns-71uxt=Ct0Ht)SPLw`?(Q%fz2{+le6uH#jQ4h{yaVp@%0W7zhkg@Gi^Ws53+ zbN|<(86FdaV?>;@a!4UTQ1aPphqr2UYJ{2}3r||0vflp^1MzH?@t6Pf{To+~N`!nG z#YcY93?b?e$;Y1|ra+xJ^CRy2FqK?>xN}P5{`pDY4=o< zwy?MU1ahE5CvtUi@scJ}7+@p4{cg}sTIb*P46o_h15qv$ko6HJSY}! z9W%^JC?Y#9EPK&k0H@1`yAqcX5fe;!cbo4GD6{5~4Hz9txm2D*a4$!^qmw~UzC|23 zIrEIctYh>x-pVkkd>%>TFXdQ`5YA8pCVJShxvpn+#`}zzCQ#4HtF&cdLWw1x7=0Pe z`zU8yL4*>T?DSbY_~gmsoOjIbf=s%J3Myoqb@s=kHC=#VTL%A;5x z#pPyPl~vq5Z)tX!i0j2}(W7{+pH; z?|u-Ja}QeNCng(>m`ROM+&Jq7zQMn!5}QK7lhBl+>bl1GeD(@);L94mOu@M&#>c@!Kepo z9daUuU)g`!FO+xCH!8BP(Z@PgNZ!9OzW*7WQ$#aiQptKxG;cZy+X;(U^`SY`gP7 zYj0)Fex|xOPJeD%AwTsvEW<&>>7}58GPC>SCbp;EGJ+K(Bt5OVWd2bNDEReJ-e>BF zkK5qR@V{Byr+xpFq$Tp7yzhS%fsD}$%xA0ISv>o{f4yIx7UnZ;wH4fG+*+>Gu1UGVpLTUzrGXV@)^q_)P*3{d~WBCDjeGk z7z9vW_Jn30k+O^@XP?hsTjnNf7bYS~(zd?M4QAqB!rVjDV94q#y~tbn_pyJ|eGdO9 z#zAss)PMX@LZAHU$B%c2eY=4Yok}!eW{sJp58n_j z$SE@79!QE!>wguuo{F{Av8w2a@X0|TBR5kYM6%x%Hd3x;iW-~NZ~w-o8}#!zLkFrm zd@*S&h#P>m|m$eysRd)f1o+PP=%hr6_EjpmXb+H$L$EYj%bGI*}8 zB*n$ezF43IZu@L7t{CVgI=E$PwqT5e6w0YwjnPuhg0!jepW2X4t5JN0UQ;lUbSip4u=USkTqM?_u9IMXQ z&J!X925JHyRb_+y-?=ko#|7{yf6jIG5V+?{Tyi>Ty=i$|uW)@V{i6i^0Sw+=4o@fF z_^C-B`6P zO}Jm!hfCE651-g3+WeaErs$?+&@m!hV7B~1($~+^@jKF^p7D6@XMUy?C%DW8XnoTE zki;(GF;H>Y>1KYYaskJN!*vYoRO<7oT<(W<*dN0?hidteZ^sWGF22nw{mM;fIE)Lv zIsaRmLMRAX+Pt4dSN1%GD(!z%0G8eh?zj-=yx= z4dx?|Krf~d7&~jI+t9W-COJWPv{Y}VV z3{6XsJHYh#5BXB1Ses$a_>Sb_8X1Z&Qhk1N#iMkl(eY>gG2M^OldbxC!Jl>R&M%2_ z?tJh@0?aGD4=ExV-fF(NeF*~Wd~`Q#zc58+To#o!k=ultc}HZ|kbc8iStWQ0x-u}! zX7~rxP}7_)sSK1z_i8*Vb2cS=lv2p{K{vy*Q zSo-9OMJ)0VWgWj7`IvdS+2Knaxs;vZ?N15EdWS>O+?1O19phw-|Us{2sAII>!!~sptpEzaAhlfT2 zlev~814EKYw%Oei&-9Yy2Xl%X1~~kTDX(mr2G{~#|8rcxMhsiNx&q^Fx?w>(#4|%7 zcP?dFZhH7DI&R;@Y`y3+c3@}lal@Y>CnPQg$l&#VdHY?IuU_BP0W4JFAy*=(HH;M0 zp{KKWI#-BGg{7!R8)vf7hyl7{>|80dJpH0i!(T0ZoXT5nt1bA^>k@))_vzUxNaUWu zvZEO$bv=SE2E>y6&2>6ry;@q&;5L&LmF_T{G_Ds6EuP6uok$%p`a0e~mqp1DN?{ng z7Pez^v249oMcahkowzK;BjRyNg06ms3b&%zoZP*7EACC@Q!tgU!jp0;X%ajY3l*8S zlfg|K{%6od-3f%51)}iB=wI{VlojU?S*9wtSG0ZMb(sRbYoxXdBTn% z?o$HA5eM%#&d*otMDAphZ(nyDY{uFtFfZkX<1J$4P955xfQ_MHWWci`<-}3RBR#zt zJ^7r#yOss)nrEXmyOyQ^_n=$F;*Ut_GJS*>xV}MoHgffZXj;h|FXZ)kw%6Oj5cIKR zYHduYmq`TvdIUW!a(B+S;tyr$yf*q-8k5rOE``)bkyz!A8VU{v65$F?l->mlWR8hh zY+`^+#FJ5ReR9*Z2hJiO%lWOp%~~s+CuCM)(XRqcUxq1?+rrDer%C|Myi7?e1hs;4 zrsv{P??(3CEBf+yFIixvoATk~-nW(DO)PwNQ5{Qe>h-atHv&}1|1-SWqS~ca-XlWm5Fa+&8+1?@*yG>%*QcZvD#OdXh&3bQBda zCo)iM9_3ItrEFR4GV`H(_a>eMhChKXqI6Z8)!}*D0Bf8T$-u1uE2-61EimOGRrB7m z4R;Nm#9r`I7!7?nWPFVmS&j5VJf@hn6$Qi*U8t zq($1*U-@AH$ObQ`<>}qpuzu8%O80>>_dNCdwKFLkE_?FbL!h$h?^|c1t)=>)&?^Zm zyv+zP^b*(!{&iK;$c%@+o_bV3NE)bK>1S)kb9(Ch`i3R@f4L{u$g?NO-+z&}P98KQ zdflfCVyf>NC`Yca6LXCT^_DS+`f=9d5#{KV&>g#&PUFC5_q*SfU4;7saQf~yA70dR z@ah>_0z`G#jpdi^cfNdw3UvwcjVmMR)bzJCm^(fj>m!QFcnDwdP6c35Zyr+nJL<2V zPtx2ZE!a5UTh#LeD1YSbV}#Wu$c65w8Z@y9C^{@{hQeGfD>lC|c6iV{6Hx;c)3J>l zN+4vjOaK2J6|K~4X(id*Joj0~z3stTMFB!-1PS1tmwzr4$4#$7sxGkyv%R8+F5jj5 z;MqkKJ}!~w92PisF>fNaqd@b}ZBpIf2elTSPm|otmt`cQz>W;-?ETplAmR)yLF=|w zsv=J#j#6X;nkA#_Ju{|S(8(>Q`RG<}LwU$4XF_r%YR*7O5c0;LB^@eZ<@QPsbl{eO z`AJnb$6Q5*lRiGb`#C3EX=Pjb0^C8cVA_`B1s@86TU(Mj394T0{bh{g`T4C{+hRKx z{2ae7ZnIh4jBum0WFg<mchv{`zwbi2L+E{+Sz>LJNCxE@4&-bh7|@y_=n~~ooe)o*mk&0uv+^Jq+`umpkJ0pO_f1%g4KtS zyehKINQhOJ5?jV}O>KcS*@oBWkYeM)mXI<0>u)p$fd}Q4fd}*${q49wtKE{gl$kS^ zDn+i5brN|m^L|Vejh@*J{S%L__wsNRmKsxopQ@MB?DrG{k#_0zJfr2g9r|mq<5|XA z>vj{3`)qLgac7g^adRkr6r)1@*y@h=P)&QHgJW2D z5INZI{+(TW6tbRI;lJIonSo1%s3IhLLz#gEis1Lej-MQ5+pJ94zf*FE;?~n0sB11~ z>C6veh&I(JRd2KMw#p?A5AFc-2K*O;mSvne>XFYlJ?@Mq*(!o*<*0osx2ORXN-*ZC zAAM@9J5QB$ehy(L$vA3>9<|q!57`NBT~$^Vr)AnDu;Vm$FSF<3X0e9fhm*>T-&GWL z>W*v3`kAs%?j@g)t1F%+^jCU!z?EO-Sb0RftkkR4$=YD(-yz$r=u-th77i@t?Bbi{WQSuIIz1hJ_r*)6*uQI3E z}9r8dUQ0jWO z+fiLgh-9mYW>hrJI_55!;;4)avig>uEK0LJsMh)0z;n_cQhb~%MYSAyfnr>-aaR>_ zGmX=A$TX$?nYCEIed~d!r8da725gEYgQ$)JHC3fBjY3K!faG<=kOS<`Rvu8!bG98Z zPEhNTM0@s>IIB%Bq?tukJiFrhEF4w*o3a*1Jf$+XL*`fpB;_SGZ(y(Jm z49FACFa8s0%kK`O9`sz$#u_}ysr?z$;Pw5b7Pldf0FQ=&s-^4Xk!Fy#N{6ml@=PA0 zt;pbEf&Cr0?$ufdwK{0#|CU1R#^SqHhtCmNo+;-5)RB3M?`2eRVv)5Nn`4!Pt>rGi zxV{+!b-cQk}7DbRUq$)(KU`NsCq1A#B*THD5i71p(t`(9Bgd zFYYM@U#Kd@QxlL{gA?~MV0B`ro7^&g5WnP4S4F5R?16g<3HUrw8W0gwLCX9zpnE`- zS$b8Y+;PDtKV;S{=+nvW-9>AFmRaP0N)Zq0CF#7p`FH7<+2x|Mal5hv!tNHtM?DL2 zE)W=&ry^>xUSBWdTBcxqnJu7d`3qL=vM(K2JH7#|4zo=XcRrR(cEtH@A-kqs=gD zncmQ`-}mjoKym=l91MkMpy;8Nm7c08T@T$dRIO;;dBozI%+kE09_bFO^3xjdSfRi^ z*dct&+nT|JPuSA}s8pBVaqs^yq9mAcB&E@OcEPI3GYCLROu@TB_m)lNYGQ{=8i+zH z7jQh=L9KWpAaxTgka3kS;O7ASyRc_|f0Z-3_2-)udKux9p`1Y&nH)$w^y1*qme&`X z0`&7JI%?_!q1Lxfmk~27Ui5?xxmy*5*UorC9IRh%a}-bE^b|yo!$&8M8QAmb=t02c zvPZ6?plX++QhC4se9A^5rrF^&o#%Fj{G>X|!$N0Z161Z-{&EA9VZ zH<1%fqm?;!fff2Rje_Pa9x)xR9+c@lr;ARWu=;1!y_l4E@C>mYI8_$i-cw`f%TRS@ zf#4Lj9q7fehu5oU6HJ=K3o@%JP+}@1L%b23YeTmoBsF+iKtIa&J|B9WErmyN`Ol7( z>S6o0-kc-Bcr-HrYA!f8phEpogrcN7roO}a-ey4NQn)Y?B6ehk#IxTr9QmK?_}WY@ zu(LK+7GyjG=(%<#-ztP(bx~{Z-uFcXYAyR$Z#H_qTi^!J8&cGSmCBUsd~#yi6=JS} zL>t@gF@PA8nkD&ozE5-4 z?E8A@u*G?}zVvIxtu{o~6YB+4a=#t8+qNCZsQnrZ;>KzJ+E z6jJ#3%5v#FTae-yNEb+u)kZl9p=ux^66C@-n5mtVK9FRRjk~Qc%aiUwMxyU-uLtfE zP+U^A53owL2}g&JaYUIS-#u|G)kB6d{9_m3(@G1}yW~Gyid%eqUP2JSA6_%9H?@KO zGrr2yx!rptu(|`@U*6RQihsbM;4BS9)z#!$hBWbnUt^CTAF_!ujI8Hbc z1VlH09@72^nTx?AslIkW!KWSWV-Kw3h}e+MjB1$zE&tZF&48JHzX4q3368Hh$oQ^Y z4{Pl0_>mNf#n|#{63a4H9X#aLi9|+7o40oFua6;<+aeSa%e~*l+n>n&TJVh<7yCN2 zfJXd}`>KlVwYc>Gl9&?nzRX9<{m#@h$|(X=lmgPMmv6RS3S2kpWemyiv6>!y(C1Np z3Z_(i5rBf7Rq4%s?a1lYQJBL_C3!Ajr%m47lG&uq7$Mel!Eo`4IGq(C@{XuldPQ{HLjlm~)+Olo z?Ft=D_Mgk&{>T1X9eN%$J5$>gD_ec%CNav0JU)JF4rJx};Dg!VbM^-qJyM4b&o7S3 zUK&tvNlWVcaQ=^MI~CwSjRi}pji-MKq)E!j=ig*1Lavz1@NT7}4P=h#SNaQ!kaQjz zmzwjqmq;uW2~W!6%b{ZoB!WHB@_7fLLmh7sky!Gpq-@DDvlIG_LHo^UV-;A3%!dr8 z{>tLIDc{B56HI}^aNJsM>w(n8VDV0Z!@1RpW`K+_Z|{HmSq zttvN7K^vO1AIxl9FuOIJLw3+Vq9UZup1_-0JTcj^Mj9r(Pnbr#V7-+;6bp46D9#Be@Ey`_YFf-wNBI zh{U$fk_cC%<1;6Fk=<)81dy=qKt!`Qn4$U$qCk=Q#E$3rKd(-@nZ_U!H?#F5)+6!BNQAy{-yQ@&LWwT4l8q1&A&XtAjzg^7gZ+7e+=EExd z4jkV~TD+B9`7kg0!DtQqSCiGL0XYS^StEBg8kw)ncA9vmGC^YNhV{C!VrX0k@2 zZ$B)tS6c8mFl}1r#s4I6T0_fO*;~e^FU#y?M$zrB95c;CYLRobs&lotleGjj4xMYR zruRmL+0l!6yzL)cmBZ@~*++vpEF zg$UVz((xnPEHYcTt|xLk^$HbOo9=-Te>Z(3!Seg**+2mwxCoPHB10c+Ya@C&EOoKC z70&U^epu%I2G=vSK*tkcNVQvsPi7(|vdYEzC54t`$%}Hi#MY?lN_so{;LC{#HrK)P zIL%Hd*1c=w{F_@ZB>obp_I@StrAJXv!)HCi9ooCEIx1VR~toP$}K~bsHNYfM@m4Q7i?aP8vB^}70cV-noTa? z?P@9+x)=JE+x}#ADqQj*e7_yfl3(G$q?*vD0sQYK3kq^%X7;ji#m)1@0)VpV!yQ=g6ZhJZj9Y4e>zFLH=OS> zV!Fkk;MPtmtZJ4_e$~*>&R-~j+qv_vaKLlW$%ha)PDnO^o}a}i?;G(+jekS;gKl8b z;7+>P+!q!IX~jyyk6b2r53h1Y5I_Iaa7*rU=XU4B$uk}n3v}pqI=fSdMyUY?f5z8) z6+}OuK~qp=>P&x@g5gixBEvJ$+X+@WDu{5_=Qw)l57W`_e_GfN zq9s+iG!C!Jzd5K#+a?I0#^4g|XAJ(^MmxB+*HFidZ5^M#=2^|0amh?7n+j6HKiwWg z7giSws7UWgHHt55^OhA4$g`s&p{1mt;4iiHo5 z=nVL{B?%z?BC(W2$0}Qp!zymlA!@)PNB{n1B%Mq*rKg8WPU- z;W;I#qQi*f5u#fAR{|o8S{9MW#}vjZ_>@EhFUV>XoPcSH{;I1pu!BgyHduviWgx~v zr57^R3}eOC!`bm3g^o@^rpG0_1zQKE$?=6|?Ul(NQb; zUgLNJ)T%w>&J+$2kORe5`#&e3^q=9pFvZZ*yDoXw=DcqDLz{;X#kGbb<}s!??tj(1 zvP|HQYvmudH^UP*<4uyS9!=&BEs|Ee@;ihTj6OtXY58Yj%}SdJ@SdFnZe^30TM>I@ zW+wdny2k@^ZI%abOR`a6#}Q9X%GdU=z893yq0`ex49BAN9H2NdYL!fh?9MGeD}Ryc z!997ut?qLh<}XyjAe4T9%}?!MHd zN&K!YmJLyK(O8mGQBp9CjzwG*jx4KjNvsBfLvZiqur@G3*bE}<02G=-UQ3ZeRC0%T zfY%M=9Z{$p-2pClKkp(1$LfU+`5pY;Zq$Hi>zPFY1Igi^V{ta0+b)*$4!i(gfE8MK zH(O}(${kRSj}kTfU1U})$LjZl1FKho89ze9E#3yuY3z|QeZ87oQidn}^j1$pcxKdd zA@x))EkAd?!qn=4vfm%RXM0l_wEN>-($a)bIi@l?Qv6pZ6^-I{2STu#JY(8dHymg8 zq*6S;jWtRJ;VlPt*M3K$oJ~~ur^>ArP5nLs=foaw8Oei+{LaX$XYR`K7#{zU`;nZl zEmZT%zhO{{+8!=UZ}=u`vBKwbuemeE=j{%a`I2ue;8JGAtOj3@5ctEeMJNrg)FxIm&3i_o&e zMbq48+kI8vn;A%q7r;v#JlN!pKOlHxHby$KbxI>Ky4~UfkJUD^$HFB8^DRY3COHUu5C5rVMT)@A&QZ4F89T2-onP@cXP%j09S&PJDd?Vt z%B4SZcDo(&G~V^__0I%oosDm_Pr04{ev`0<}bL|DAx2FNi7rmTQ~Me$H;VvYG8i zV{~mjNiuOPm~UIbLHx&fY<+h5Mcp{b(En$nO0}s~bvZ6gs2q_q_`4HnHi1BR`_6C{ z)s`2vi>Vm0>DNdb`nz5fDqGXv?#Yayd6f26syd~XN-&3SeXp9s$MoY;bpm=2u>8}= zR~<6k4U@cnBUj&1ajGZ-`@?HNKdBS7s;wmtLgk zH(#DO&7G?4>=hG=EySp32JwlrQv=xeGIAcroWECe%i&Q~>n$bX3GVgFQZ|Ov*t`82 z_f`uUBpY#t!9d2`63w93!w zS@YFvzT!a;U?YECZaf@$^8>|A1V{&QROtGYb_|Nj8tgVa1TxU5(BsI4k1Ae{Ns@;Q z_@<63{I1>bK@b+H?L7Xc!_&Be&Yn3MH8+o<`g;BE0a6J%E`vJN_dk7&CmqC>sp0;N zHose0hxMSCLv_=LWnQDzmU)D@ypR*Q`mB&> zKmhL`9Yl}MO^JC%3wL+oYhSPfL8fkuAgK=O1i{n2J;i>CMQyIrcHNa|1&D|_G&zEB zk)fC6wt~KyJ@OZ0DIubv?RKHv?lZo>c>HKo)2!{(%lHo{-eC0obP3f2J`$c%u?8LL zn4(KwMwUi9N}F;VyR(di>PzREfVUe&+X{W8QDxb_kFm{8$PxKn&3Yo&G&#Fg|2Xnp z*SP9zOUlU`%}+(VbM1Dw@hi7S^KH#N=h`IGrr^X#VytG)bkdOv9#yxU&*rVF z6}w*9eC3EE2(r+5bP&9!C3orD6GsO@`894)4-v0;5%6?Kw~zMOt&udh4D?3*?x84_ zfre!@6Y0)hENqKxX7~=k-O0Z@Gfe8wwwY(eGF7mVPb=& z)*EU1qKC;KmFin`U}Z`$TQ;r4yxHg7zAOngxc&&y5KIKF<+UFZGoJu@Ox}e-Z?Zpd zb3ABNt}=KRyqQ3S)c9LVkAJq1)zhR@*=Bb|!#3P}C1H8oEP#l+4pC~N#6uhxzcKaP zExT-dV|d})C~#k`89S-pc$TxZrzE#B_9<K&+vnSv?$yQ-ikC4(5M*u>v*jYEAr80bK&HvyGRjhm-8DZpO{{w zdsp-Ci#v}R5qmFW#>vCwa+mDV$#)UEQy$-a`%6A9y>H>!Yx4cG)wK7>;i^(=^l2kk z;W^W?TLHa4BQL1vPg0r1i3pb5WK&5?Nnq>$sBj9&P z2aMQh{Vl9Rv=X+$`!n(VTJ7w;vxXf=$#mS%0KM6p2PK9%8ArVX);lHE{lSuk0w-BV9>Ii&5$tBV6-3G} z=7PGo+N`>Lwjg8h5byZ(EVDVyIjEh&vodC;mCWYDYXO(t)T+{ ziAIAVAD>jg@8o9hB+TdU$uiw+8nY`q>!dn z6X94|M`FHCRVx2(;}esP-%Ps=e-FG;9nmDpdpfUq`53(9h-IdZYFZAPJUCnHsPD+R z+_T(4XGg_qsiBj!jl04%+Gz5nghkwy>`c$1o7!{JwCSY1U4}+NBm*H-6t_%0v3p|L zqLh=#Ip#|QpijRtjAAA+jF@u%Aas<^@@HHTP|z=*?%WLg!{-~N#9y0430u=-@&Hi5 zt#I0!97|@#d?l9p1oSWzdE?Wu*l~5_#oU}X-!-df_d@z=R9YCo&w23WFTT6VWZwD+ z9vPKiRB&P6=lm{^C{sS{dC!rxJn1}deYN3Z-kO9j|EaVe5N2faJU9~xecX}p&H3dd z?o;~GMizRN>!$ND5Q*BB6J62SDsTfq_hLb zxDv6Td8U#X()aY@d@@8sFO`|zXPr9Jpqw@WJ??NfqS7_aaqxv^6@0&zpqe2!7?U7FJ9{k%yg>!dTZYqzrQimka1UhU-ns9NnVjdmjGS?p9ezEXN z$Ils%xioOpIH(yJuj1!E$EmWs`Fg>feub?y*tcTf>X9qo3hF3XBg2=aLvyZ<75QEF z^z7LPdml}&sC9wa9=`PIj~)Fb-Av;~3Eqdgwm zI<789t2f^$QggLE2oX}K4K|yMnL;TuXTjQl_lD*`jA2I7x- z&0p~=^3dHQ!7nmJ74o;tSFMhDA6tF>XJ)QdS{$H6pSCL^hby>wi{0lLEGF{6{%q78@vVnpe?m@m8MChBc7N{B? z>P@eqxzobPi-5XVZkE_xV9(w*Zz#dvT>S|#w`ZqH6&|C(vBPgmVwIKC^2pCk>I$7@ zkrIq9k_Q=RL-4fW$KzS2__e3^&W zUzii5v^F^7MJIE~P(wZ^F%*AmY`#9ygR7`E$91wGH(ul`K5tor-K`dS6XzrTrtKlu z9!jv1Wb=Q?eM^7VpHLM+1v+Z*wGWOaGy^3xnqnjOU~o6`vR(Hfx*V3JU2<|Zkzc{< zmv1r}rDkFhuQ*X$GhFW1e6e{_XW5L$Or|?Jo|~g7UqxeLM(FA$^UY8uIs(gS?EO66 zoo<<%tT*GH7j+v(UL2Q)48USia+4Y9A0mfjkRRp!I_e4zOlu)shRPa!ebHhwxHkp(<4sLyioRzH_dNxFcI{~EJAIJ@>RrdU zy#;(f7@?oxyT3D$*&f*|9%ZLOUN2%%x=Q|;=r+wCz8Z`z+n&h&LjHsEcu^d8&Vb$h zo}-pEc!sr0Cum zJ<0p5VqJ_VpS2^}O(xaB=BxhAQW}ei>B$=ZMki#oXY~b!RfzGtd3w+CGH~y!-m=o; Q2iVtZMRkR8Ig6nG0Fxl1Z~y=R literal 0 HcmV?d00001 diff --git a/examples/shellmath/runTests.sh b/examples/shellmath/runTests.sh new file mode 100644 index 00000000..c9687e5f --- /dev/null +++ b/examples/shellmath/runTests.sh @@ -0,0 +1,124 @@ +#!/bin/env bash + +############################################################################### +# runTests.sh +# +# Usage: runTests.sh [testFile] +# where testFile defaults to testCases.in +# +# Processes a test file such as the testCases.in included with this package +############################################################################### + +# Process one line from the test cases file. Invoked below through mapfile. +function _shellmath_runTests() +{ + local lineNumber=$1 + local text=$2 + + # Trim leading whitespace + [[ $text =~ ^[$' \t']*(.*) ]] + text=${BASH_REMATCH[1]} + + # Skip comments and blank lines + [[ "$text" =~ ^# || -z $text ]] && return 0 + + # Check for line continuation + local len="${#text}" + if [[ ${text:$((len-1))} == '\' ]]; then + + # Eat the continuation character and add to the buffer + __shellfloat_commandBuffer+="${text/%\\/ }" + + # Defer processing + return + + # No line continuation + else + + # Assemble the command + local command=${__shellfloat_commandBuffer}${text} + __shellfloat_commandBuffer="" + + words=($command) + + # Expand first word to an assertion function + case ${words[0]} in + + Code) + words[0]=_shellmath_assert_return${words[0]} + + # Validate next word as a positive integer + if [[ ! "${words[1]}" =~ ^[0-9]+$ ]]; then + echo Line: "$lineNumber": Command "$command" + echo FAIL: \"Code\" requires integer return code + return 1 + else + nextWord=2 + fi + ;; + + String) + words[0]=_shellmath_assert_return${words[0]} + # Allow multiword arguments if quoted + if [[ ${words[1]} =~ ^\" ]]; then + if [[ ${words[1]} =~ \"$ ]]; then + nextWord=2 + else + for ((nextWord=2;;nextWord++)); do + if [[ ${words[nextWord]} =~ \"$ ]]; then + ((nextWord++)) + break + fi + done + fi + else + nextWord=2 + fi + ;; + + Both) + ;; + + *) + echo -e ${RED}FAIL${NO_COLOR} Line "$lineNumber": Command "$command": Code or String indicator required + return 2 + ;; + esac + + # Expand the next word to a shellmath function name + words[nextWord]=_shellmath_${words[nextWord]} + if ! type -t "${words[nextWord]}" >/dev/null; then + echo "${RED}FAIL${NO_COLOR} Line $lineNumber: Command "$command": Syntax error. Required: String|Code value operation args..." + return 3 + fi + + # Run the command, being respectful of shell metacharacters + fullCommand="${words[*]}" + eval "$fullCommand" + local returnString + _shellmath_getReturnValue returnString + echo -e "$returnString" Line "$lineNumber": "$command" + + fi + +} + + +function _main() +{ + source shellmath.sh + source assert.sh + + # Initialize certain globals. As "public" functions, the arithmetic + # functions need to do this themselves, but there are some "private" + # functions that need this here when they are auto-tested. + _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true + + # Process the test file line-by-line using the above runTests() function + mapfile -t -c 1 -C _shellmath_runTests -O 1 < "${1:-testCases.in}" + + exit 0 +} + +_main "$@" + diff --git a/examples/shellmath/shellmath.sh b/examples/shellmath/shellmath.sh new file mode 100644 index 00000000..5804ad2a --- /dev/null +++ b/examples/shellmath/shellmath.sh @@ -0,0 +1,1068 @@ +#!/bin/env bash +################################################################################ +# shellmath.sh +# Shell functions for floating-point arithmetic using only builtins +# +# Copyright (c) 2020 by Michael Wood. All rights reserved. +# +# Usage: +# +# source _thisPath_/_thisFileName_ +# +# # Conventional method: call the APIs by subshelling +# mySum=$( _shellmath_add 202.895 6.00311 ) +# echo $mySum +# +# # Optimized method: use hidden globals to simulate more flexible pass-and-return +# _shellmath_isOptimized=1 +# _shellmath_add 44.2 -87 +# _shellmath_getReturnValue mySum +# echo $mySum +# +################################################################################ + + +################################################################################ +# Program constants +################################################################################ +declare -A -r __shellmath_numericTypes=( + [INTEGER]=0 + [DECIMAL]=1 +) + +declare -A -r __shellmath_returnCodes=( + [SUCCESS]="0:Success" + [FAIL]="1:General failure" + [ILLEGAL_NUMBER]="2:Invalid argument; decimal number required: '%s'" + [DIVIDE_BY_ZERO]="3:Divide by zero error" +) + +declare -r -i __shellmath_true=1 +declare -r -i __shellmath_false=0 + +declare __shellmath_SUCCESS __shellmath_FAIL __shellmath_ILLEGAL_NUMBER + +################################################################################ +# Program state +################################################################################ +declare __shellmath_isOptimized=${__shellmath_false} +declare __shellmath_didPrecalc=${__shellmath_false} + + +################################################################################ +# Error-handling utilities +################################################################################ +function _shellmath_getReturnCode() +{ + local errorName=$1 + return "${__shellmath_returnCodes[$errorName]%%:*}" +} + +function _shellmath_warn() +{ + # Generate an error message and return control to the caller + _shellmath_handleError -r "$@" + return $? +} + +function _shellmath_exit() +{ + # Generate an error message and EXIT THE SCRIPT / interpreter + _shellmath_handleError "$@" +} + +function _shellmath_handleError() +{ + # Hidden option "-r" causes return instead of exit + local returnDontExit=$__shellmath_false + if [[ "$1" == "-r" ]]; then + returnDontExit=${__shellmath_true} + shift + fi + + # Format of $1: returnCode:msgTemplate + [[ "$1" =~ ^([0-9]+):(.*) ]] + returnCode=${BASH_REMATCH[1]} + msgTemplate=${BASH_REMATCH[2]} + shift + + # Display error msg, making parameter substitutions as needed + msgParameters="$*" + printf "$msgTemplate" "${msgParameters[@]}" + + if ((returnDontExit)); then + return "$returnCode" + else + exit "$returnCode" + fi +} + + +################################################################################ +# precalc() +# +# Pre-calculates certain global data and by setting the global variable +# "__shellmath_didPrecalc" records that this routine has been called. As an +# optimization, the caller should check that global to prevent needless +# invocations. +################################################################################ +function _shellmath_precalc() +{ + # Set a few global constants + _shellmath_getReturnCode SUCCESS; __shellmath_SUCCESS=$? + _shellmath_getReturnCode FAIL; __shellmath_FAIL=$? + _shellmath_getReturnCode ILLEGAL_NUMBER; __shellmath_ILLEGAL_NUMBER=$? + + # Determine the decimal precision to which we can accurately calculate. + # To do this we probe for the threshold at which integers overflow and + # take the integer floor of that number's base-10 logarithm. + # We check the 64-bit, 32-bit and 16-bit thresholds only. + if ((2**63 < 2**63-1)); then + __shellmath_precision=18 + __shellmath_maxValue=$((2**63-1)) + elif ((2**31 < 2**31-1)); then + __shellmath_precision=9 + __shellmath_maxValue=$((2**31-1)) + else ## ((2**15 < 2**15-1)) + __shellmath_precision=4 + __shellmath_maxValue=$((2**15-1)) + fi + + __shellmath_didPrecalc=$__shellmath_true +} + + +################################################################################ +# Simulate pass-and-return by reference using a secret global storage array +################################################################################ + +declare -a __shellmath_storage + +function _shellmath_setReturnValues() +{ + local -i _i + + for ((_i=1; _i<=$#; _i++)); do + __shellmath_storage[_i]="${!_i}" + done + + __shellmath_storage[0]=$# +} + +function _shellmath_getReturnValues() +{ + local -i _i + local evalString + + for ((_i=1; _i<=$#; _i++)); do + evalString+=${!_i}="${__shellmath_storage[_i]}"" " + done + + eval "$evalString" +} + +function _shellmath_setReturnValue() { __shellmath_storage=(1 "$1"); } +function _shellmath_getReturnValue() { eval "$1"=\"${__shellmath_storage[1]}\"; } +function _shellmath_getReturnValueCount() { eval "$1"=\"${__shellmath_storage[0]}\"; } + +################################################################################ +# validateAndParse(numericString) +# Return Code: SUCCESS or ILLEGAL_NUMBER +# Return Signature: integerPart fractionalPart isNegative numericType isScientific +# +# Validate and parse arguments to the main arithmetic routines +################################################################################ + +function _shellmath_validateAndParse() +{ + local n="$1" + local isNegative=${__shellmath_false} + local isScientific=${__shellmath_false} + local numericType returnCode + + ((returnCode = __shellmath_SUCCESS)) + + # Accept decimals: leading digits (optional), decimal point, trailing digits + if [[ "$n" =~ ^[-]?([0-9]*)\.([0-9]+)$ ]]; then + local integerPart=${BASH_REMATCH[1]:-0} + local fractionalPart=${BASH_REMATCH[2]} + + # Strip superfluous trailing zeros + if [[ "$fractionalPart" =~ ^(.*[^0])0*$ ]]; then + fractionalPart=${BASH_REMATCH[1]} + fi + + numericType=${__shellmath_numericTypes[DECIMAL]} + + # Factor out the negative sign if it is present + if [[ "$n" =~ ^- ]]; then + isNegative=${__shellmath_true} + n=${n:1} + fi + + _shellmath_setReturnValues "$integerPart" "$fractionalPart" \ + $isNegative "$numericType" $isScientific + return "$returnCode" + + # Accept integers + elif [[ "$n" =~ ^[-]?[0-9]+$ ]]; then + numericType=${__shellmath_numericTypes[INTEGER]} + + # Factor out the negative sign if it is present + if [[ "$n" =~ ^- ]]; then + isNegative=${__shellmath_true} + n=${n:1} + fi + + _shellmath_setReturnValues "$n" 0 $isNegative "$numericType" $isScientific + return "$returnCode" + + # Accept scientific notation: 1e5, 2.44E+10, etc. + elif [[ "$n" =~ (.*)[Ee](.*) ]]; then + local significand=${BASH_REMATCH[1]} + local exponent=${BASH_REMATCH[2]} + + # Validate the significand: optional sign, integer part, + # optional decimal point and fractional part + if [[ "$significand" =~ ^[-]?([0-9]+)(\.([0-9]+))?$ ]]; then + + isScientific=${__shellmath_true} + + # Separate the integer and fractional parts + local sigInteger=${BASH_REMATCH[1]} + local sigIntLength=${#sigInteger} + local sigFraction=${BASH_REMATCH[3]} + + # Strip superfluous trailing zeros + if [[ "$sigFraction" =~ ^(.*[^0])0*$ ]]; then + sigFraction=${BASH_REMATCH[1]} + fi + + local sigFracLength=${#sigFraction} + + if [[ "$n" =~ ^- ]]; then + isNegative=${__shellmath_true} + n=${n:1} + fi + + # Rewrite the scientifically-notated number in ordinary decimal notation. + # IOW, realign the integer and fractional parts. Separate with a space + # so they can be returned as two separate values + if ((exponent > 0)); then + local zeroCount integer fraction + ((zeroCount = exponent - sigFracLength)) + if ((zeroCount > 0)); then + printf -v zeros "%0*d" "$zeroCount" 0 + n=${sigInteger}${sigFraction}${zeros}" 0" + numericType=${__shellmath_numericTypes[INTEGER]} + elif ((zeroCount < 0)); then + n=${sigInteger}${sigFraction:0:exponent}" "${sigFraction:exponent} + numericType=${__shellmath_numericTypes[DECIMAL]} + else + n=${sigInteger}${sigFraction}" 0" + numericType=${__shellmath_numericTypes[INTEGER]} + fi + integer=${n% *}; fraction=${n#* } + _shellmath_setReturnValues "$integer" "$fraction" $isNegative "$numericType" $isScientific + return "$returnCode" + + elif ((exponent < 0)); then + local zeroCount integer fraction + ((zeroCount = -exponent - sigIntLength)) + if ((zeroCount > 0)); then + printf -v zeros "%0*d" "$zeroCount" 0 + n="0 "${zeros}${sigInteger}${sigFraction} + numericType=${__shellmath_numericTypes[DECIMAL]} + elif ((zeroCount < 0)); then + n=${sigInteger:0:-zeroCount}" "${sigInteger:(-zeroCount)}${sigFraction} + numericType=${__shellmath_numericTypes[DECIMAL]} + else + n="0 "${sigInteger}${sigFraction} + numericType=${__shellmath_numericTypes[DECIMAL]} + fi + integer=${n% *}; fraction=${n#* } + _shellmath_setReturnValues "$integer" "$fraction" $isNegative "$numericType" $isScientific + return "$returnCode" + + else + # exponent == 0 means the number is already aligned as desired + numericType=${__shellmath_numericTypes[DECIMAL]} + _shellmath_setReturnValues "$sigInteger" "$sigFraction" $isNegative "$numericType" $isScientific + return "$returnCode" + fi + + # Reject strings like xxx[Ee]yyy where xxx, yyy are not valid numbers + else + ((returnCode = __shellmath_ILLEGAL_NUMBER)) + _shellmath_setReturnValues "" + return "$returnCode" + fi + + # Reject everything else + else + ((returnCode = __shellmath_ILLEGAL_NUMBER)) + _shellmath_setReturnValues "" + return "$returnCode" + fi +} + + +################################################################################ +# numToScientific (integerPart, fractionalPart) +# +# Format conversion utility function +################################################################################ +function _shellmath_numToScientific() +{ + local integerPart=$1 fractionalPart=$2 + local exponent head tail scientific + + if ((integerPart > 0)); then + ((exponent = ${#integerPart}-1)) + head=${integerPart:0:1} + tail=${integerPart:1}${fractionalPart} + elif ((integerPart < 0)); then + ((exponent = ${#integerPart}-2)) # skip "-" and first digit + head=${integerPart:0:2} + tail=${integerPart:2}${fractionalPart} + else + [[ "$fractionalPart" =~ ^[-]?(0*)([^0])(.*)$ ]] + exponent=$((-(${#BASH_REMATCH[1]} + 1))) + head=${BASH_REMATCH[2]} + tail=${BASH_REMATCH[3]} + fi + + # Remove trailing zeros + [[ $tail =~ ^.*[^0] ]]; tail=${BASH_REMATCH[0]:-0} + + printf -v scientific "%d.%de%d" "$head" "$tail" "$exponent" + + _shellmath_setReturnValue "$scientific" +} + + +################################################################################ +# _shellmath_add (addend_1, addend_2) +################################################################################ +function _shellmath_add() +{ + local n1="$1" + local n2="$2" + + if ((! __shellmath_didPrecalc)); then + _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true + fi + + local isVerbose=$(( __shellmath_isOptimized == __shellmath_false )) + + # Is the caller itself an arithmetic function? + local isSubcall=${__shellmath_false} + local isMultiplication=${__shellmath_false} + if [[ "${FUNCNAME[1]}" =~ shellmath_(add|subtract|multiply|divide)$ ]]; then + isSubcall=${__shellmath_true} + if [[ "${BASH_REMATCH[1]}" == multiply ]]; then + isMultiplication=${__shellmath_true} + fi + fi + + # Handle corner cases where argument count is not 2 + local argCount=$# + if ((argCount == 0)); then + echo "Usage: ${FUNCNAME[0]} addend_1 addend_2" + return "$__shellmath_SUCCESS" + elif ((argCount == 1)); then + # Note the result as-is, print if running "normally", and return + _shellmath_setReturnValue "$n1" + (( isVerbose && ! isSubcall )) && echo "$n1" + return "$__shellmath_SUCCESS" + elif ((argCount > 2 && !isSubcall)); then + local recursiveReturn + + # Use a binary recursion tree to add everything up + # 1) left branch + _shellmath_add "${@:1:$((argCount/2))}"; recursiveReturn=$? + _shellmath_getReturnValue n1 + if (( recursiveReturn != __shellmath_SUCCESS )); then + _shellmath_setReturnValue "$n1" + return "$recursiveReturn" + fi + # 2) right branch + _shellmath_add "${@:$((argCount/2+1))}"; recursiveReturn=$? + _shellmath_getReturnValue n2 + if (( recursiveReturn != __shellmath_SUCCESS )); then + _shellmath_setReturnValue "$n2" + return "$recursiveReturn" + fi + # 3) head node + local sum + _shellmath_add "$n1" "$n2"; recursiveReturn=$? + _shellmath_getReturnValue sum + _shellmath_setReturnValue "$sum" + if (( isVerbose && ! isSubcall )); then + echo "$sum" + fi + return "$recursiveReturn" + fi + + local integerPart1 fractionalPart1 integerPart2 fractionalPart2 + local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2 + local flags + + if ((isMultiplication)); then + integerPart1="$1" + fractionalPart1="$2" + integerPart2="$3" + fractionalPart2="$4" + + type1=${__shellmath_numericTypes[DECIMAL]} + type2=${__shellmath_numericTypes[DECIMAL]} + isNegative1=$__shellmath_false + isNegative2=$__shellmath_false + isScientific1=$__shellmath_false + isScientific2=$__shellmath_false + else + # Check and parse the arguments + _shellmath_validateAndParse "$n1"; flags=$? + _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1" + return $? + fi + _shellmath_validateAndParse "$n2"; flags=$? + _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2" + return $? + fi + fi + + # Quick add & return for integer adds + if ((type1==type2 && type1==__shellmath_numericTypes[INTEGER])); then + ((isNegative1)) && ((integerPart1*=-1)) + ((isNegative2)) && ((integerPart2*=-1)) + local sum=$((integerPart1 + integerPart2)) + if (( (!isSubcall) && (isScientific1 || isScientific2) )); then + _shellmath_numToScientific $sum "" + _shellmath_getReturnValue sum + fi + _shellmath_setReturnValue $sum + if (( isVerbose && ! isSubcall )); then + echo "$sum" + fi + return "$__shellmath_SUCCESS" + fi + + # Right-pad both fractional parts with zeros to the same length + local fractionalLen1=${#fractionalPart1} + local fractionalLen2=${#fractionalPart2} + if ((fractionalLen1 > fractionalLen2)); then + # Use printf to zero-pad. This avoids mathematical side effects. + printf -v fractionalPart2 %-*s "$fractionalLen1" "$fractionalPart2" + fractionalPart2=${fractionalPart2// /0} + elif ((fractionalLen2 > fractionalLen1)); then + printf -v fractionalPart1 %-*s "$fractionalLen2" "$fractionalPart1" + fractionalPart1=${fractionalPart1// /0} + fi + local unsignedFracLength=${#fractionalPart1} + + # Implement a sign convention that will enable us to detect carries by + # comparing string lengths of addends and sums: propagate the sign across + # both numeric parts (whether unsigned or zero). + if ((isNegative1)); then + fractionalPart1="-"$fractionalPart1 + integerPart1="-"$integerPart1 + fi + if ((isNegative2)); then + fractionalPart2="-"$fractionalPart2 + integerPart2="-"$integerPart2 + fi + + local integerSum=0 + local fractionalSum=0 + + ((integerSum = integerPart1+integerPart2)) + + # Summing the fractional parts is tricky: We need to override the shell's + # default interpretation of leading zeros, but the operator for doing this + # (the "10#" operator) cannot work directly with negative numbers. So we + # break it all down. + if ((isNegative1)); then + ((fractionalSum += (-1) * 10#${fractionalPart1:1})) + else + ((fractionalSum += 10#$fractionalPart1)) + fi + if ((isNegative2)); then + ((fractionalSum += (-1) * 10#${fractionalPart2:1})) + else + ((fractionalSum += 10#$fractionalPart2)) + fi + + unsignedFracSumLength=${#fractionalSum} + if [[ "$fractionalSum" =~ ^[-] ]]; then + ((unsignedFracSumLength--)) + fi + + # Restore any leading zeroes that were lost when adding + if ((unsignedFracSumLength < unsignedFracLength)); then + local lengthDiff=$((unsignedFracLength - unsignedFracSumLength)) + local zeroPrefix + printf -v zeroPrefix "%0*d" "$lengthDiff" 0 + if ((fractionalSum < 0)); then + fractionalSum="-"${zeroPrefix}${fractionalSum:1} + else + fractionalSum=${zeroPrefix}${fractionalSum} + fi + fi + + # Carry a digit from fraction to integer if required + if ((10#$fractionalSum!=0 && unsignedFracSumLength > unsignedFracLength)); then + local carryAmount + ((carryAmount = isNegative1?-1:1)) + ((integerSum += carryAmount)) + # Remove the leading 1-digit whether the fraction is + or - + fractionalSum=${fractionalSum/1/} + fi + + # Transform the partial sums from additive to concatenative. Example: the + # pair (-2,3) is not -2.3 but rather (-2)+(0.3), i.e. -1.7 so we want to + # transform (-2,3) to (-1,7). This transformation is meaningful when + # the two parts have opposite signs, so that's what we look for. + if ((integerSum < 0 && 10#$fractionalSum > 0)); then + ((integerSum += 1)) + ((fractionalSum = 10#$fractionalSum - 10**unsignedFracSumLength)) + elif ((integerSum > 0 && 10#$fractionalSum < 0)); then + ((integerSum -= 1)) + ((fractionalSum = 10**unsignedFracSumLength + 10#$fractionalSum)) + fi + # This last case needs to function either as an "else" for the above, + # or as a coda to the "if" clause when integerSum is -1 initially. + if ((integerSum == 0 && 10#$fractionalSum < 0)); then + integerSum="-"$integerSum + ((fractionalSum *= -1)) + fi + + # Touch up the numbers for display + local sum + ((10#$fractionalSum < 0)) && fractionalSum=${fractionalSum:1} + if (( (!isSubcall) && (isScientific1 || isScientific2) )); then + _shellmath_numToScientific "$integerSum" "$fractionalSum" + _shellmath_getReturnValue sum + elif ((10#$fractionalSum)); then + printf -v sum "%s.%s" "$integerSum" "$fractionalSum" + else + sum=$integerSum + fi + + # Note the result, print if running "normally", and return + _shellmath_setReturnValue $sum + if (( isVerbose && ! isSubcall )); then + echo "$sum" + fi + + return "$__shellmath_SUCCESS" +} + + +################################################################################ +# subtract (subtrahend, minuend) +################################################################################ +function _shellmath_subtract() +{ + local n1="$1" + local n2="$2" + local isVerbose=$(( __shellmath_isOptimized == __shellmath_false )) + + if ((! __shellmath_didPrecalc)); then + _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true + fi + + if (( $# == 0 || $# > 2 )); then + echo "Usage: ${FUNCNAME[0]} subtrahend minuend" + return "$__shellmath_SUCCESS" + elif (( $# == 1 )); then + # Note the value as-is and return + _shellmath_setReturnValue "$n1" + ((isVerbose)) && echo "$n1" + return "$__shellmath_SUCCESS" + fi + + # Symbolically negate the second argument + if [[ "$n2" =~ ^- ]]; then + n2=${n2:1} + else + n2="-"$n2 + fi + + # Calculate, note the result, print if running "normally", and return + local difference + _shellmath_add "$n1" "$n2" + _shellmath_getReturnValue difference + if ((isVerbose)); then + echo "$difference" + fi + + return $? +} + + +################################################################################ +# reduceOuterPairs (two integer parts [, two fractional parts]) +# +# Examines the magnitudes of two numbers in advance of a multiplication +# and either chops off their lowest-order digits or pushes them to the +# corresponding lower-order parts in order to prevent overflow in the product. +# The choice depends on whether 2 or 4 arguments are supplied. +################################################################################ +function _shellmath_reduceOuterPairs() +{ + local value1="$1" value2="$2" subvalue1="$3" subvalue2="$4" + + local digitExcess value1Len=${#value1} value2Len=${#value2} + ((digitExcess = value1Len + value2Len - __shellmath_precision)) + + # Be very precise about detecting overflow. The "digit excess" underestimates + # this: floor(log_10(longLongMax)). We don't want to needlessly lose precision + # when a product barely squeezes under the exact threshold. + if ((digitExcess>1 || (digitExcess==1 && value1 > __shellmath_maxValue/value2) )); then + + # Identify the digit-tails to be pruned off and either discarded or + # pushed past the decimal point. In pruning the two values we want to + # retain as much "significance" as possible, so we try to equalize the + # lengths of the remaining digit sequences. + local tail1 tail2 + local lengthDiff leftOver + + # Which digit string is longer, and by how much? + ((lengthDiff = value1Len - value2Len)) + if ((lengthDiff > 0)); then + if ((digitExcess <= lengthDiff)); then + # Chop digits from the longer string only + tail1=${value1:(-digitExcess)} + tail2="" # do not chop anything + else + # Chop more digits from the longer string so the two strings + # end up as nearly-equal in length as possible + ((leftOver = digitExcess - lengthDiff)) + tail1=${value1:(-(lengthDiff + leftOver/2))} + tail2=${value2:(-((leftOver+1)/2))} + fi + else + ((lengthDiff *= -1)) + # Mirror the above code block but swap 1 and 2 + if ((digitExcess <= lengthDiff)); then + tail1="" + tail2=${value2:(-digitExcess)} + else + ((leftOver = digitExcess - lengthDiff)) + tail1=${value1:(-((leftOver+1)/2))} + tail2=${value2:(-(lengthDiff + leftOver/2))} + fi + fi + + # Discard the least-significant digits or move them past the decimal point + value1=${value1%${tail1}} + [[ -n "$subvalue1" ]] && subvalue1=${tail1}${subvalue1%0} # remove placeholder zero + value2=${value2%${tail2}} + [[ -n "$subvalue2" ]] && subvalue2=${tail2}${subvalue2%0} + else + # Signal the caller that no rescaling was actually done + ((digitExcess = 0)) + fi + + _shellmath_setReturnValues "$value1" "$value2" \ + "$subvalue1" "$subvalue2" "$digitExcess" +} + +################################################################################ +# rescaleValue(value, rescaleFactor) +# +# Upscales a decimal value by "factor" orders of magnitude: 3.14159 --> 3141.59 +################################################################################ +function _shellmath_rescaleValue() +{ + local value="$1" rescalingFactor="$2" + local head tail zeroCount zeroTail + + [[ "$value" =~ ^(.*)\.(.*)$ ]] + head=${BASH_REMATCH[1]} + tail=${BASH_REMATCH[2]} + ((zeroCount = rescalingFactor - ${#tail})) + if ((zeroCount > 0)); then + printf -v zeroTail "%0*d" "$zeroCount" 0 + value=${head}${tail}${zeroTail} + elif ((zeroCount < 0)); then + value=${head}${tail:0:rescalingFactor}"."${tail:rescalingFactor} + else + value=${head}${tail} + fi + + _shellmath_setReturnValue "$value" +} + +################################################################################ +# reduceCrossPairs (two integer parts, two fractional parts) +# +# Examines the precision of the inner products (of "multiplication by parts") +# and if necessary truncates the fractional part(s) to prevent overflow +################################################################################ +function _shellmath_reduceCrossPairs() +{ + local value1="$1" value2="$2" subvalue1="$3" subvalue2="$4" + + local digitExcess value1Len=${#value1} value2Len=${#value2} + local subvalue1Len=${#subvalue1} subvalue2Len=${#subvalue2} + + # Check BOTH cross-products + ((digitExcess = value1Len + subvalue2Len - __shellmath_precision)) + if ((digitExcess > 1 || (digitExcess==1 && value1 > __shellmath_maxValue/subvalue2) )); then + subvalue2=${subvalue2:0:(-digitExcess)} + fi + ((digitExcess = value2Len + subvalue1Len - __shellmath_precision)) + if ((digitExcess > 1 || (digitExcess==1 && value2 > __shellmath_maxValue/subvalue1) )); then + subvalue1=${subvalue1:0:(-digitExcess)} + fi + + _shellmath_setReturnValues "$subvalue1" "$subvalue2" +} + + +function _shellmath_round() +{ + local number="$1" digitCount="$2" + local nextDigit=${number:digitCount:1} + + number=${number:0:digitCount} + if ((nextDigit >= 5)); then + printf -v number "%0*d" "$digitCount" $((10#$number + 1)) + fi + + _shellmath_setReturnValue "$number" +} + +################################################################################ +# multiply (multiplicand, multiplier) +################################################################################ +function _shellmath_multiply() +{ + local n1="$1" + local n2="$2" + + if ((! __shellmath_didPrecalc)); then + _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true + fi + + local isVerbose=$(( __shellmath_isOptimized == __shellmath_false )) + + # Is the caller itself an arithmetic function? + local isSubcall=${__shellmath_false} + if [[ "${FUNCNAME[1]}" =~ shellmath_(add|subtract|multiply|divide)$ ]]; then + isSubcall=${__shellmath_true} + fi + + # Handle corner cases where argument count is not 2 + local argCount=$# + if ((argCount == 0)); then + echo "Usage: ${FUNCNAME[0]} factor_1 factor_2" + return "$__shellmath_SUCCESS" + elif ((argCount == 1)); then + # Note the value as-is and return + _shellmath_setReturnValue "$n1" + (( isVerbose && ! isSubcall )) && echo "$n1" + return "$__shellmath_SUCCESS" + elif ((argCount > 2)); then + local recursiveReturn + + # Use a binary recursion tree to multiply everything out + # 1) left branch + _shellmath_multiply "${@:1:$((argCount/2))}"; recursiveReturn=$? + _shellmath_getReturnValue n1 + if (( recursiveReturn != __shellmath_SUCCESS )); then + _shellmath_setReturnValue "$n1" + return "$recursiveReturn" + fi + # 2) right branch + _shellmath_multiply "${@:$((argCount/2+1))}"; recursiveReturn=$? + _shellmath_getReturnValue n2 + if (( recursiveReturn != __shellmath_SUCCESS )); then + _shellmath_setReturnValue "$n2" + return "$recursiveReturn" + fi + # 3) head node + local product + _shellmath_multiply "$n1" "$n2"; recursiveReturn=$? + _shellmath_getReturnValue product + _shellmath_setReturnValue "$product" + if (( isVerbose && ! isSubcall )); then + echo "$product" + fi + return "$recursiveReturn" + fi + + local integerPart1 fractionalPart1 integerPart2 fractionalPart2 + local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2 + local flags + + # Check and parse the arguments + _shellmath_validateAndParse "$n1"; flags=$? + _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1" + return $? + fi + _shellmath_validateAndParse "$n2"; flags=$? + _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2" + return $? + fi + + # Overflow / underflow detection and accommodation + local rescalingFactor=0 + if ((${#integerPart1} + ${#integerPart2} + ${#fractionalPart1} + ${#fractionalPart2} >= ${__shellmath_precision})); then + _shellmath_reduceOuterPairs "$integerPart1" "$integerPart2" "$fractionalPart1" "$fractionalPart2" + _shellmath_getReturnValues integerPart1 integerPart2 fractionalPart1 fractionalPart2 rescalingFactor + if ((10#$fractionalPart1)); then type1=${__shellmath_numericTypes[DECIMAL]}; fi + if ((10#$fractionalPart2)); then type2=${__shellmath_numericTypes[DECIMAL]}; fi + + _shellmath_reduceCrossPairs "$integerPart1" "$integerPart2" "$fractionalPart1" "$fractionalPart2" + _shellmath_getReturnValues fractionalPart1 fractionalPart2 + + _shellmath_reduceOuterPairs "$fractionalPart1" "$fractionalPart2" + _shellmath_getReturnValues fractionalPart1 fractionalPart2 + fi + + # Quick multiply & return for integer multiplies + if ((type1==type2 && type1==__shellmath_numericTypes[INTEGER])); then + ((isNegative1)) && ((integerPart1*=-1)) + ((isNegative2)) && ((integerPart2*=-1)) + local product=$((integerPart1 * integerPart2)) + if ((rescalingFactor > 0)); then + _shellmath_rescaleValue "$product" "$rescalingFactor" + _shellmath_getReturnValue product + fi + if (( (!isSubcall) && (isScientific1 || isScientific2) )); then + _shellmath_numToScientific $product "" + _shellmath_getReturnValue product + fi + _shellmath_setReturnValue $product + if (( isVerbose && ! isSubcall )); then + echo "$product" + fi + return "$__shellmath_SUCCESS" + fi + + # The product has four components per the distributive law + local intProduct floatProduct innerProduct1 innerProduct2 + # Widths of the decimal parts + local floatWidth fractionalWidth1 fractionalWidth2 + + # Compute the integer and floating-point components + ((intProduct = integerPart1 * integerPart2)) + + fractionalWidth1=${#fractionalPart1} + fractionalWidth2=${#fractionalPart2} + ((floatWidth = fractionalWidth1 + fractionalWidth2)) + ((floatProduct = 10#$fractionalPart1 * 10#$fractionalPart2)) + if ((${#floatProduct} < floatWidth)); then + printf -v floatProduct "%0*d" "$floatWidth" "$floatProduct" + fi + + # Compute the inner products: First integer-multiply, then rescale + ((innerProduct1 = integerPart1 * 10#$fractionalPart2)) + ((innerProduct2 = integerPart2 * 10#$fractionalPart1)) + + # Rescale the inner products back to decimals so we can shellmath_add() them + if ((fractionalWidth2 <= ${#innerProduct1})); then + local innerInt1=${innerProduct1:0:(-$fractionalWidth2)} + local innerFloat1=${innerProduct1:(-$fractionalWidth2)} + integerPart1=${innerInt1} + fractionalPart1=${innerFloat1} + else + integerPart1=0 + printf -v fractionalPart1 "%0*d" "$fractionalWidth2" "$innerProduct1" + fi + if ((fractionalWidth1 <= ${#innerProduct2})); then + local innerInt2=${innerProduct2:0:(-$fractionalWidth1)} + local innerFloat2=${innerProduct2:(-$fractionalWidth1)} + integerPart2=${innerInt2} + fractionalPart2=${innerFloat2} + else + integerPart2=0 + printf -v fractionalPart2 "%0*d" "$fractionalWidth1" "$innerProduct2" + fi + + # Combine the distributed parts + local innerSum product + # Add the inner products to get the inner sum + _shellmath_add "$integerPart1" "$fractionalPart1" "$integerPart2" "$fractionalPart2" + _shellmath_getReturnValue innerSum + [[ "$innerSum" =~ (.*)\.(.*) ]] + integerPart1=${BASH_REMATCH[1]} + fractionalPart1=${BASH_REMATCH[2]} + # Add inner sum + outer sum + _shellmath_add "$integerPart1" "$fractionalPart1" "$intProduct" "$floatProduct" + _shellmath_getReturnValue product + + # Determine the sign of the product + if ((isNegative1 != isNegative2)); then + product="-"$product + fi + + # When we pre-detect overflow in the integer part of the computation, + # we compensate by shrinking the inputs by some order of magnitude. + # Having now finished the computation, we revert to the original magnitude. + if ((rescalingFactor > 0)); then + _shellmath_rescaleValue "$product" "$rescalingFactor" + _shellmath_getReturnValue product + fi + + # Convert to scientific notation if appropriate + if (( (!isSubcall) && (isScientific1 || isScientific2) )); then + _shellmath_numToScientific "${product%.*}" "${product#*.}" + _shellmath_getReturnValue product + fi + + # Note the result, print if running "normally", and return + _shellmath_setReturnValue $product + if (( isVerbose && ! isSubcall )); then + echo "$product" + fi + + return "$__shellmath_SUCCESS" +} + + +################################################################################ +# divide (dividend, divisor) +################################################################################ +function _shellmath_divide() +{ + local n1="$1" + local n2="$2" + local integerPart1 fractionalPart1 integerPart2 fractionalPart2 + local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2 + + if ((! __shellmath_didPrecalc)); then + _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true + fi + + local isVerbose=$(( __shellmath_isOptimized == __shellmath_false )) + + local isTesting=${__shellmath_false} + if [[ "${FUNCNAME[1]}" == "_shellmath_assert_functionReturn" ]]; then + isTesting=${__shellmath_true} + fi + + if [[ $# -eq 0 || $# -gt 2 ]]; then + echo "Usage: ${FUNCNAME[0]} dividend divisor" + return "$__shellmath_SUCCESS" + elif [[ $# -eq 1 ]]; then + # Note the value as-is and return + _shellmath_setReturnValue "$n1" + ((isVerbose)) && echo "$n1" + return "$__shellmath_SUCCESS" + fi + + # Check and parse the arguments + local flags + _shellmath_validateAndParse "$n1"; flags=$? + _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1" + return $? + fi + _shellmath_validateAndParse "$n2"; flags=$? + _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2 + if ((flags == __shellmath_ILLEGAL_NUMBER)); then + _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2" + return $? + fi + + # Throw error on divide by zero + if ((integerPart2 == 0 && 10#$fractionalPart2 == 0)); then + _shellmath_warn "${__shellmath_returnCodes[DIVIDE_BY_ZERO]}" "$n2" + return $? + fi + + # Convert the division problem to an *integer* division problem by rescaling + # both inputs so as to lose their decimal points. To obtain maximal precision, + # we scale up the numerator further, padding with as many zeros as it can hold + local numerator denominator quotient + local rescaleFactor zeroCount zeroTail + + if ((integerPart1 == 0)); then + integerPart1="" + fi + ((zeroCount = __shellmath_precision - ${#integerPart1} - ${#fractionalPart1})) + ((rescaleFactor = __shellmath_precision - ${#integerPart1} - ${#fractionalPart2})) + if ((zeroCount > 0)); then + printf -v zeroTail "%0*d" "$zeroCount" 0 + fi + + # Rescale and rewrite the fraction to be computed, and compute it + numerator=${integerPart1}${fractionalPart1}${zeroTail} + denominator=${integerPart2}${fractionalPart2} + ((quotient = 10#$numerator / 10#$denominator)) + + # For greater precision, re-divide by the remainder to get the next digits of the quotient + local remainder quotient_2 + ((remainder = 10#$numerator % 10#$denominator)) # cannot exceed numerator or thus, maxValue + ((zeroCount = __shellmath_precision - ${#remainder})) + if ((zeroCount > 0)); then + printf -v zeroTail "%0*d" "$zeroCount" 0 + else + zeroTail="" + fi + # Derive the new numerator from the remainder. Do not change the denominator. + numerator=${remainder}${zeroTail} + ((quotient_2 = 10#$numerator / 10#$denominator)) + quotient=${quotient}${quotient_2} + ((rescaleFactor += ${#quotient_2})) + + # Rescale back. For aesthetic reasons we also round off at the "precision"th decimal place + ((zeroCount = rescaleFactor - ${#quotient})) + if ((zeroCount >= 0)); then + local zeroPrefix="" fractionalPart + if ((zeroCount > 0)); then + printf -v zeroPrefix "%0*d" "$((rescaleFactor - ${#quotient}))" 0 + fi + fractionalPart=${zeroPrefix}${quotient} + _shellmath_round "$fractionalPart" $__shellmath_precision + _shellmath_getReturnValue fractionalPart + quotient="0."${fractionalPart} + else + fractionalPart=${quotient:(-$rescaleFactor)} + _shellmath_round "$fractionalPart" $__shellmath_precision + _shellmath_getReturnValue fractionalPart + quotient=${quotient:0:(-$rescaleFactor)}"."${fractionalPart} + fi + + # Determine the sign of the quotient + if ((isNegative1 != isNegative2)); then + quotient="-"$quotient + fi + + if ((isTesting)); then + # Trim zeros. (Requires decimal point and zero tail.) + if [[ "$quotient" =~ [\.].*0$ ]]; then + # If the decimal point IMMEDIATELY precedes the 0s, remove that too + [[ $quotient =~ [\.]?0+$ ]] + quotient=${quotient%${BASH_REMATCH[0]}} + fi + fi + + # Convert to scientific notation if appropriate + if ((isScientific1 || isScientific2)); then + _shellmath_numToScientific "${quotient%.*}" "${quotient#*.}" + _shellmath_getReturnValue quotient + fi + + # Note the result, print if running "normally", and return + _shellmath_setReturnValue "$quotient" + if ((isVerbose)); then + echo "$quotient" + fi + + return "$__shellmath_SUCCESS" +} + diff --git a/examples/shellmath/slower_e_demo.sh b/examples/shellmath/slower_e_demo.sh new file mode 100755 index 00000000..d8fc9314 --- /dev/null +++ b/examples/shellmath/slower_e_demo.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +############################################################################### +# This script illustrates the use of the shellmath APIs to perform +# decimal calculations. Here we approximate the mathematical constant 'e' +# using its Maclaurin polynomials (i.e. its Taylor polynomials centered at 0). +############################################################################### + +source shellmath.sh + +# Setting the '-t' flag will cause the script to time the algorithm +if [[ "$1" == '-t' ]]; then + do_timing=${__shellmath_true} + shift +fi + +if [[ $# -ne 1 ]]; then + echo "USAGE: ${BASH_SOURCE##*/} [-t] *N*" + echo " Approximates 'e' using the N-th order Maclaurin polynomial" + echo " (i.e. the Taylor polynomial centered at 0)." + echo " Specify the '-t' flag to time the main algorithm." + exit 0 +elif [[ ! "$1" =~ ^[0-9]+$ ]]; then + echo "Illegal argument. Whole numbers only, please." + exit 1 +fi + + +function run_algorithm() +{ + # Initialize + n=0; N=$1; zero_factorial=1 + + # Initialize e to the zeroth-order term + term=$(_shellmath_divide 1 $zero_factorial) + e=$term + + # Compute successive terms T(n) := T(n-1)/n and accumulate into e + for ((n=1; n<=N; n++)); do + term=$(_shellmath_divide "$term" "$n") + e=$(_shellmath_add "$e" "$term") + done + + echo "e = $e" +} + + +if (( do_timing == __shellmath_true )); then + time run_algorithm "$1" +else + run_algorithm "$1" +fi + +exit 0 + diff --git a/examples/shellmath/testCases.in b/examples/shellmath/testCases.in new file mode 100644 index 00000000..54e3a822 --- /dev/null +++ b/examples/shellmath/testCases.in @@ -0,0 +1,142 @@ +################################################################ +# The general testcase syntax is +# assertionType expectedValue functionUnderTest [args ... ] +# +# where assertionType is either of: +# Code to indicate the (bash-style) integer return code +# String to indicate the string "printed" as a side effect +# +# and functionUnderTest is the function name +# with the "_shellmath_" prefix removed. +################################################################ + +################################ +# Tests for SUPPORTING FUNCTIONS +################################ + +# Tests for getReturnCode() +Code 0 getReturnCode SUCCESS +Code 1 getReturnCode FAIL +Code 2 getReturnCode ILLEGAL_NUMBER + +## Tests for validateAndParse(): +## Validate a number, determine its type and sign, split it into parts + +# Detect Invalid input +Code 2 validateAndParse NaN +String "" validateAndParse NaN +# Positive integers +String "4 0 0 0 0" validateAndParse 4 +# Negative integers +String "9 0 1 0 0" validateAndParse -9 +# Decimals +String "4 2 0 1 0" validateAndParse 4.2 +# Negative decimals +String "4 2 1 1 0" validateAndParse -4.2 +# Scientific / exponential notation: Check all code branches +String "340000 0 0 0 1" validateAndParse 3.4e5 +String "344 4 0 1 1" validateAndParse 3.444e2 +String "34567 0 0 0 1" validateAndParse 3.4567e4 +String "0 003456 0 1 1" validateAndParse 3.456e-3 +String "34 56 0 1 1" validateAndParse 345.6e-1 +String "0 23011 0 1 1" validateAndParse 23.011e-2 +String "23 011 0 1 1" validateAndParse 23.011e0 + +#################### +# Tests for ADDITION +#################### +String 4 add 4 +String 9 add 4 5 + +# Same-length decimal tails with no leading zeros, no carry across decimal point +String 2.214 add 1.105 1.109 + +# Carry across decimal point +String 3.8 add 1.9 1.9 +String -3.8 add -1.9 -1.9 + +# Different-length decimals, one with leading zero +String 2.195 add 1.105 1.09 +String -2.195 add -1.105 -1.09 + +# Same-length tails having leading zeros +String 2.014 add 1.005 1.009 +String -2.014 add -1.005 -1.009 +# Different-length tails with and without leading zeros +String 3.31462 add 1.905 1.40962 +String 2.01462 add 1.005 1.00962 + +# Subtraction +String 2.5 subtract 5.2 2.7 +String -2.5 subtract 2.7 5.2 +String 2.5 add 5.2 -2.7 + +# Integer part equal to 0 +String 1.5 add 0.6 0.9 +String 1.5 add .6 .9 +String -0.3 add 0.6 -0.9 +String -0.3 add .6 -.9 + +# Recursive/multiple addition +String 12 add 2 4 6 +String 6.6 add 1.1 2.2 3.3 + +########################## +# Tests for MULTIPLICATION +########################## +String 4 multiply 4 +String 20 multiply 4 5 + +String 21.32 multiply 4.1 5.2 +String -21.32 multiply -4.1 5.2 + +# Carry-heavy products +String 98.901 multiply 9.9 9.99 + +# Leading zeros after decimal point: +# Track place value with zero-padding +String 1.0201 multiply 1.01 1.01 +String 0.0001 multiply 0.01 0.01 +String 0.0001 add 0 0.0001 + +# Staggered decimal precisions +String 0.000001 multiply 0.01 0.0001 + +# Interpret in base 10 +String 2.2781 multiply 1.09 2.09 + +# Recursive multiplication +String 35.1384 multiply 1.1 2.2 3.3 4.4 + +#################### +# Tests for DIVISION +#################### +String 4 divide 4 +String 4 divide 20 5 + +String 0.5 divide 1 2 +String -0.5 divide -1 2 + +# Mixed fractions +String 34.54 divide 3454 100 + +# Non-terminating decimals +String 0.166666666666666667 divide 1 6 + +# Decimal arguments +String 0.25 divide 0.5 2 +String 0.04165 divide 0.1666 4 + +########################### +# Tests for scientific math +########################### +String 8.8e4 add 1.1e4 7.7e4 +String 4.239e1 add 1.224e1 3.015e1 +String -6.6e4 add 1.1e4 -7.7e4 +String -66000 add 11000 -77000 +String 1.23123e2 add 1.23e2 1.23e-1 +String 8.1403e7 multiply 2.03e5 4.01e2 +String 1.0e-7 multiply 1.0e-3 1.0e-4 +String 1.0e-7 multiply 1e-3 1e-4 + + diff --git a/examples/shellmath/timingData.txt b/examples/shellmath/timingData.txt new file mode 100644 index 00000000..d1de887b --- /dev/null +++ b/examples/shellmath/timingData.txt @@ -0,0 +1,42 @@ +$ ######## Activate optimized mode as described in the README ######## +$ __shellmath_isOptimized=1 + +$ ######## Addition ######## +$ time { for ((i=0; i<100; i++)); do _shellmath_add 3.1415926 2.7182818; _shellmath_getReturnValue sum; done; } +real 0m0.196s +user 0m0.195s +sys 0m0.000s +$ time { for ((i=0; i<100; i++)); do sum=$(bc <<< "3.1415926+2.7182818"); done; } +real 0m0.488s +user 0m0.092s +sys 0m0.384s + +$ ######## Subtraction ######## +$ time { for ((i=0; i<100; i++)); do _shellmath_subtract 3.1415926 2.7182818; _shellmath_getReturnValue diff; done; } +real 0m0.236s +user 0m0.234s +sys 0m0.001s +$ time { for ((i=0; i<100; i++)); do diff=$(bc <<< "3.1415926-2.7182818"); done; } +real 0m0.461s +user 0m0.090s +sys 0m0.388s + +$ ######## Multiplication ######## +$ time { for ((i=0; i<100; i++)); do _shellmath_multiply 3.1415926 2.7182818; _shellmath_getReturnValue prod; done; } +real 0m0.340s +user 0m0.333s +sys 0m0.005s +$ time { for ((i=0; i<100; i++)); do prod=$(bc <<< "3.1415926*2.7182818"); done; } +real 0m0.465s +user 0m0.105s +sys 0m0.377s + +$ ######## Division ######## +$ time { for ((i=0; i<100; i++)); do _shellmath_divide 3.1415926/2.7182818; _shellmath_getReturnValue quot; done; } +real 0m0.196s +user 0m0.195s +sys 0m0.000s +$ time { for ((i=0; i<100; i++)); do quot=$(bc <<< "scale=8; 3.1415926/2.7182818"); done; } +real 0m0.463s +user 0m0.116s +sys 0m0.364s diff --git a/subst.c b/subst.c index 457c7325..b82ada60 100644 --- a/subst.c +++ b/subst.c @@ -6243,7 +6243,7 @@ read_comsub (fd, quoted, flags, rflag) /* read a multibyte character from buf */ /* punt on the hard case for now */ memset (&ps, '\0', sizeof (mbstate_t)); - mblen = mbrtowc (&wc, bufp-1, bufn+1, &ps); + mblen = mbrtowc (&wc, bufp-1, bufn, &ps); if (MB_INVALIDCH (mblen) || mblen == 0 || mblen == 1) istring[istring_index++] = c; else diff --git a/support/shobj-conf b/support/shobj-conf index 45e157cd..729d847b 100644 --- a/support/shobj-conf +++ b/support/shobj-conf @@ -151,13 +151,13 @@ darwin*) darwin[1-7].*) SHOBJ_STATUS=unsupported SHOBJ_LDFLAGS='-dynamic' - SHLIB_XLDFLAGS='-arch_only `/usr/bin/arch` -install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -v' + SHLIB_XLDFLAGS='-install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -v' ;; # Darwin 8 == Mac OS X 10.4; Mac OS X 10.N == Darwin N+4 *) case "${host_os}" in darwin[89]*|darwin1[012]*) - SHOBJ_ARCHFLAGS='-arch_only `/usr/bin/arch`' + SHOBJ_ARCHFLAGS= ;; *) # Mac OS X 10.9 (Mavericks) and later SHOBJ_ARCHFLAGS= diff --git a/tests/builtins.right b/tests/builtins.right index 832472f6..ae2cf3ef 100644 --- a/tests/builtins.right +++ b/tests/builtins.right @@ -1,3 +1,4 @@ +1000 a end-1 a @@ -144,11 +145,11 @@ AVAR foo declare -x foo="" declare -x FOO="\$\$" -./builtins.tests: line 226: declare: FOO: not found +./builtins.tests: line 228: declare: FOO: not found declare -x FOO="\$\$" ok ok -./builtins.tests: line 258: kill: 4096: invalid signal specification +./builtins.tests: line 260: kill: 4096: invalid signal specification 1 a\n\n\nb a @@ -271,4 +272,4 @@ type + command -p -- command -v type type + set +x -./builtins.tests: line 282: exit: status: numeric argument required +./builtins.tests: line 284: exit: status: numeric argument required diff --git a/tests/builtins.tests b/tests/builtins.tests index 00ebc0fd..8eee43e6 100644 --- a/tests/builtins.tests +++ b/tests/builtins.tests @@ -15,7 +15,9 @@ set +p set +o posix -ulimit -c 0 2>/dev/null +ulimit -S -c 0 2>/dev/null +ulimit -c -S -- 1000 2>/dev/null +ulimit -c 2>/dev/null # check that break breaks loops for i in a b c; do echo $i; break; echo bad-$i; done