diff options
-rw-r--r-- | gnu/usr.bin/dc/COPYING | 339 | ||||
-rw-r--r-- | gnu/usr.bin/dc/ChangeLog | 77 | ||||
-rw-r--r-- | gnu/usr.bin/dc/Makefile | 7 | ||||
-rw-r--r-- | gnu/usr.bin/dc/NEWS | 7 | ||||
-rw-r--r-- | gnu/usr.bin/dc/README | 13 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.c | 909 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.info | 330 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.texinfo | 381 | ||||
-rw-r--r-- | gnu/usr.bin/dc/decimal.c | 1235 | ||||
-rw-r--r-- | gnu/usr.bin/dc/decimal.h | 93 |
10 files changed, 3391 insertions, 0 deletions
diff --git a/gnu/usr.bin/dc/COPYING b/gnu/usr.bin/dc/COPYING new file mode 100644 index 000000000000..e77696ae8ddf --- /dev/null +++ b/gnu/usr.bin/dc/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/usr.bin/dc/ChangeLog b/gnu/usr.bin/dc/ChangeLog new file mode 100644 index 000000000000..09aaf4706884 --- /dev/null +++ b/gnu/usr.bin/dc/ChangeLog @@ -0,0 +1,77 @@ +Fri May 21 15:02:52 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) + + * Version 0.2 released. + +Fri May 21 11:48:11 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * decimal.c (decimal_rem): Update to match fixes in decimal_div. + +Thu May 20 03:12:41 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) + + * Makefile.in (realclean): Delete dc.info* and configure. + (DISTFILES): Add `texinfo.tex' and `NEWS'. + texinfo.tex: New file (symlink to canonical source). + NEWS: New file. + +Wed May 19 11:30:09 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * dc.c (dec_read): Accept only A through F. + +Tue May 18 12:35:54 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * dc.c (read_string): New arg STARTC to handle nested brackets. + (execute): Change calls to read_string. + (condop): Don't assume result of decimal_compare has abs value <= 1. + (popmacro): If no macro in progress, exit. + +Sun May 2 00:42:47 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * decimal.c (decimal_div): Include in trial_dividend the digit + at length2 + i - 2, if there is one. + +Sat May 1 09:54:35 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * decimal.c (decimal_parse): Don't use digits without recalculation + if some digit exceeds the radix. + + * dc.c (execute): Treat A...F as digits. + (dec_read): Treat A...F as digits. + +Thu Apr 29 14:17:30 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * decimal.h (bcopy): Use memcpy, not memmove. + + * decimal.c (flush_trailing_digits): Use explicit loop, not bcopy. + +Tue Apr 20 17:21:27 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) + + * dc.c (pushsqrt): `precision' is an argument to `decimal_sqrt', not + `push'. + +Sat Apr 17 15:47:55 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) + + * All files: Updated GPL version number. + + * decimal.c: Include decimal.h and delete duplicate declarations. + + * decimal.h [!HAVE_BCOPY]: #define bcopy. + [!HAVE_BZERO]: #define bzero. + +Sun Feb 10 22:06:15 1991 Richard Stallman (rms at mole.ai.mit.edu) + + * dc.c (execute): Insert break; in \n case. + +Sun Jul 29 17:50:14 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * decimal.c (decimal_neg): New function. + +Fri Jul 27 04:11:34 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * bceval.c, bclex.c, bcprint.c, bcsym.c: Declare some functions + static. + +Mon Dec 25 03:01:49 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * Makefile: add some missing rules. + + * decimal.c: change a 'max' to 'MAX'. diff --git a/gnu/usr.bin/dc/Makefile b/gnu/usr.bin/dc/Makefile new file mode 100644 index 000000000000..d786bc80544d --- /dev/null +++ b/gnu/usr.bin/dc/Makefile @@ -0,0 +1,7 @@ +PROG= dc +CFLAGS+=-I${.CURDIR} -DHAVE_BCOPY=1 -DHAVE_BZERO=1 +SRCS= dc.c decimal.c +DPADD= ${LIBM} +LDADD= -lm + +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/dc/NEWS b/gnu/usr.bin/dc/NEWS new file mode 100644 index 000000000000..6486afb3ec86 --- /dev/null +++ b/gnu/usr.bin/dc/NEWS @@ -0,0 +1,7 @@ +Changes between version 0.2 and 0.1: + +* You can now have nested square bracket pairs within a string. + +* The letters A-F can now be part of a number when the input radix is +large enough to make them meaningful. + diff --git a/gnu/usr.bin/dc/README b/gnu/usr.bin/dc/README new file mode 100644 index 000000000000..c23cc6699202 --- /dev/null +++ b/gnu/usr.bin/dc/README @@ -0,0 +1,13 @@ +This is a preliminary release of GNU `dc', since people asked for it. GNU +`bc' (which doesn't rely on a separate `dc') has been available separately +for a couple of years. Eventually this version of `dc' will be merged with +the bc package. + +See comments in the file decimal.c for some limitations in the arbitrary +precision library. It's questionable whether it's worth fixing these +problems since the merged dc will probably use bc's math library instead. +However, you might want to be aware of known problems. + +See the file `INSTALL' for instructions on building and installing dc. + +Please report bugs to bug-gnu-utils@prep.ai.mit.edu. diff --git a/gnu/usr.bin/dc/dc.c b/gnu/usr.bin/dc/dc.c new file mode 100644 index 000000000000..933b24b916cd --- /dev/null +++ b/gnu/usr.bin/dc/dc.c @@ -0,0 +1,909 @@ +/* + * `dc' desk calculator utility. + * + * Copyright (C) 1984, 1993 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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, you can either send email to this + * program's author (see below) or write to: The Free Software Foundation, + * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include "decimal.h" /* definitions for our decimal arithmetic package */ + +FILE *open_file; /* input file now open */ +int file_count; /* Number of input files not yet opened */ +char **next_file; /* Pointer to vector of names of input files left */ + +struct regstack + { + decimal value; /* Saved value of register */ + struct regstack *rest; /* Tail of list */ + }; + +typedef struct regstack *regstack; + +regstack freeregstacks; /* Chain of free regstack structures for fast realloc */ + +decimal regs[128]; /* "registers", with single-character names */ +regstack regstacks[128]; /* For each register, a stack of previous values */ + +int stacktop; /* index of last used element in stack */ +int stacksize; /* Current allocates size of stack */ +decimal *stack; /* Pointer to computation stack */ + +/* A decimal number can be regarded as a string by + treating its contents as characters and ignoring the + position of its decimal point. + Decimal numbers are marked as strings by having an `after' field of -1 + One use of strings is to execute them as macros. +*/ + +#define STRING -1 + +int macrolevel; /* Current macro nesting; 0 if taking keyboard input */ +int macrostacksize; /* Current allocated size of macrostack and macroindex */ +decimal *macrostack; /* Pointer to macro stack array */ +int *macroindex; /* Pointer to index-within-macro stack array */ + /* Note that an empty macro is popped from the stack + only when an trying to read a character from it + or trying to push another macro. */ + +int ibase; /* Radix for numeric input. */ +int obase; /* Radix for numeric output. */ +int precision; /* Number of digits to keep in multiply and divide. */ + +char *buffer; /* Address of buffer used for reading numbers */ +int bufsize; /* Current size of buffer (made bigger when nec) */ + +decimal dec_read (); +regstack get_regstack (); +int fetch (); +int fgetchar (); +char *concat (); +void pushsqrt (); +void condop (); +void setibase (); +void setobase (); +void setprecision (); +void pushmacro (); +decimal read_string (); +void pushlength (); +void pushscale (); +void unfetch (); +void popmacros (); +void popmacro (); +void popstack (); +void print_obj (); +void print_string (); +void free_regstack (); +void pushreg (); +void execute (); +void fputchar (); +void push (); +void incref (); +void decref (); +void binop (); + +main (argc, argv, env) + int argc; + char **argv, **env; +{ + + ibase = 10; + obase = 10; + precision = 0; + + freeregstacks = 0; + + bzero (regs, sizeof regs); + bzero (regstacks, sizeof regstacks); + + bufsize = 40; + buffer = (char *) xmalloc (40); + + stacksize = 40; + stack = (decimal *) xmalloc (stacksize * sizeof (decimal)); + stacktop = -1; + + macrostacksize = 40; + macrostack = (decimal *) xmalloc (macrostacksize * sizeof (decimal)); + macroindex = (int *) xmalloc (macrostacksize * sizeof (int)); + macrolevel = 0; + /* Initialize for reading input files if any */ + + open_file = 0; + + file_count = argc - 1; + next_file = argv + 1; + + + while (1) + { + execute (); + } +} + +/* Read and execute one command from the current source of input */ + +void +execute () +{ + int c = fetch (); + + if (c < 0) exit (0); + + { + switch (c) + { + case '+': /* Arithmetic operators... */ + binop (decimal_add); + break; + + case '-': + binop (decimal_sub); + break; + + case '*': + binop (decimal_mul_dc); /* Like decimal_mul but hairy + way of deciding precision to keep */ + break; + + case '/': + binop (decimal_div); + break; + + case '%': + binop (decimal_rem); + break; + + case '^': + binop (decimal_expt); + break; + + case '_': /* Begin a negative decimal constant */ + { + decimal tem = dec_read (stdin); + tem->sign = !tem->sign; + push (tem); + } + break; + + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': /* All these begin decimal constants */ + unfetch (c); + push (dec_read (stdin)); + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + unfetch (c); + push (dec_read (stdin)); + break; + + case 'c': /* Clear the stack */ + while (stacktop >= 0) + decref (stack[stacktop--]); + break; + + case 'd': /* Duplicate top of stack */ + if (stacktop < 0) + error ("stack empty", 0); + else push (stack[stacktop]); + break; + + case 'f': /* Describe all registers and stack contents */ + { + int regno; + int somereg = 0; /* set to 1 if we print any registers */ + for (regno = 0; regno < 128; regno++) + { + if (regs[regno]) + { + printf ("register %c: ", regno); + print_obj (regs[regno]); + somereg = 1; + printf ("\n"); + } + } + if (somereg) + printf ("\n"); + if (stacktop < 0) + printf ("stack empty\n"); + else + { + int i; + printf ("stack:\n"); + for (i = 0; i <= stacktop; i++) + { + print_obj (stack[stacktop - i]); + printf ("\n"); + } + } + } + break; + + case 'i': /* ibase <- top of stack */ + popstack (setibase); + break; + + case 'I': /* Push current ibase */ + push (decimal_from_int (ibase)); + break; + + case 'k': /* like i, I but for precision instead of ibase */ + popstack (setprecision); + break; + + case 'K': + push (decimal_from_int (precision)); + break; + + case 'l': /* l<x> load register <x> onto stack */ + { + char c1 = fetch (); + if (c1 < 0) exit (0); + if (!regs[c1]) + error ("register %c empty", c1); + else + push (regs[c1]); + } + break; + + case 'L': /* L<x> load register <x> to stack, pop <x>'s own stack */ + { + char c1 = fetch (); + if (c1 < 0) exit (0); + if (!regstacks[c1]) + error ("nothing pushed on register %c", c1); + else + { + regstack r = regstacks[c1]; + if (!regs[c1]) + error ("register %c empty after pop", c1); + else + push (regs[c1]); + regs[c1] = r->value; + regstacks[c1] = r->rest; + free_regstack (r); + } + } + break; + + case 'o': /* o, O like i, I but for obase instead of ibase */ + popstack (setobase); + break; + + case 'O': + push (decimal_from_int (obase)); + break; + + case 'p': /* Print tos, don't pop, do print newline afterward */ + if (stacktop < 0) + error ("stack empty", 0); + else + { + print_obj (stack[stacktop]); + printf ("\n"); + } + break; + + case 'P': /* Print tos, do pop, no newline afterward */ + popstack (print_obj); + break; + + case 'q': /* Exit */ + if (macrolevel) + { popmacro (); popmacro (); } /* decrease recursion level by 2 */ + else + exit (0); /* If not in a macro, exit the program. */ + + break; + + case 'Q': /* Tos says how many levels to exit */ + popstack (popmacros); + break; + + case 's': /* s<x> -- Pop stack and set register <x> */ + if (stacktop < 0) + empty (); + else + { + int c1 = fetch (); + if (c1 < 0) exit (0); + if (regs[c1]) decref (regs[c1]); + regs[c1] = stack[stacktop--]; + } + break; + + case 'S': /* S<x> -- pop stack and push as new value of register <x> */ + if (stacktop < 0) + empty (); + else + { + int c1 = fetch (); + if (c1 < 0) exit (0); + pushreg (c1); + regs[c1] = stack[stacktop--]; + } + break; + + case 'v': /* tos gets square root of tos */ + popstack (pushsqrt); + break; + + case 'x': /* pop stack , call as macro */ + popstack (pushmacro); + break; + + case 'X': /* Pop stack, get # fraction digits, push that */ + popstack (pushscale); + break; + + case 'z': /* Compute depth of stack, push that */ + push (decimal_from_int (stacktop + 1)); + break; + + case 'Z': /* Pop stack, get # digits, push that */ + popstack (pushlength); + break; + + case '<': /* Conditional: pop two numbers, compare, maybe execute register */ + /* Note: for no obvious reason, the standard Unix `dc' + considers < to be true if the top of stack is less + than the next-to-top of stack, + and vice versa for >. + This seems backwards to me, but I am preserving compatibility. */ + condop (1); + break; + + case '>': + condop (-1); + break; + + case '=': + condop (0); + break; + + case '?': /* Read expression from terminal and execute it */ + /* First ignore any leading newlines */ + { + int c1; + while ((c1 = getchar ()) == '\n'); + ungetc (c1, stdin); + } + /* Read a line from the terminal and execute it. */ + pushmacro (read_string ('\n', fgetchar, 0)); + break; + + case '[': /* Begin string constant */ + push (read_string (']', fetch, '[')); + break; + + case ' ': + case '\n': + break; + + default: + error ("undefined command %c", c); + } + } +} + +/* Functionals for performing arithmetic, etc */ + +/* Call the function `op', with the top of stack value as argument, + and then pop the stack. + If the stack is empty, print a message and do not call `op'. */ + +void +popstack (op) + void (*op) (); +{ + if (stacktop < 0) + empty (); + else + { + decimal value = stack[stacktop--]; + op (value); + decref (value); + } +} + +/* Call the function `op' with two arguments taken from the stack top, + then pop those arguments and push the value returned by `op'. + `op' is assumed to return a decimal number. + If there are not two values on the stack, print a message + and do not call `op'. */ + +void +binop (op) + decimal (*op) (); +{ + if (stacktop < 1) + error ("stack empty", 0); + else if (stack[stacktop]->after == STRING || stack[stacktop - 1]->after == STRING) + error ("operands not both numeric"); + else + { + decimal arg2 = stack [stacktop--]; + decimal arg1 = stack [stacktop--]; + + push (op (arg1, arg2, precision)); + + decref (arg1); + decref (arg2); + } +} + +void +condop (cond) + int cond; +{ + int regno = fetch (); + if (!regs[regno]) + error ("register %c is empty", regno); + else if (stacktop < 1) + empty (); + else + { + decimal arg2 = stack[stacktop--]; + decimal arg1 = stack[stacktop--]; + int relation = decimal_compare (arg1, arg2); + decref (arg1); + decref (arg2); + if (cond == relation + || (cond < 0 && relation < 0) + || (cond > 0 && relation > 0)) + pushmacro (regs[regno]); + } +} + +/* Handle the command input source */ + +/* Fetch the next command character from a macro or from the terminal */ + +int +fetch() +{ + int c = -1; + + while (macrolevel && + LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1]) + popmacro(); + if (macrolevel) + return macrostack[macrolevel - 1]->contents[macroindex[macrolevel-1]++]; + while (1) + { + if (open_file) + { + c = getc (open_file); + if (c >= 0) break; + fclose (open_file); + open_file = 0; + } + else if (file_count) + { + open_file = fopen (*next_file++, "r"); + file_count--; + if (!open_file) + perror_with_name (*(next_file - 1)); + } + else break; + } + if (c >= 0) return c; + return getc (stdin); +} + +/* Unread character c on command input stream, whatever it is */ + +void +unfetch (c) + char c; +{ + if (macrolevel) + macroindex[macrolevel-1]--; + else if (open_file) + ungetc (c, open_file); + else + ungetc (c, stdin); +} + +/* Begin execution of macro m. */ + +void +pushmacro (m) + decimal m; +{ + while (macrolevel && + LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1]) + popmacro(); + if (m->after == STRING) + { + if (macrolevel == macrostacksize) + { + macrostacksize *= 2; + macrostack = (decimal *) xrealloc (macrostack, macrostacksize * sizeof (decimal)); + macroindex = (int *) xrealloc (macroindex, macrostacksize * sizeof (int)); + } + macroindex[macrolevel] = 0; + macrostack[macrolevel++] = m; + incref (m); + } + else + { /* Number supplied as a macro! */ + push (m); /* Its effect wouyld be to push the number. */ + } +} + +/* Pop a specified number of levels of macro execution. + The number of levels is specified by a decimal number d. */ + +void +popmacros (d) + decimal d; +{ + int num_pops = decimal_to_int (d); + int i; + for (i = 0; i < num_pops; i++) + popmacro (); +} +/* Exit one level of macro execution. */ + +void +popmacro () +{ + if (!macrolevel) + exit (0); + else + { + decref (macrostack[--macrolevel]); + } +} + +void +push (d) + decimal d; +{ + if (stacktop == stacksize - 1) + stack = (decimal *) xrealloc (stack, (stacksize *= 2) * sizeof (decimal)); + + incref (d); + + stack[++stacktop] = d; +} + +/* Reference counting and storage freeing */ + +void +decref (d) + decimal d; +{ + if (!--d->refcnt) + free (d); +} + +void +incref (d) + decimal d; +{ + d->refcnt++; +} + +empty () +{ + error ("stack empty", 0); +} + +regstack +get_regstack () +{ + if (freeregstacks) + { + regstack r = freeregstacks; + freeregstacks = r ->rest; + return r; + } + else + return (regstack) xmalloc (sizeof (struct regstack)); +} + +void +free_regstack (r) + regstack r; +{ + r->rest = freeregstacks; + freeregstacks = r; +} + +void +pushreg (c) + char c; +{ + regstack r = get_regstack (); + + r->rest = regstacks[c]; + r->value = regs[c]; + regstacks[c] = r; + regs[c] = 0; +} + +/* Input of numbers and strings */ + +/* Return a character read from the terminal. */ + +fgetchar () +{ + return getchar (); +} + +void +fputchar (c) + char (c); +{ + putchar (c); +} + +/* Read text from command input source up to a close-bracket, + make a string out of it, and return it. + If STARTC is nonzero, then it and STOPC must balance when nested. */ + +decimal +read_string (stopc, inputfn, startc) + char stopc; + int (*inputfn) (); + int startc; +{ + int c; + decimal result; + int i = 0; + int count = 0; + + while (1) + { + c = inputfn (); + if (c < 0 || (c == stopc && count == 0)) + { + if (count != 0) + error ("Unmatched `%c'", startc); + break; + } + if (c == stopc) + count--; + if (c == startc) + count++; + if (i + 1 >= bufsize) + buffer = (char *) xrealloc (buffer, bufsize *= 2); + buffer[i++] = c; + } + result = make_decimal (i, 0); + result->after = -1; /* Mark it as a string */ + result->before++; /* but keep the length unchanged */ + bcopy (buffer, result->contents, i); + return result; +} + +/* Read a number from the current input source */ + +decimal +dec_read () +{ + int c; + int i = 0; + + while (1) + { + c = fetch (); + if (! ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'F') + || c == '.')) + break; + if (i + 1 >= bufsize) + buffer = (char *) xrealloc (buffer, bufsize *= 2); + buffer[i++] = c; + } + buffer[i++] = 0; + unfetch (c); + + return decimal_parse (buffer, ibase); +} + +/* Output of numbers and strings */ + +/* Print the contents of obj, either numerically or as a string, + according to what obj says it is. */ + +void +print_obj (obj) + decimal obj; +{ + if (obj->after == STRING) + print_string (obj); + else + decimal_print (obj, fputchar, obase); +} + +/* Print the contents of the decimal number `string', treated as a string. */ + +void +print_string (string) + decimal string; +{ + char *p = string->contents; + int len = LENGTH (string); + int i; + + for (i = 0; i < len; i++) + { + putchar (*p++); + } +} + +/* Set the input radix from the value of the decimal number d, if valid. */ + +void +setibase (d) + decimal d; +{ + int value = decimal_to_int (d); + if (value < 2 || value > 36) + error ("input radix must be from 2 to 36", 0); + else + ibase = value; +} + +/* Set the output radix from the value of the decimal number d, if valid. */ + +void +setobase (d) + decimal d; +{ + int value = decimal_to_int (d); + if (value < 2 || value > 36) + error ("output radix must be from 2 to 36", 0); + else + obase = value; +} + +/* Set the precision for mul and div from the value of the decimal number d, if valid. */ + +void +setprecision (d) + decimal d; +{ + int value = decimal_to_int (d); + if (value < 0 || value > 30000) + error ("precision must be nonnegative and < 30000", 0); + else + precision = value; +} + +/* Push the number of digits in decimal number d, as a decimal number. */ + +void +pushlength (d) + decimal d; +{ + push (decimal_from_int (LENGTH (d))); +} + +/* Push the number of fraction digits in d. */ + +void +pushscale (d) + decimal d; +{ + push (decimal_from_int (d->after)); +} + +/* Push the square root of decimal number d. */ + +void +pushsqrt (d) + decimal d; +{ + push (decimal_sqrt (d, precision)); +} + +/* Print error message and exit. */ + +fatal (s1, s2) + char *s1, *s2; +{ + error (s1, s2); + exit (1); +} + +/* Print error message. `s1' is printf control string, `s2' is arg for it. */ + +error (s1, s2) + char *s1, *s2; +{ + printf ("dc: "); + printf (s1, s2); + printf ("\n"); +} + +decimal_error (s1, s2) + char *s1, *s2; +{ + error (s1, s2); +} + +perror_with_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + error (s, name); +} + +/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + *(result + len1 + len2 + len3) = 0; + + return result; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +int +xmalloc (size) + int size; +{ + int result = malloc (size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +int +xrealloc (ptr, size) + char *ptr; + int size; +{ + int result = realloc (ptr, size); + if (!result) + fatal ("virtual memory exhausted"); + return result; +} diff --git a/gnu/usr.bin/dc/dc.info b/gnu/usr.bin/dc/dc.info new file mode 100644 index 000000000000..a30fea9f5555 --- /dev/null +++ b/gnu/usr.bin/dc/dc.info @@ -0,0 +1,330 @@ +This is Info file dc.info, produced by Makeinfo-1.52 from the input +file dc.texinfo. + + This file documents DC, an arbitrary precision calculator. + + Published by the Free Software Foundation, 675 Massachusetts Avenue, +Cambridge, MA 02139 USA + + Copyright (C) 1984 Free Software Foundation, Inc. + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of +this manual under the conditions for verbatim copying, provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the Foundation. + + +File: dc.info, Node: Top, Next: Introduction, Prev: (dir), Up: (dir) + +* Menu: + +* Introduction:: Introduction +* Printing Commands:: Printing Commands +* Arithmetic:: Arithmetic +* Stack Control:: Stack Control +* Registers:: Registers +* Parameters:: Parameters +* Strings:: Strings +* Status Inquiry:: Status Inquiry +* Notes:: Notes + + +File: dc.info, Node: Introduction, Next: Printing Commands, Prev: Top, Up: Top + +Introduction +************ + + DC is a reverse-polish desk calculator which supports unlimited +precision arithmetic. It also allows you to define and call macros. +Normally DC reads from the standard input; if any command arguments are +given to it, they are filenames, and DC reads and executes the contents +of the files before reading from standard input. All output is to +standard output. + + To exit, use `q'. `C-c' does not exit; it is used to abort macros +that are looping, etc. (Currently this is not true; `C-c' does exit.) + + A reverse-polish calculator stores numbers on a stack. Entering a +number pushes it on the stack. Arithmetic operations pop arguments off +the stack and push the results. + + To enter a number in DC, type the digits, with an optional decimal +point. Exponential notation is not supported. To enter a negative +number, begin the number with `_'. `-' cannot be used for this, as it +is a binary operator for subtraction instead. To enter two numbers in +succession, separate them with spaces or newlines. These have no +meaning as commands. + + +File: dc.info, Node: Printing Commands, Next: Arithmetic, Prev: Introduction, Up: Top + +Printing Commands +***************** + +`p' + Prints the value on the top of the stack, without altering the + stack. A newline is printed after the value. + +`P' + Prints the value on the top of the stack, popping it off, and does + not print a newline after. + +`f' + Prints the entire contents of the stack and the contents of all of + the registers, without altering anything. This is a good command + to use if you are lost or want to figure out what the effect of + some command has been. + + +File: dc.info, Node: Arithmetic, Next: Stack Control, Prev: Printing Commands, Up: Top + +Arithmetic +********** + +`+' + Pops two values off the stack, adds them, and pushes the result. + The precision of the result is determined only by the values of + the arguments, and is enough to be exact. + +`-' + Pops two values, subtracts the first one popped from the second + one popped, and pushes the result. + +`*' + Pops two values, multiplies them, and pushes the result. The + number of fraction digits in the result is controlled by the + current precision flag (see below) and does not depend on the + values being multiplied. + +`/' + Pops two values, divides the second one popped from the first one + popped, and pushes the result. The number of fraction digits is + specified by the precision flag. + +`%' + Pops two values, computes the remainder of the division that the + `/' command would do, and pushes that. The division is done with + as many fraction digits as the precision flag specifies, and the + remainder is also computed with that many fraction digits. + +`^' + Pops two values and exponentiates, using the first value popped as + the exponent and the second popped as the base. The fraction part + of the exponent is ignored. The precision flag specifies the + number of fraction digits in the result. + +`v' + Pops one value, computes its square root, and pushes that. The + precision flag specifies the number of fraction digits in the + result. + + Most arithmetic operations are affected by the "precision flag", +which you can set with the `k' command. The default precision value is +zero, which means that all arithmetic except for addition and +subtraction produces integer results. + + The remainder operation (`%') requires some explanation: applied to +arguments `a' and `b' it produces `a - (b * (a / b))', where `a / b' is +computed in the current precision. + + +File: dc.info, Node: Stack Control, Next: Registers, Prev: Arithmetic, Up: Top + +Stack Control +************* + +`c' + Clears the stack, rendering it empty. + +`d' + Duplicates the value on the top of the stack, pushing another copy + of it. Thus, `4d*p' computes 4 squared and prints it. + + +File: dc.info, Node: Registers, Next: Parameters, Prev: Stack Control, Up: Top + +Registers +********* + + DC provides 128 memory registers, each named by a single ASCII +character. You can store a number in a register and retrieve it later. + +`sR' + Pop the value off the top of the stack and store it into register + R. + +`lR' + Copy the value in register R, and push it onto the stack. This + does not alter the contents of R. + + Each register also contains its own stack. The current register + value is the top of the register's stack. + +`SR' + Pop the value off the top of the (main) stack and push it onto the + stack of register R. The previous value of the register becomes + inaccessible. + +`LR' + Pop the value off the top of register R's stack and push it onto + the main stack. The previous value in register R's stack, if any, + is now accessible via the `lR' command. + + The `f' command prints a list of all registers that have contents +stored in them, together with their contents. Only the current +contents of each register (the top of its stack) is printed. + + +File: dc.info, Node: Parameters, Next: Strings, Prev: Registers, Up: Top + +Parameters +********** + + DC has three parameters that control its operation: the precision, +the input radix, and the output radix. The precision specifies the +number of fraction digits to keep in the result of most arithmetic +operations. The input radix controls the interpretation of numbers +typed in; *all* numbers typed in use this radix. The output radix is +used for printing numbers. + + The input and output radices are separate parameters; you can make +them unequal, which can be useful or confusing. Each radix must be +between 2 and 36 inclusive. The precision must be zero or greater. +The precision is always measured in decimal digits, regardless of the +current input or output radix. + +`i' + Pops the value off the top of the stack and uses it to set the + input radix. + +`o' +`k' + Similarly set the output radix and the precision. + +`I' + Pushes the current input radix on the stack. + +`O' +`K' + Similarly push the current output radix and the current precision. + + +File: dc.info, Node: Strings, Next: Status Inquiry, Prev: Parameters, Up: Top + +Strings +******* + + DC can operate on strings as well as on numbers. The only things you +can do with strings are print them and execute them as macros (which +means that the contents of the string are processed as DC commands). +Both registers and the stack can hold strings, and DC always knows +whether any given object is a string or a number. Some commands such as +arithmetic operations demand numbers as arguments and print errors if +given strings. Other commands can accept either a number or a string; +for example, the `p' command can accept either and prints the object +according to its type. + +`[CHARACTERS]' + Makes a string containing CHARACTERS and pushes it on the stack. + For example, `[foo]P' prints the characters `foo' (with no + newline). + +`x' + Pops a value off the stack and executes it as a macro. Normally + it should be a string; if it is a number, it is simply pushed back + onto the stack. For example, `[1p]x' executes the macro `1p', + which pushes 1 on the stack and prints `1' on a separate line. + + Macros are most often stored in registers; `[1p]sa' stores a macro + to print `1' into register `a', and `lax' invokes the macro. + +`>R' + Pops two values off the stack and compares them assuming they are + numbers, executing the contents of register R as a macro if the + original top-of-stack is greater. Thus, `1 2>a' will invoke + register `a''s contents and `2 1>a' will not. + +`<R' + Similar but invokes the macro if the original top-of-stack is less. + +`=R' + Similar but invokes the macro if the two numbers popped are equal. + This can also be validly used to compare two strings for equality. + +`?' + Reads a line from the terminal and executes it. This command + allows a macro to request input from the user. + +`q' + During the execution of a macro, this comand does not exit DC. + Instead, it exits from that macro and also from the macro which + invoked it (if any). + +`Q' + Pops a value off the stack and uses it as a count of levels of + macro execution to be exited. Thus, `3Q' exits three levels. + + +File: dc.info, Node: Status Inquiry, Next: Notes, Prev: Strings, Up: Top + +Status Inquiry +************** + +`Z' + Pops a value off the stack, calculates the number of digits it has + (or number of characters, if it is a string) and pushes that + number. + +`X' + Pops a value off the stack, calculates the number of fraction + digits it has, and pushes that number. For a string, the value + pushed is -1. + +`z' + Pushes the current stack depth; the number of objects on the stack + before the execution of the `z' command. + +`I' + Pushes the current value of the input radix. + +`O' + Pushes the current value of the output radix. + +`K' + Pushes the current value of the precision. + + +File: dc.info, Node: Notes, Prev: Status Inquiry, Up: Top + +Notes +***** + + The `:' and `;' commands of the Unix DC program are not supported, +as the documentation does not say what they do. The `!' command is not +supported, but will be supported as soon as a library for executing a +line as a command exists. + + + +Tag Table: +Node: Top960 +Node: Introduction1440 +Node: Printing Commands2603 +Node: Arithmetic3211 +Node: Stack Control5168 +Node: Registers5468 +Node: Parameters6586 +Node: Strings7659 +Node: Status Inquiry9857 +Node: Notes10571 + +End Tag Table diff --git a/gnu/usr.bin/dc/dc.texinfo b/gnu/usr.bin/dc/dc.texinfo new file mode 100644 index 000000000000..15b285fbd579 --- /dev/null +++ b/gnu/usr.bin/dc/dc.texinfo @@ -0,0 +1,381 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename dc.info +@settitle DC, An Arbitrary Precision Calculator +@c %**end of header + +@c This file has the new style title page commands. +@c Run `makeinfo' rather than `texinfo-format-buffer'. + +@c smallbook + +@c tex +@c \overfullrule=0pt +@c end tex + +@c Combine indices. +@synindex cp fn +@syncodeindex vr fn +@syncodeindex ky fn +@syncodeindex pg fn +@syncodeindex tp fn + +@ifinfo +This file documents DC, an arbitrary precision calculator. + +Published by the Free Software Foundation, +675 Massachusetts Avenue, +Cambridge, MA 02139 USA + +Copyright (C) 1984 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end ifinfo + +@setchapternewpage odd + +@titlepage +@title DC, An Arbitrary Precision Calculator + +@author by Richard Stallman +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1984 Free Software Foundation, Inc. + +@sp 2 +Published by the Free Software Foundation, @* +675 Massachusetts Avenue, @* +Cambridge, MA 02139 USA + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. + +@end titlepage +@page + +@node Top, Introduction, (dir), (dir) + +@menu +* Introduction:: Introduction +* Printing Commands:: Printing Commands +* Arithmetic:: Arithmetic +* Stack Control:: Stack Control +* Registers:: Registers +* Parameters:: Parameters +* Strings:: Strings +* Status Inquiry:: Status Inquiry +* Notes:: Notes +@end menu + +@node Introduction, Printing Commands, Top, Top +@comment node-name, next, previous, up +@chapter Introduction + +DC is a reverse-polish desk calculator which supports unlimited +precision arithmetic. It also allows you to define and call macros. +Normally DC reads from the standard input; if any command arguments +are given to it, they are filenames, and DC reads and executes the +contents of the files before reading from standard input. All output +is to standard output. + +To exit, use @samp{q}. @kbd{C-c} does not exit; it is used to abort +macros that are looping, etc. (Currently this is not true; @kbd{C-c} +does exit.) + +A reverse-polish calculator stores numbers on a stack. Entering a +number pushes it on the stack. Arithmetic operations pop arguments off +the stack and push the results. + +To enter a number in DC, type the digits, with an optional decimal +point. Exponential notation is not supported. To enter a negative +number, begin the number with @samp{_}. @samp{-} cannot be used for +this, as it is a binary operator for subtraction instead. +To enter two numbers in succession, separate them with spaces or +newlines. These have no meaning as commands. + +@node Printing Commands, Arithmetic, Introduction, Top +@chapter Printing Commands + +@table @samp +@item p +Prints the value on the top of the stack, +without altering the stack. A newline is printed +after the value. + +@item P +Prints the value on the top of the stack, +popping it off, and does not print a newline after. + +@item f +Prints the entire contents of the stack +and the contents of all of the registers, +without altering anything. This is a good command +to use if you are lost or want to figure out +what the effect of some command has been. +@end table + +@node Arithmetic, Stack Control, Printing Commands, Top +@chapter Arithmetic + +@table @samp +@item + +Pops two values off the stack, adds them, +and pushes the result. The precision of the result +is determined only by the values of the arguments, +and is enough to be exact. + +@item - +Pops two values, subtracts the first one popped +from the second one popped, and pushes the result. + +@item * +Pops two values, multiplies them, and pushes the result. +The number of fraction digits in the result is controlled +by the current precision flag (see below) and does not +depend on the values being multiplied. + +@item / +Pops two values, divides the second one popped from +the first one popped, and pushes the result. +The number of fraction digits is specified by the precision flag. + +@item % +Pops two values, computes the remainder of the division +that the @samp{/} command would do, and pushes that. +The division is done with as many fraction digits +as the precision flag specifies, and the remainder +is also computed with that many fraction digits. + +@item ^ +Pops two values and exponentiates, using the first +value popped as the exponent and the second popped as the base. +The fraction part of the exponent is ignored. +The precision flag specifies the number of fraction +digits in the result. + +@item v +Pops one value, computes its square root, and pushes that. +The precision flag specifies the number of fraction digits +in the result. +@end table + +Most arithmetic operations are affected by the "precision flag", +which you can set with the @samp{k} command. The default precision +value is zero, which means that all arithmetic except for +addition and subtraction produces integer results. + +The remainder operation (@samp{%}) requires some explanation: applied to +arguments @samp{a} and @samp{b} it produces @samp{a - (b * (a / b))}, +where @samp{a / b} is computed in the current precision. + +@node Stack Control, Registers, Arithmetic, Top +@chapter Stack Control + +@table @samp +@item c +Clears the stack, rendering it empty. + +@item d +Duplicates the value on the top of the stack, +pushing another copy of it. Thus, +`4d*p' computes 4 squared and prints it. +@end table + +@node Registers, Parameters, Stack Control, Top +@chapter Registers + +DC provides 128 memory registers, each named by a single +ASCII character. You can store a number in a register +and retrieve it later. + +@table @samp +@item s@var{r} +Pop the value off the top of the stack and store +it into register @var{r}. + +@item l@var{r} +Copy the value in register @var{r}, and push it onto +the stack. This does not alter the contents of @var{r}. + +Each register also contains its own stack. The current +register value is the top of the register's stack. + +@item S@var{r} +Pop the value off the top of the (main) stack and +push it onto the stack of register @var{r}. +The previous value of the register becomes inaccessible. + +@item L@var{r} +Pop the value off the top of register @var{r}'s stack +and push it onto the main stack. The previous value +in register @var{r}'s stack, if any, is now accessible +via the `l@var{r}' command. +@end table + +The @samp{f} command prints a list of all registers that have contents +stored in them, together with their contents. Only the +current contents of each register (the top of its stack) +is printed. + +@node Parameters, Strings, Registers, Top +@chapter Parameters + +DC has three parameters that control its operation: the precision, the +input radix, and the output radix. The precision specifies the number +of fraction digits to keep in the result of most arithmetic operations. +The input radix controls the interpretation of numbers typed in; +@emph{all} numbers typed in use this radix. The output radix is used +for printing numbers. + +The input and output radices are separate parameters; you can make them +unequal, which can be useful or confusing. Each radix must be between 2 +and 36 inclusive. The precision must be zero or greater. The precision +is always measured in decimal digits, regardless of the current input or +output radix. + +@table @samp +@item i +Pops the value off the top of the stack +and uses it to set the input radix. + +@item o +@itemx k +Similarly set the output radix and the precision. + +@item I +Pushes the current input radix on the stack. + +@item O +@itemx K +Similarly push the current output radix and the current precision. +@end table + +@node Strings, Status Inquiry, Parameters, Top +@chapter Strings + +DC can operate on strings as well as on numbers. The only things you +can do with strings are print them and execute them as macros (which +means that the contents of the string are processed as DC commands). +Both registers and the stack can hold strings, and DC always knows +whether any given object is a string or a number. Some commands such as +arithmetic operations demand numbers as arguments and print errors if +given strings. Other commands can accept either a number or a string; +for example, the @samp{p} command can accept either and prints the object +according to its type. + +@table @samp +@item [@var{characters}] +Makes a string containing @var{characters} and pushes it +on the stack. For example, @samp{[foo]P} prints the +characters @samp{foo} (with no newline). + +@item x +Pops a value off the stack and executes it as a macro. +Normally it should be a string; if it is a number, +it is simply pushed back onto the stack. +For example, @samp{[1p]x} executes the macro @samp{1p}, which +pushes 1 on the stack and prints @samp{1} on a separate line. + +Macros are most often stored in registers; +@samp{[1p]sa} stores a macro to print @samp{1} into register @samp{a}, +and @samp{lax} invokes the macro. + +@item >@var{r} +Pops two values off the stack and compares them +assuming they are numbers, executing the contents +of register @var{r} as a macro if the original top-of-stack +is greater. Thus, @samp{1 2>a} will invoke register @samp{a}'s contents +and @samp{2 1>a} will not. + +@item <@var{r} +Similar but invokes the macro if the original top-of-stack +is less. + +@item =@var{r} +Similar but invokes the macro if the two numbers popped +are equal. This can also be validly used to compare two +strings for equality. + +@item ? +Reads a line from the terminal and executes it. +This command allows a macro to request input from the user. + +@item q +During the execution of a macro, this comand +does not exit DC. Instead, it exits from that +macro and also from the macro which invoked it (if any). + +@item Q +Pops a value off the stack and uses it as a count +of levels of macro execution to be exited. Thus, +@samp{3Q} exits three levels. +@end table + +@node Status Inquiry, Notes, Strings, Top +@chapter Status Inquiry + +@table @samp +@item Z +Pops a value off the stack, calculates the number of +digits it has (or number of characters, if it is a string) +and pushes that number. + +@item X +Pops a value off the stack, calculates the number of +fraction digits it has, and pushes that number. +For a string, the value pushed is -1. + +@item z +Pushes the current stack depth; the number of +objects on the stack before the execution of the @samp{z} command. + +@item I +Pushes the current value of the input radix. + +@item O +Pushes the current value of the output radix. + +@item K +Pushes the current value of the precision. +@end table + +@node Notes, , Status Inquiry, Top +@chapter Notes + +The @samp{:} and @samp{;} commands of the Unix DC program are +not supported, as the documentation does not say what they do. +The @samp{!} command is not supported, but will be supported +as soon as a library for executing a line as a command exists. + +@contents +@bye diff --git a/gnu/usr.bin/dc/decimal.c b/gnu/usr.bin/dc/decimal.c new file mode 100644 index 000000000000..1fb95c18aa14 --- /dev/null +++ b/gnu/usr.bin/dc/decimal.c @@ -0,0 +1,1235 @@ +/* + * Arbitrary precision decimal arithmetic. + * + * Copyright (C) 1984 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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, you can either send email to this + * program's author (see below) or write to: The Free Software Foundation, + * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* Some known problems: + + Another problem with decimal_div is found when you try to + divide a number with > scale fraction digits by 1. The + expected result is simply truncation, but all sorts of things + happen instead. An example is that the result of .99999998/1 + with scale set to 6 is .000001 + + There are some problems in the behavior of the decimal package + related to printing and parsing. The + printer is weird about very large output radices, tending to want + to output single ASCII characters for any and all digits (even + in radices > 127). The UNIX bc approach is to print digit groups + separated by spaces. There is a rather overwrought workaround in + the function decputc() in bcmisc.c, but it would be better if + decimal.c got a fix for this. */ + +/* For stand-alone testing, compile with -DTEST. + This DTESTable feature defines a `main' function + which is a simple loop that accepts input of the form + number space op space number newline + where op is +, -, *, /, %, p or r, + and performs the operation and prints the operands and result. + `p' means print the first number in the radix spec'd by the second. + `r' means read the first one in the radix specified by the second + (and print the result in decimal). + Divide in this test keeps three fraction digits. */ + +#include "decimal.h" + +#define MAX(a, b) (((a) > (b) ? (a) : (b))) + +/* Some constant decimal numbers */ + +struct decimal decimal_zero = {0, 0, 0, 0, 0}; + +struct decimal decimal_one = {0, 0, 1, 0, 1}; + +/*** Assumes RADIX is even ***/ +struct decimal decimal_half = {0, 1, 0, 0, RADIX / 2}; + +decimal static decimal_add1 (), decimal_sub1 (); +static void add_scaled (); +static int subtract_scaled (); + +/* Create and return a decimal number that has `before' digits before + the decimal point and `after' digits after. The digits themselves are + initialized to zero. */ + +decimal +make_decimal (before, after) + int before, after; +{ + decimal result; + if (before >= 1<<16) + { + decimal_error ("%d too many decimal digits", before); + return 0; + } + if (after >= 1<<15) + { + decimal_error ("%d too many decimal digits", after); + return 0; + } + result = (decimal) malloc (sizeof (struct decimal) + before + after - 1); + result->sign = 0; + result->before = before; + result->after = after; + result->refcnt = 0; + bzero (result->contents, before + after); + return result; +} + +/* Create a copy of the decimal number `b' and return it. */ + +decimal +decimal_copy (b) + decimal b; +{ + decimal result = make_decimal (b->before, b->after); + bcopy (b->contents, result->contents, LENGTH(b)); + result->sign = b->sign; + return result; +} + +/* Copy a decimal number `b' but extend or truncate to exactly + `digits' fraction digits. */ + +static decimal +decimal_copy_1 (b, digits) + decimal b; + int digits; +{ + if (digits > b->after) + { + decimal result = make_decimal (b->before, digits); + bcopy (b->contents, result->contents + (digits - (int) b->after), LENGTH(b)); + return result; + } + else + return decimal_round_digits (b, digits); +} + +/* flush specified number `digits' of trailing fraction digits, + and flush any trailing fraction zero digits exposed after they are gone. + The number `b' is actually modified; no new storage is allocated. + That is why this is not global. */ + +static void +flush_trailing_digits (b, digits) + decimal b; + int digits; +{ + int flush = digits; + int maxdig = b->after; + + while (flush < maxdig && !b->contents [flush]) + flush++; + + if (flush) + { + int i; + + b->after -= flush; + for (i = 0; i < LENGTH (b); i++) + b->contents[i] = b->contents[flush + i]; + } + +} + +/* Return nonzero integer if the value of decimal number `b' is zero. */ + +int +decimal_zerop (b) + decimal b; +{ + return !LENGTH(b); +} + +/* Compare two decimal numbers arithmetically. + The value is < 0 if b1 < b2, > 0 if b1 > b2, 0 if b1 = b2. + This is the same way that `strcmp' reports the result of comparing + strings. */ + +int +decimal_compare (b1, b2) + decimal b1, b2; +{ + int l1, l2; + char *p1, *p2, *s1, *s2; + int i; + + /* If signs differ, deduce result from the signs */ + + if (b2->sign && !b1->sign) return 1; + if (b1->sign && !b2->sign) return -1; + + /* If same sign but number of nonfraction digits differs, + the one with more of them is farther from zero. */ + + if (b1->before != b2->before) + if (b1->sign) + return (int) (b2->before - b1->before); + else + return (int) (b1->before - b2->before); + + /* Else compare the numbers digit by digit from high end */ + l1 = LENGTH(b1); + l2 = LENGTH(b2); + s1 = b1->contents; /* Start of number -- don't back up digit pointer past here */ + s2 = b2->contents; + p1 = b1->contents + l1; /* Scanning pointer, for fetching digits. */ + p2 = b2->contents + l2; + for (i = MAX(l1, l2); i >= 0; i--) + { + int r = ((p1 != s1) ? *--p1 : 0) - ((p2 != s2) ? *--p2 : 0); + if (r) + return b1->sign ? -r : r; + } + return 0; +} + +/* Return the number of digits stored in decimal number `b' */ + +int +decimal_length (b) + decimal b; +{ + return LENGTH(b); +} + +/* Return the number of fraction digits stored in decimal number `b'. */ + +int +decimal_after (b) + decimal b; +{ + return b->after; +} + +/* Round decimal number `b' to have only `digits' fraction digits. + Result is rounded to nearest unit in the last remaining digit. + Return the result, another decimal number. */ + +decimal +decimal_round_digits (b, digits) + decimal b; + int digits; +{ + decimal result; + int old; + + if (b->after <= digits) return decimal_copy (b); + + if (digits < 0) + { + decimal_error ("request to keep negative number of digits %d", digits); + return decimal_copy (b); + } + + result = make_decimal (b->before + 1, b->after); + result->sign = b->sign; + bcopy (b->contents, result->contents, LENGTH(b)); + + old = result->after; + + /* Add .5 * last place to keep, so that we round rather than truncate */ + /* Note this ignores sign of result, so if result is negative + it is subtracting */ + + add_scaled (result, DECIMAL_HALF, 1, old - digits - 1); + + /* Flush desired digits, and any trailing zeros exposed by them. */ + + flush_trailing_digits (result, old - digits); + + /* Flush leading digits -- always is one, unless was a carry into it */ + + while (result->before > 0 + && result->contents[LENGTH(result) - 1] == 0) + result->before--; + + return result; +} + +/* Truncate decimal number `b' to have only `digits' fraction digits. + Any fraction digits in `b' beyond that are dropped and ignored. + Truncation is toward zero. + Return the result, another decimal number. */ + +decimal +decimal_trunc_digits (b, digits) + decimal b; + int digits; +{ + decimal result = decimal_copy (b); + int old = result->after; + + if (old <= digits) return result; + + if (digits < 0) + { + decimal_error ("request to keep negative number of digits %d", digits); + return result; + } + + flush_trailing_digits (result, old - digits); + + return result; +} + +/* Return the fractional part of decimal number `b': + that is, `b' - decimal_trunc_digits (`b') */ + +decimal +decimal_fraction (b) + decimal b; +{ + decimal result = make_decimal (0, b->after); + bcopy (b->contents, result->contents, b->after); + return result; +} + +/* return an integer whose value is that of decimal `b', sans its fraction. */ + +int +decimal_to_int (b) + decimal b; +{ + int result = 0; + int i; + int end = b->after; + + for (i = LENGTH(b) - 1; i >= end; i--) + { + result *= RADIX; + result += b->contents[i]; + } + return result; +} + +/* return a decimal whose value is the integer i. */ + +decimal +decimal_from_int (i) + int i; +{ + int log, tem; + decimal result; + + for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX); + + result = make_decimal (log, 0); + + for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX) + result->contents[log] = tem % RADIX; + + if (i < 0) result->sign = 1; + return result; +} + +/* Return (as an integer) the result of dividing decimal number `b' by + integer `divisor'. + This is used in printing decimal numbers in other radices. */ + +int +decimal_int_rem (b, divisor) + decimal b; + int divisor; +{ + int len = LENGTH(b); + int end = b->after; + int accum = 0; + int i; + + for (i = len - 1; i >= end; i--) + { + accum %= divisor; + accum *= RADIX; + accum += b->contents[i]; + } + return accum % divisor; +} + +/* Convert digit `digit' to a character and output it by calling + `charout' with it as arg. */ + +static void +print_digit (digit, charout) + int digit; + void (*charout) (); +{ + if (digit < 10) + charout ('0' + digit); + else + charout ('A' + digit - 10); +} + +/* print decimal number `b' in radix `radix', assuming it is an integer. + `r' is `radix' expressed as a decimal number. */ + +static +decimal_print_1 (b, r, radix, charout) + decimal b, r; + int radix; + void (*charout) (); +{ + int digit = decimal_int_rem (b, radix); + decimal rest = decimal_div (b, r, 0); + + if (!decimal_zerop (rest)) + decimal_print_1 (rest, r, radix, charout); + + print_digit (digit, charout); + + free (rest); +} + +/* User entry: print decimal number `b' in radix `radix' (an integer), + outputting characters by calling `charout'. */ + +void +decimal_print (b, charout, radix) + decimal b; + void (*charout) (); + int radix; +{ + if (b->sign) charout ('-'); + + if (radix == RADIX) + { + /* decimal output => just print the digits, inserting a point in + the proper place. */ + int i; + int before = b->before; + int len = before + b->after; + for (i = 0; i < len; i++) + { + if (i == before) charout ('.'); + /* Broken if RADIX /= 10 + charout ('0' + b->contents [len - 1 - i]); */ + print_digit (b->contents [len - 1 - i], charout); + } + if (!len) + charout ('0'); + } + else + { + /* nonstandard radix: must use multiply and divide to determine the + digits of the number in that radix. */ + + int i; + extern double log10 (); + /* Compute the number of fraction digits we want to have in the + new radix. They should contain the same amount of + information as the decimal digits we have. */ + int nfrac = (b->after / log10 ((double) radix) + .99); + decimal r = decimal_from_int (radix); + decimal intpart = decimal_trunc_digits (b, 0); + + /* print integer part */ + decimal_print_1 (intpart, r, radix, charout); + free (intpart); + + /* print fraction part */ + if (nfrac) + { + decimal tem1, tem2; + tem1 = decimal_fraction (b); + charout ('.'); + /* repeatedly multiply by `radix', print integer part as one digit, + and flush the integer part. */ + for (i = 0; i < nfrac; i++) + { + tem2 = decimal_mul (tem1, r); + free (tem1); + print_digit (decimal_to_int (tem2), charout); + tem1 = decimal_fraction (tem2); + free (tem2); + } + free (tem1); + } + free (r); + } +} + +static int +decode_digit (digitchar) + char digitchar; +{ + if ('0' <= digitchar && digitchar <= '9') + return digitchar - '0'; + if ('a' <= digitchar && digitchar <= 'z') + return digitchar - 'a' + 10; + if ('A' <= digitchar && digitchar <= 'Z') + return digitchar - 'A' + 10; + return -1; +} + +/* Parse string `s' into a number using radix `radix' + and return result as a decimal number. */ + +decimal +decimal_parse (s, radix) + char *s; + int radix; +{ + int i, len, before = -1; + char *p; + char c; + decimal result; + int negative = 0; + int excess_digit = 0; + + if (*s == '-') + { + s++; + negative = 1; + } + + /* First scan for valid characters. + Count total num digits, and count num before the decimal point. */ + + p = s; + i = 0; + while (c = *p++) + { + if (c == '.') + { + if (before >= 0) + decimal_error ("two decimal points in %s", s); + before = i; + } + else if (c == '0' && !i && before < 0) + s++; /* Discard leading zeros */ + else if (decode_digit (c) >= 0) + { + i++; + if (decode_digit (c) > RADIX) + excess_digit = 1; + } + else + decimal_error ("invalid number %s", s); + } + + len = i; + if (before < 0) before = i; + + p = s; + + /* Now parse those digits */ + + if (radix != RADIX || excess_digit) + { + decimal r = decimal_from_int (radix); + extern double log10 (); + int digits = (len - before) * log10 ((double) radix) + .99; + result = decimal_copy (DECIMAL_ZERO); + + /* Parse all the digits into an integer, ignoring decimal point, + by multiplying by `radix'. */ + + while (i > 0 && (c = *p++)) + { + if (c != '.') + { + decimal newdig = decimal_from_int (decode_digit (c)); + decimal prod = decimal_mul (result, r); + decimal newresult = decimal_add (newdig, prod); + + free (newdig); free (prod); free (result); + result = newresult; + i--; + } + } + + /* Now put decimal point in right place + by dividing by `radix' once for each digit + that really should have followed the decimal point. */ + + for (i = before; i < len; i++) + { + decimal newresult = decimal_div (result, r, digits); + free (result); + result = newresult; + } + free (r); + } + else + { + /* radix is standard - just copy the digits into a decimal number. */ + + int tem; + result = make_decimal (before, len - before); + + while (i > 0 && (c = *p++)) + { + if ((c != '.') && + ((tem = decode_digit (c)) >= 0)) + result->contents [--i] = tem; + } + } + + if (negative) result->sign = 1; + flush_trailing_digits (result, 0); + return result; +} + +/* Add b1 and b2, considering their signs */ + +decimal +decimal_add (b1, b2) + decimal b1, b2; +{ + decimal v; + + if (b1->sign != b2->sign) + v = decimal_sub1 (b1, b2); + else + v = decimal_add1 (b1, b2); + if (b1->sign && !decimal_zerop (v)) + v->sign = !v->sign; + return v; +} + +/* Add b1 and minus b2, considering their signs */ + +decimal +decimal_sub (b1, b2) + decimal b1, b2; +{ + decimal v; + + if (b1->sign != b2->sign) + v = decimal_add1 (b1, b2); + else + v = decimal_sub1 (b1, b2); + if (b1->sign && !decimal_zerop (v)) + v->sign = !v->sign; + return v; +} + +/* Return the negation of b2. */ + +decimal +decimal_neg (b2) + decimal b2; +{ + decimal v = decimal_copy (b2); + + if (!decimal_zerop (v)) + v->sign = !v->sign; + return v; +} + +/* add magnitudes of b1 and b2, ignoring their signs. */ + +static decimal +decimal_add1 (b1, b2) + decimal b1, b2; +{ + int before = MAX (b1->before, b2->before); + int after = MAX (b1->after, b2->after); + + int len = before+after+1; + decimal result = make_decimal (before+1, after); + + int i; + char *s1 = b1->contents; + char *s2 = b2->contents; + char *p1 = s1 + b1->after - after; + char *p2 = s2 + b2->after - after; + char *e1 = s1 + b1->before + b1->after; + char *e2 = s2 + b2->before + b2->after; + char *pr = result->contents; + int accum = 0; + + for (i = 0; i < len; i++, p1++, p2++) + { + accum /= RADIX; + if (p1 >= s1 && p1 < e1) accum += *p1; + if (p2 >= s2 && p2 < e2) accum += *p2; + *pr++ = accum % RADIX; + } + if (!accum) + (result->before)--; + + flush_trailing_digits (result, 0); + + return result; +} + +/* subtract magnitude of b2 from that or b1, returning signed decimal + number. */ + +static decimal +decimal_sub1 (b1, b2) + decimal b1, b2; +{ + int before = MAX (b1->before, b2->before); + int after = MAX (b1->after, b2->after); + + int len = before+after; + decimal result = make_decimal (before, after); + + int i; + char *s1 = b1->contents; + char *s2 = b2->contents; + char *p1 = s1 + b1->after - after; + char *p2 = s2 + b2->after - after; + char *e1 = s1 + b1->before + b1->after; + char *e2 = s2 + b2->before + b2->after; + char *pr = result->contents; + int accum = 0; + + for (i = 0; i < len; i++, p1++, p2++) + { + if (p1 >= s1 && p1 < e1) accum += *p1; + if (p2 >= s2 && p2 < e2) accum -= *p2; + if (accum < 0 && accum % RADIX) + *pr = RADIX - (- accum) % RADIX; + else + *pr = accum % RADIX; + accum -= *pr++; + accum /= RADIX; + } + + /* If result is negative, subtract it from RADIX**length + so that we get the right digits for sign-magnitude + rather than RADIX-complement */ + + if (accum) + { + result->sign = 1; + pr = result->contents; + accum = 0; + for (i = 0; i < len; i++) + { + accum -= *pr; + if (accum) + *pr = accum + RADIX; + else + *pr = 0; + accum -= *pr++; + accum /= RADIX; + } + } + + /* flush leading nonfraction zero digits */ + + while (result->before && *--pr == 0) + (result->before)--; + + flush_trailing_digits (result, 0); + + return result; +} + +/* multiply b1 and b2 keeping `digits' fraction digits */ + +decimal +decimal_mul_rounded (b1, b2, digits) + decimal b1, b2; + int digits; +{ + decimal tem = decimal_mul (b1, b2); + decimal result = decimal_round_digits (tem, digits); + free (tem); + return result; +} + +/* multiply b1 and b2 keeping the right number of fraction digits + for the `dc' program with precision = `digits'. */ + +decimal +decimal_mul_dc (b1, b2, digits) + decimal b1, b2; + int digits; +{ + decimal tem = decimal_mul (b1, b2); + decimal result + = decimal_round_digits (tem, MAX (digits, MAX (b1->after, b2->after))); + free (tem); + return result; +} + +/* multiply b1 and b2 as decimal error-free values; + keep LENGTH(b1) plus LENGTH(b2) significant figures. */ + +decimal +decimal_mul (b1, b2) + decimal b1, b2; +{ + decimal result = make_decimal (b1->before + b2->before, b1->after + b2->after); + int i; + int length2 = LENGTH(b2); + char *pr; + + for (i = 0; i < length2; i++) + add_scaled (result, b1, b2->contents[i], i); + + /* flush leading nonfraction zero digits */ + + pr = result->contents + LENGTH(result); + while (result->before && *--pr == 0) + (result->before)--; + + flush_trailing_digits (result, 0); /* flush trailing zeros */ + + /* Set sign properly */ + + if (b1->sign != b2->sign && LENGTH(result)) + result->sign = 1; + + return result; +} + +/* Modify decimal number `into' by adding `from', + multiplied by `factor' (which should be nonnegative and less than RADIX) + and shifted left `scale' digits at the least significant end. */ + +static void +add_scaled (into, from, factor, scale) + decimal into, from; + int factor, scale; +{ + char *pf = from->contents; + char *pi = into->contents + scale; + int lengthf = LENGTH(from); + int lengthi = LENGTH(into) - scale; + + int accum = 0; + int i; + + for (i = 0; i < lengthi; i++) + { + accum /= RADIX; + if (i < lengthf) + accum += *pf++ * factor; + accum += *pi; + *pi++ = accum % RADIX; + } +} + +/* Divide decimal number `b1' by `b2', keeping at most `digits' + fraction digits. + Returns the result as a decimal number. + + When division is not exact, the quotient is truncated toward zero. */ + +decimal +decimal_div (b1, b2, digits) + decimal b1, b2; + int digits; +{ + decimal result = make_decimal (MAX(1, (int) (1 + b1->before - b2->before)), digits); + + /* b1copy holds what is left of the dividend, + that is not accounted for by the quotient digits already known */ + + decimal b1copy = decimal_copy_1 (b1, b2->after + digits); + int length1 = LENGTH(b1copy); + int length2 = LENGTH(b2); + int lengthr = LENGTH(result); + int i; + + /* leading_divisor_digits contains the first two divisor digits, as + an integer */ + + int leading_divisor_digits = b2->contents[length2-1]*RADIX; + if (length2 > 1) + leading_divisor_digits += b2->contents[length2-2]; + + if (decimal_zerop (b2)) + { + decimal_error ("divisor is zero", 0); + return decimal_copy (DECIMAL_ZERO); + } + + if (lengthr <= (length1 - length2)) + abort(); /* My reasoning says this cannot happen, I hope */ + + for (i = length1 - length2; i >= 0; i--) + { + /* Guess the next quotient digit (in order of decreasing significance) + using integer division */ + + int guess; + int trial_dividend = b1copy->contents[length2+i-1]*RADIX; + if (i != length1 - length2) + trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX; + if (length2 + i > 1) + trial_dividend += b1copy->contents[length2+i-2]; + + guess = trial_dividend / leading_divisor_digits; + + /* Remove the quotient times this digit from the dividend left */ + /* We may find that the quotient digit is too large, + when we consider the entire divisor. + Then we decrement the quotient digit and add the divisor back in */ + + if (guess && 0 > subtract_scaled (b1copy, b2, guess, i)) + { + guess--; + add_scaled (b1copy, b2, 1, i); + } + + if (guess >= RADIX) + { + result->contents[i + 1] += guess / RADIX; + guess %= RADIX; + } + result->contents[i] = guess; + } + + free (b1copy); + + result->sign = (b1->sign != b2->sign); + + /* flush leading nonfraction zero digits */ + + { + char *pr = result->contents + lengthr; + while (result->before && *--pr == 0) + (result->before)--; + } + + flush_trailing_digits (result, 0); /* Flush trailing zero fraction digits */ + + return result; +} + +/* The remainder for the above division. + Same as `b1' - (`b1' / `b2') * 'b2'. + Note that the value depends on the number of fraction digits + that were kept in computing `b1' / `b2'; + the argument `digits' specifies this. + + The remainder has the same sign as the dividend. + The divisor's sign is ignored. */ + +decimal +decimal_rem (b1, b2, digits) + decimal b1, b2; + int digits; +{ + decimal b1copy = decimal_copy_1 (b1, b2->after + digits); + int length1 = LENGTH(b1copy); + int length2 = LENGTH(b2); + int i; + + int leading_divisor_digits = b2->contents[length2-1]*RADIX; + + if (length2 > 1) + leading_divisor_digits += b2->contents[length2-2]; + + if (decimal_zerop (b2)) + { + decimal_error ("divisor is zero", 0); + return decimal_copy (DECIMAL_ZERO); + } + + /* Do like division, above, but throw away the quotient. + Keep only the final `rest of dividend', which becomes the remainder. */ + + for (i = length1 - length2; i >= 0; i--) + { + int guess; + int trial_dividend = b1copy->contents[length2+i-1]*RADIX; + if (i != length1 - length2) + trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX; + if (length2 + i > 1) + trial_dividend += b1copy->contents[length2+i-2]; + + guess = trial_dividend / leading_divisor_digits; + + if (guess && 0 > subtract_scaled (b1copy, b2, guess, i)) + { + guess--; + add_scaled (b1copy, b2, 1, i); + } + /* No need to check whether guess exceeds RADIX + since we are not saving guess. */ + } + + /* flush leading nonfraction zero digits */ + + { + char *pr = b1copy->contents + length1; + while (b1copy->before && *--pr == 0) + (b1copy->before)--; + } + + flush_trailing_digits (b1copy, 0); + return b1copy; +} + +/* returns negative number if we chose factor too large */ + +static int +subtract_scaled (into, from, factor, scale) + decimal into, from; + int factor, scale; +{ + char *pf = from->contents; + char *pi = into->contents + scale; + int lengthf = LENGTH(from); + int lengthi = LENGTH(into) - scale; + int accum = 0; + int i; + + for (i = 0; i < lengthi && i <= lengthf; i++) + { + if (i < lengthf) + accum -= *pf++ * factor; + accum += *pi; + if (accum < 0 && accum % RADIX) + *pi = RADIX - (- accum) % RADIX; + else + *pi = accum % RADIX; + accum -= *pi++; + accum /= RADIX; + } + return accum; +} + +/* Return the square root of decimal number D, using Newton's method. + Number of fraction digits returned is max of FRAC_DIGITS + and D's number of fraction digits. */ + +decimal +decimal_sqrt (d, frac_digits) + decimal d; + int frac_digits; +{ + decimal guess; + int notdone = 1; + + if (decimal_zerop (d)) return d; + if (d->sign) + { + decimal_error ("square root argument negative", 0); + return decimal_copy (DECIMAL_ZERO); + } + + frac_digits = MAX (frac_digits, d->after); + + /* Compute an initial guess by taking the square root + of a nearby power of RADIX. */ + + if (d->before) + { + guess = make_decimal ((d->before + 1) / 2, 0); + guess->contents[guess->before - 1] = 1; + } + else + { + /* Arg is less than 1; compute nearest power of RADIX */ + char *p = d->contents + LENGTH(d); + char *sp = p; + + while (!*--p); /* Find most significant nonzero digit */ + if (sp - p == 1) + { + /* Arg is bigger than 1/RADIX; use 1 as a guess */ + guess = decimal_copy (DECIMAL_ONE); + } + else + { + guess = make_decimal (0, (sp - p) / 2); + guess->contents[0] = 1; + } + } + + /* Iterate doing guess = (guess + d/guess) / 2 */ + + while (notdone) + { + decimal tem1 = decimal_div (d, guess, frac_digits + 1); + decimal tem2 = decimal_add (guess, tem1); + decimal tem3 = decimal_mul_rounded (tem2, DECIMAL_HALF, frac_digits); + notdone = decimal_compare (guess, tem3); + free (tem1); + free (tem2); + free (guess); + guess = tem3; + if (decimal_zerop (guess)) return guess; /* Avoid divide-by-zero */ + } + + return guess; +} + +/* Raise decimal number `base' to power of integer part of decimal + number `expt'. + This function depends on using radix 10. + It is too hard to write it to work for any value of RADIX, + so instead it is simply not available if RADIX is not ten. */ + +#if !(RADIX - 10) + +decimal +decimal_expt (base, expt, frac_digits) + decimal base, expt; + int frac_digits; +{ + decimal accum = decimal_copy (DECIMAL_ONE); + decimal basis1 = base; + int digits = expt->before; + int dig = 0; /* Expt digit being processed */ + + if (expt->sign) + /* If negative power, take reciprocal first thing + so that fraction digit truncation won't destroy + what will ultimately be nonfraction digits. */ + basis1 = decimal_div (DECIMAL_ONE, base, frac_digits); + while (dig < digits) + { + decimal basis2, basis4, basis8, basis10; + int thisdigit = expt->contents[expt->after + dig]; + + /* Compute factors to multiply in for each bit of this digit */ + + basis2 = decimal_mul_rounded (basis1, basis1, frac_digits); + basis4 = decimal_mul_rounded (basis2, basis2, frac_digits); + basis8 = decimal_mul_rounded (basis4, basis4, frac_digits); + + /* Now accumulate the factors this digit value selects */ + + if (thisdigit & 1) + { + decimal accum1 = decimal_mul_rounded (accum, basis1, frac_digits); + free (accum); + accum = accum1; + } + + if (thisdigit & 2) + { + decimal accum1 = decimal_mul_rounded (accum, basis2, frac_digits); + free (accum); + accum = accum1; + } + + if (thisdigit & 4) + { + decimal accum1 = decimal_mul_rounded (accum, basis4, frac_digits); + free (accum); + accum = accum1; + } + + if (thisdigit & 8) + { + decimal accum1 = decimal_mul_rounded (accum, basis8, frac_digits); + free (accum); + accum = accum1; + } + + /* If there are further digits, compute the basis1 for the next digit */ + + if (++dig < digits) + basis10 = decimal_mul_rounded (basis2, basis8, frac_digits); + + /* Free intermediate results */ + + if (basis1 != base) free (basis1); + free (basis2); + free (basis4); + free (basis8); + basis1 = basis10; + } + return accum; +} +#endif + +#ifdef TEST + +fputchar (c) + char c; +{ + putchar (c); +} + +/* Top level that can be used to test the arithmetic functions */ + +main () +{ + char s1[40], s2[40]; + decimal b1, b2, b3; + char c; + + while (1) + { + scanf ("%s %c %s", s1, &c, s2); + b1 = decimal_parse (s1, RADIX); + b2 = decimal_parse (s2, RADIX); + switch (c) + { + default: + c = '+'; + case '+': + b3 = decimal_add (b1, b2); + break; + case '*': + b3 = decimal_mul (b1, b2); + break; + case '/': + b3 = decimal_div (b1, b2, 3); + break; + case '%': + b3 = decimal_rem (b1, b2, 3); + break; + case 'p': + decimal_print (b1, fputchar, RADIX); + printf (" printed in base %d is ", decimal_to_int (b2)); + decimal_print (b1, fputchar, decimal_to_int (b2)); + printf ("\n"); + continue; + case 'r': + printf ("%s read in base %d is ", s1, decimal_to_int (b2)); + decimal_print (decimal_parse (s1, decimal_to_int (b2)), fputchar, RADIX); + printf ("\n"); + continue; + } + decimal_print (b1, fputchar, RADIX); + printf (" %c ", c); + decimal_print (b2, fputchar, RADIX); + printf (" = "); + decimal_print (b3, fputchar, RADIX); + printf ("\n"); + } +} + +decimal_error (s1, s2) + char *s1, *s2; +{ + printf ("\n"); + printf (s1, s2); + printf ("\n"); +} + +static void +pbi (b) + int b; +{ + decimal_print ((decimal) b, fputchar, RADIX); +} + +static void +pb (b) + decimal b; +{ + decimal_print (b, fputchar, RADIX); +} + +#endif diff --git a/gnu/usr.bin/dc/decimal.h b/gnu/usr.bin/dc/decimal.h new file mode 100644 index 000000000000..2b41158166a2 --- /dev/null +++ b/gnu/usr.bin/dc/decimal.h @@ -0,0 +1,93 @@ +/* + * Header file for decimal.c (arbitrary precision decimal arithmetic) + * + * Copyright (C) 1984 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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, you can either send email to this + * program's author (see below) or write to: The Free Software Foundation, + * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* Autoconf stuff */ +#ifndef HAVE_BCOPY +#undef bcopy +#define bcopy(s2, s1, n) memcpy (s1, s2, n) +#endif + +#ifndef HAVE_BZERO +#undef bzero +#define bzero(b, l) memset (b, 0, l) +#endif + +/* Define the radix to use by default, and for representing the + numbers internally. This does not need to be decimal; that is just + the default for it. */ + +/* Currently, this is required to be even for this program to work. */ + +#ifndef RADIX +#define RADIX 10 +#endif + +/* The user must define the external function `decimal_error' + which is called with two arguments to report errors in this package. + The two arguments may be passed to `printf' to print a message. */ + +/* Structure that represents a decimal number */ + +struct decimal +{ + unsigned int sign: 1; /* One for negative number */ + /* The sign should always be zero for the number 0 */ + int after: 15; /* number of fraction digits */ + unsigned short before; /* number of non-fraction digits */ + unsigned short refcnt; /* number of pointers to this number */ + /* (used by calling program) */ + char contents[1]; /* the digits themselves, least significant first. */ + /* digits are just numbers 0 .. RADIX-1 */ +}; + +/* There may never be leading nonfraction zeros or trailing fraction + zeros in a number. They must be removed by all the arithmetic + functions. Therefore, the number zero always has no digits stored. */ + +typedef struct decimal *decimal; + +/* Decimal numbers are always passed around as pointers. + All the external entries in this file allocate new numbers + using `malloc' to store values in. + They never modify their arguments or any existing numbers. */ + +/* Return the total number of digits stored in the number `b' */ +#define LENGTH(b) ((b)->before + (b)->after) + +/* Some constant decimal numbers */ + + +#define DECIMAL_ZERO &decimal_zero + + +#define DECIMAL_ONE &decimal_one + +#define DECIMAL_HALF &decimal_half + +decimal decimal_add (), decimal_sub (), decimal_mul (), decimal_div (); +decimal decimal_mul_dc (), decimal_mul_rounded (), decimal_rem (); +decimal decimal_round_digits (); +decimal make_decimal (), decimal_copy (), decimal_parse (); +decimal decimal_sqrt (), decimal_expt (); + +void decimal_print (); + +/* End of decimal.h */ |