Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..555e30c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.lib.*
+cmake-build-*
+.idea
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..ba4a81d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.13)
+
+project(units C)
+
+file(GLOB SOURCES *.c *.h)
+add_executable(units-host ${SOURCES})
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <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 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..421f9b4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -Os -DSUPPORT_UTF8 -sASYNCIFY -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,stringToNewUTF8 \
+	-sEXPORTED_FUNCTIONS=_test_int,_do_a_conversion -sMODULARIZE -s 'EXPORT_NAME="createMyModule"' \
+	--pre-js=module-pre.js
+
+units.wasm: *.c *.h
+	emcc -o units.lib.js *.c $(CFLAGS) --preload-file definitions.units --preload-file elements.units --preload-file currency.units --preload-file cpi.units
+
+units-host:
+	gcc *.c -o units-host -g
+
+.PHONY: units.wasm units-host
diff --git a/README b/README
new file mode 100644
index 0000000..2af845a
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+GNU Units port to WebAssembly
+
+Run make to build. Emscripten is needed.
diff --git a/cpi.units b/cpi.units
new file mode 100644
index 0000000..0852312
--- /dev/null
+++ b/cpi.units
@@ -0,0 +1,1349 @@
+!message Consumer price index data from US BLS, 2024-02-18
+
+UScpi[1] noerror \
+          1913.0833333333333 9.8 \
+          1913.1666666666667 9.8 \
+          1913.25 9.8 \
+          1913.3333333333333 9.8 \
+          1913.4166666666667 9.7 \
+          1913.5 9.8 \
+          1913.5833333333333 9.9 \
+          1913.6666666666667 9.9 \
+          1913.75 10.0 \
+          1913.8333333333333 10.0 \
+          1913.9166666666667 10.1 \
+          1914.0 10.0 \
+          1914.0833333333333 10.0 \
+          1914.1666666666667 9.9 \
+          1914.25 9.9 \
+          1914.3333333333333 9.8 \
+          1914.4166666666667 9.9 \
+          1914.5 9.9 \
+          1914.5833333333333 10.0 \
+          1914.6666666666667 10.2 \
+          1914.75 10.2 \
+          1914.8333333333333 10.1 \
+          1914.9166666666667 10.2 \
+          1915.0 10.1 \
+          1915.0833333333333 10.1 \
+          1915.1666666666667 10.0 \
+          1915.25 9.9 \
+          1915.3333333333333 10.0 \
+          1915.4166666666667 10.1 \
+          1915.5 10.1 \
+          1915.5833333333333 10.1 \
+          1915.6666666666667 10.1 \
+          1915.75 10.1 \
+          1915.8333333333333 10.2 \
+          1915.9166666666667 10.3 \
+          1916.0 10.3 \
+          1916.0833333333333 10.4 \
+          1916.1666666666667 10.4 \
+          1916.25 10.5 \
+          1916.3333333333333 10.6 \
+          1916.4166666666667 10.7 \
+          1916.5 10.8 \
+          1916.5833333333333 10.8 \
+          1916.6666666666667 10.9 \
+          1916.75 11.1 \
+          1916.8333333333333 11.3 \
+          1916.9166666666667 11.5 \
+          1917.0 11.6 \
+          1917.0833333333333 11.7 \
+          1917.1666666666667 12.0 \
+          1917.25 12.0 \
+          1917.3333333333333 12.6 \
+          1917.4166666666667 12.8 \
+          1917.5 13.0 \
+          1917.5833333333333 12.8 \
+          1917.6666666666667 13.0 \
+          1917.75 13.3 \
+          1917.8333333333333 13.5 \
+          1917.9166666666667 13.5 \
+          1918.0 13.7 \
+          1918.0833333333333 14.0 \
+          1918.1666666666667 14.1 \
+          1918.25 14.0 \
+          1918.3333333333333 14.2 \
+          1918.4166666666667 14.5 \
+          1918.5 14.7 \
+          1918.5833333333333 15.1 \
+          1918.6666666666667 15.4 \
+          1918.75 15.7 \
+          1918.8333333333333 16.0 \
+          1918.9166666666667 16.3 \
+          1919.0 16.5 \
+          1919.0833333333333 16.5 \
+          1919.1666666666667 16.2 \
+          1919.25 16.4 \
+          1919.3333333333333 16.7 \
+          1919.4166666666667 16.9 \
+          1919.5 16.9 \
+          1919.5833333333333 17.4 \
+          1919.6666666666667 17.7 \
+          1919.75 17.8 \
+          1919.8333333333333 18.1 \
+          1919.9166666666667 18.5 \
+          1920.0 18.9 \
+          1920.0833333333333 19.3 \
+          1920.1666666666667 19.5 \
+          1920.25 19.7 \
+          1920.3333333333333 20.3 \
+          1920.4166666666667 20.6 \
+          1920.5 20.9 \
+          1920.5833333333333 20.8 \
+          1920.6666666666667 20.3 \
+          1920.75 20.0 \
+          1920.8333333333333 19.9 \
+          1920.9166666666667 19.8 \
+          1921.0 19.4 \
+          1921.0833333333333 19.0 \
+          1921.1666666666667 18.4 \
+          1921.25 18.3 \
+          1921.3333333333333 18.1 \
+          1921.4166666666667 17.7 \
+          1921.5 17.6 \
+          1921.5833333333333 17.7 \
+          1921.6666666666667 17.7 \
+          1921.75 17.5 \
+          1921.8333333333333 17.5 \
+          1921.9166666666667 17.4 \
+          1922.0 17.3 \
+          1922.0833333333333 16.9 \
+          1922.1666666666667 16.9 \
+          1922.25 16.7 \
+          1922.3333333333333 16.7 \
+          1922.4166666666667 16.7 \
+          1922.5 16.7 \
+          1922.5833333333333 16.8 \
+          1922.6666666666667 16.6 \
+          1922.75 16.6 \
+          1922.8333333333333 16.7 \
+          1922.9166666666667 16.8 \
+          1923.0 16.9 \
+          1923.0833333333333 16.8 \
+          1923.1666666666667 16.8 \
+          1923.25 16.8 \
+          1923.3333333333333 16.9 \
+          1923.4166666666667 16.9 \
+          1923.5 17.0 \
+          1923.5833333333333 17.2 \
+          1923.6666666666667 17.1 \
+          1923.75 17.2 \
+          1923.8333333333333 17.3 \
+          1923.9166666666667 17.3 \
+          1924.0 17.3 \
+          1924.0833333333333 17.3 \
+          1924.1666666666667 17.2 \
+          1924.25 17.1 \
+          1924.3333333333333 17.0 \
+          1924.4166666666667 17.0 \
+          1924.5 17.0 \
+          1924.5833333333333 17.1 \
+          1924.6666666666667 17.0 \
+          1924.75 17.1 \
+          1924.8333333333333 17.2 \
+          1924.9166666666667 17.2 \
+          1925.0 17.3 \
+          1925.0833333333333 17.3 \
+          1925.1666666666667 17.2 \
+          1925.25 17.3 \
+          1925.3333333333333 17.2 \
+          1925.4166666666667 17.3 \
+          1925.5 17.5 \
+          1925.5833333333333 17.7 \
+          1925.6666666666667 17.7 \
+          1925.75 17.7 \
+          1925.8333333333333 17.7 \
+          1925.9166666666667 18.0 \
+          1926.0 17.9 \
+          1926.0833333333333 17.9 \
+          1926.1666666666667 17.9 \
+          1926.25 17.8 \
+          1926.3333333333333 17.9 \
+          1926.4166666666667 17.8 \
+          1926.5 17.7 \
+          1926.5833333333333 17.5 \
+          1926.6666666666667 17.4 \
+          1926.75 17.5 \
+          1926.8333333333333 17.6 \
+          1926.9166666666667 17.7 \
+          1927.0 17.7 \
+          1927.0833333333333 17.5 \
+          1927.1666666666667 17.4 \
+          1927.25 17.3 \
+          1927.3333333333333 17.3 \
+          1927.4166666666667 17.4 \
+          1927.5 17.6 \
+          1927.5833333333333 17.3 \
+          1927.6666666666667 17.2 \
+          1927.75 17.3 \
+          1927.8333333333333 17.4 \
+          1927.9166666666667 17.3 \
+          1928.0 17.3 \
+          1928.0833333333333 17.3 \
+          1928.1666666666667 17.1 \
+          1928.25 17.1 \
+          1928.3333333333333 17.1 \
+          1928.4166666666667 17.2 \
+          1928.5 17.1 \
+          1928.5833333333333 17.1 \
+          1928.6666666666667 17.1 \
+          1928.75 17.3 \
+          1928.8333333333333 17.2 \
+          1928.9166666666667 17.2 \
+          1929.0 17.1 \
+          1929.0833333333333 17.1 \
+          1929.1666666666667 17.1 \
+          1929.25 17.0 \
+          1929.3333333333333 16.9 \
+          1929.4166666666667 17.0 \
+          1929.5 17.1 \
+          1929.5833333333333 17.3 \
+          1929.6666666666667 17.3 \
+          1929.75 17.3 \
+          1929.8333333333333 17.3 \
+          1929.9166666666667 17.3 \
+          1930.0 17.2 \
+          1930.0833333333333 17.1 \
+          1930.1666666666667 17.0 \
+          1930.25 16.9 \
+          1930.3333333333333 17.0 \
+          1930.4166666666667 16.9 \
+          1930.5 16.8 \
+          1930.5833333333333 16.6 \
+          1930.6666666666667 16.5 \
+          1930.75 16.6 \
+          1930.8333333333333 16.5 \
+          1930.9166666666667 16.4 \
+          1931.0 16.1 \
+          1931.0833333333333 15.9 \
+          1931.1666666666667 15.7 \
+          1931.25 15.6 \
+          1931.3333333333333 15.5 \
+          1931.4166666666667 15.3 \
+          1931.5 15.1 \
+          1931.5833333333333 15.1 \
+          1931.6666666666667 15.1 \
+          1931.75 15.0 \
+          1931.8333333333333 14.9 \
+          1931.9166666666667 14.7 \
+          1932.0 14.6 \
+          1932.0833333333333 14.3 \
+          1932.1666666666667 14.1 \
+          1932.25 14.0 \
+          1932.3333333333333 13.9 \
+          1932.4166666666667 13.7 \
+          1932.5 13.6 \
+          1932.5833333333333 13.6 \
+          1932.6666666666667 13.5 \
+          1932.75 13.4 \
+          1932.8333333333333 13.3 \
+          1932.9166666666667 13.2 \
+          1933.0 13.1 \
+          1933.0833333333333 12.9 \
+          1933.1666666666667 12.7 \
+          1933.25 12.6 \
+          1933.3333333333333 12.6 \
+          1933.4166666666667 12.6 \
+          1933.5 12.7 \
+          1933.5833333333333 13.1 \
+          1933.6666666666667 13.2 \
+          1933.75 13.2 \
+          1933.8333333333333 13.2 \
+          1933.9166666666667 13.2 \
+          1934.0 13.2 \
+          1934.0833333333333 13.2 \
+          1934.1666666666667 13.3 \
+          1934.25 13.3 \
+          1934.3333333333333 13.3 \
+          1934.4166666666667 13.3 \
+          1934.5 13.4 \
+          1934.5833333333333 13.4 \
+          1934.6666666666667 13.4 \
+          1934.75 13.6 \
+          1934.8333333333333 13.5 \
+          1934.9166666666667 13.5 \
+          1935.0 13.4 \
+          1935.0833333333333 13.6 \
+          1935.1666666666667 13.7 \
+          1935.25 13.7 \
+          1935.3333333333333 13.8 \
+          1935.4166666666667 13.8 \
+          1935.5 13.7 \
+          1935.5833333333333 13.7 \
+          1935.6666666666667 13.7 \
+          1935.75 13.7 \
+          1935.8333333333333 13.7 \
+          1935.9166666666667 13.8 \
+          1936.0 13.8 \
+          1936.0833333333333 13.8 \
+          1936.1666666666667 13.8 \
+          1936.25 13.7 \
+          1936.3333333333333 13.7 \
+          1936.4166666666667 13.7 \
+          1936.5 13.8 \
+          1936.5833333333333 13.9 \
+          1936.6666666666667 14.0 \
+          1936.75 14.0 \
+          1936.8333333333333 14.0 \
+          1936.9166666666667 14.0 \
+          1937.0 14.0 \
+          1937.0833333333333 14.1 \
+          1937.1666666666667 14.1 \
+          1937.25 14.2 \
+          1937.3333333333333 14.3 \
+          1937.4166666666667 14.4 \
+          1937.5 14.4 \
+          1937.5833333333333 14.5 \
+          1937.6666666666667 14.5 \
+          1937.75 14.6 \
+          1937.8333333333333 14.6 \
+          1937.9166666666667 14.5 \
+          1938.0 14.4 \
+          1938.0833333333333 14.2 \
+          1938.1666666666667 14.1 \
+          1938.25 14.1 \
+          1938.3333333333333 14.2 \
+          1938.4166666666667 14.1 \
+          1938.5 14.1 \
+          1938.5833333333333 14.1 \
+          1938.6666666666667 14.1 \
+          1938.75 14.1 \
+          1938.8333333333333 14.0 \
+          1938.9166666666667 14.0 \
+          1939.0 14.0 \
+          1939.0833333333333 14.0 \
+          1939.1666666666667 13.9 \
+          1939.25 13.9 \
+          1939.3333333333333 13.8 \
+          1939.4166666666667 13.8 \
+          1939.5 13.8 \
+          1939.5833333333333 13.8 \
+          1939.6666666666667 13.8 \
+          1939.75 14.1 \
+          1939.8333333333333 14.0 \
+          1939.9166666666667 14.0 \
+          1940.0 14.0 \
+          1940.0833333333333 13.9 \
+          1940.1666666666667 14.0 \
+          1940.25 14.0 \
+          1940.3333333333333 14.0 \
+          1940.4166666666667 14.0 \
+          1940.5 14.1 \
+          1940.5833333333333 14.0 \
+          1940.6666666666667 14.0 \
+          1940.75 14.0 \
+          1940.8333333333333 14.0 \
+          1940.9166666666667 14.0 \
+          1941.0 14.1 \
+          1941.0833333333333 14.1 \
+          1941.1666666666667 14.1 \
+          1941.25 14.2 \
+          1941.3333333333333 14.3 \
+          1941.4166666666667 14.4 \
+          1941.5 14.7 \
+          1941.5833333333333 14.7 \
+          1941.6666666666667 14.9 \
+          1941.75 15.1 \
+          1941.8333333333333 15.3 \
+          1941.9166666666667 15.4 \
+          1942.0 15.5 \
+          1942.0833333333333 15.7 \
+          1942.1666666666667 15.8 \
+          1942.25 16.0 \
+          1942.3333333333333 16.1 \
+          1942.4166666666667 16.3 \
+          1942.5 16.3 \
+          1942.5833333333333 16.4 \
+          1942.6666666666667 16.5 \
+          1942.75 16.5 \
+          1942.8333333333333 16.7 \
+          1942.9166666666667 16.8 \
+          1943.0 16.9 \
+          1943.0833333333333 16.9 \
+          1943.1666666666667 16.9 \
+          1943.25 17.2 \
+          1943.3333333333333 17.4 \
+          1943.4166666666667 17.5 \
+          1943.5 17.5 \
+          1943.5833333333333 17.4 \
+          1943.6666666666667 17.3 \
+          1943.75 17.4 \
+          1943.8333333333333 17.4 \
+          1943.9166666666667 17.4 \
+          1944.0 17.4 \
+          1944.0833333333333 17.4 \
+          1944.1666666666667 17.4 \
+          1944.25 17.4 \
+          1944.3333333333333 17.5 \
+          1944.4166666666667 17.5 \
+          1944.5 17.6 \
+          1944.5833333333333 17.7 \
+          1944.6666666666667 17.7 \
+          1944.75 17.7 \
+          1944.8333333333333 17.7 \
+          1944.9166666666667 17.7 \
+          1945.0 17.8 \
+          1945.0833333333333 17.8 \
+          1945.1666666666667 17.8 \
+          1945.25 17.8 \
+          1945.3333333333333 17.8 \
+          1945.4166666666667 17.9 \
+          1945.5 18.1 \
+          1945.5833333333333 18.1 \
+          1945.6666666666667 18.1 \
+          1945.75 18.1 \
+          1945.8333333333333 18.1 \
+          1945.9166666666667 18.1 \
+          1946.0 18.2 \
+          1946.0833333333333 18.2 \
+          1946.1666666666667 18.1 \
+          1946.25 18.3 \
+          1946.3333333333333 18.4 \
+          1946.4166666666667 18.5 \
+          1946.5 18.7 \
+          1946.5833333333333 19.8 \
+          1946.6666666666667 20.2 \
+          1946.75 20.4 \
+          1946.8333333333333 20.8 \
+          1946.9166666666667 21.3 \
+          1947.0 21.5 \
+          1947.0833333333333 21.5 \
+          1947.1666666666667 21.5 \
+          1947.25 21.9 \
+          1947.3333333333333 21.9 \
+          1947.4166666666667 21.9 \
+          1947.5 22.0 \
+          1947.5833333333333 22.2 \
+          1947.6666666666667 22.5 \
+          1947.75 23.0 \
+          1947.8333333333333 23.0 \
+          1947.9166666666667 23.1 \
+          1948.0 23.4 \
+          1948.0833333333333 23.7 \
+          1948.1666666666667 23.5 \
+          1948.25 23.4 \
+          1948.3333333333333 23.8 \
+          1948.4166666666667 23.9 \
+          1948.5 24.1 \
+          1948.5833333333333 24.4 \
+          1948.6666666666667 24.5 \
+          1948.75 24.5 \
+          1948.8333333333333 24.4 \
+          1948.9166666666667 24.2 \
+          1949.0 24.1 \
+          1949.0833333333333 24.0 \
+          1949.1666666666667 23.8 \
+          1949.25 23.8 \
+          1949.3333333333333 23.9 \
+          1949.4166666666667 23.8 \
+          1949.5 23.9 \
+          1949.5833333333333 23.7 \
+          1949.6666666666667 23.8 \
+          1949.75 23.9 \
+          1949.8333333333333 23.7 \
+          1949.9166666666667 23.8 \
+          1950.0 23.6 \
+          1950.0833333333333 23.5 \
+          1950.1666666666667 23.5 \
+          1950.25 23.6 \
+          1950.3333333333333 23.6 \
+          1950.4166666666667 23.7 \
+          1950.5 23.8 \
+          1950.5833333333333 24.1 \
+          1950.6666666666667 24.3 \
+          1950.75 24.4 \
+          1950.8333333333333 24.6 \
+          1950.9166666666667 24.7 \
+          1951.0 25.0 \
+          1951.0833333333333 25.4 \
+          1951.1666666666667 25.7 \
+          1951.25 25.8 \
+          1951.3333333333333 25.8 \
+          1951.4166666666667 25.9 \
+          1951.5 25.9 \
+          1951.5833333333333 25.9 \
+          1951.6666666666667 25.9 \
+          1951.75 26.1 \
+          1951.8333333333333 26.2 \
+          1951.9166666666667 26.4 \
+          1952.0 26.5 \
+          1952.0833333333333 26.5 \
+          1952.1666666666667 26.3 \
+          1952.25 26.3 \
+          1952.3333333333333 26.4 \
+          1952.4166666666667 26.4 \
+          1952.5 26.5 \
+          1952.5833333333333 26.7 \
+          1952.6666666666667 26.7 \
+          1952.75 26.7 \
+          1952.8333333333333 26.7 \
+          1952.9166666666667 26.7 \
+          1953.0 26.7 \
+          1953.0833333333333 26.6 \
+          1953.1666666666667 26.5 \
+          1953.25 26.6 \
+          1953.3333333333333 26.6 \
+          1953.4166666666667 26.7 \
+          1953.5 26.8 \
+          1953.5833333333333 26.8 \
+          1953.6666666666667 26.9 \
+          1953.75 26.9 \
+          1953.8333333333333 27.0 \
+          1953.9166666666667 26.9 \
+          1954.0 26.9 \
+          1954.0833333333333 26.9 \
+          1954.1666666666667 26.9 \
+          1954.25 26.9 \
+          1954.3333333333333 26.8 \
+          1954.4166666666667 26.9 \
+          1954.5 26.9 \
+          1954.5833333333333 26.9 \
+          1954.6666666666667 26.9 \
+          1954.75 26.8 \
+          1954.8333333333333 26.8 \
+          1954.9166666666667 26.8 \
+          1955.0 26.7 \
+          1955.0833333333333 26.7 \
+          1955.1666666666667 26.7 \
+          1955.25 26.7 \
+          1955.3333333333333 26.7 \
+          1955.4166666666667 26.7 \
+          1955.5 26.7 \
+          1955.5833333333333 26.8 \
+          1955.6666666666667 26.8 \
+          1955.75 26.9 \
+          1955.8333333333333 26.9 \
+          1955.9166666666667 26.9 \
+          1956.0 26.8 \
+          1956.0833333333333 26.8 \
+          1956.1666666666667 26.8 \
+          1956.25 26.8 \
+          1956.3333333333333 26.9 \
+          1956.4166666666667 27.0 \
+          1956.5 27.2 \
+          1956.5833333333333 27.4 \
+          1956.6666666666667 27.3 \
+          1956.75 27.4 \
+          1956.8333333333333 27.5 \
+          1956.9166666666667 27.5 \
+          1957.0 27.6 \
+          1957.0833333333333 27.6 \
+          1957.1666666666667 27.7 \
+          1957.25 27.8 \
+          1957.3333333333333 27.9 \
+          1957.4166666666667 28.0 \
+          1957.5 28.1 \
+          1957.5833333333333 28.3 \
+          1957.6666666666667 28.3 \
+          1957.75 28.3 \
+          1957.8333333333333 28.3 \
+          1957.9166666666667 28.4 \
+          1958.0 28.4 \
+          1958.0833333333333 28.6 \
+          1958.1666666666667 28.6 \
+          1958.25 28.8 \
+          1958.3333333333333 28.9 \
+          1958.4166666666667 28.9 \
+          1958.5 28.9 \
+          1958.5833333333333 29.0 \
+          1958.6666666666667 28.9 \
+          1958.75 28.9 \
+          1958.8333333333333 28.9 \
+          1958.9166666666667 29.0 \
+          1959.0 28.9 \
+          1959.0833333333333 29.0 \
+          1959.1666666666667 28.9 \
+          1959.25 28.9 \
+          1959.3333333333333 29.0 \
+          1959.4166666666667 29.0 \
+          1959.5 29.1 \
+          1959.5833333333333 29.2 \
+          1959.6666666666667 29.2 \
+          1959.75 29.3 \
+          1959.8333333333333 29.4 \
+          1959.9166666666667 29.4 \
+          1960.0 29.4 \
+          1960.0833333333333 29.3 \
+          1960.1666666666667 29.4 \
+          1960.25 29.4 \
+          1960.3333333333333 29.5 \
+          1960.4166666666667 29.5 \
+          1960.5 29.6 \
+          1960.5833333333333 29.6 \
+          1960.6666666666667 29.6 \
+          1960.75 29.6 \
+          1960.8333333333333 29.8 \
+          1960.9166666666667 29.8 \
+          1961.0 29.8 \
+          1961.0833333333333 29.8 \
+          1961.1666666666667 29.8 \
+          1961.25 29.8 \
+          1961.3333333333333 29.8 \
+          1961.4166666666667 29.8 \
+          1961.5 29.8 \
+          1961.5833333333333 30.0 \
+          1961.6666666666667 29.9 \
+          1961.75 30.0 \
+          1961.8333333333333 30.0 \
+          1961.9166666666667 30.0 \
+          1962.0 30.0 \
+          1962.0833333333333 30.0 \
+          1962.1666666666667 30.1 \
+          1962.25 30.1 \
+          1962.3333333333333 30.2 \
+          1962.4166666666667 30.2 \
+          1962.5 30.2 \
+          1962.5833333333333 30.3 \
+          1962.6666666666667 30.3 \
+          1962.75 30.4 \
+          1962.8333333333333 30.4 \
+          1962.9166666666667 30.4 \
+          1963.0 30.4 \
+          1963.0833333333333 30.4 \
+          1963.1666666666667 30.4 \
+          1963.25 30.5 \
+          1963.3333333333333 30.5 \
+          1963.4166666666667 30.5 \
+          1963.5 30.6 \
+          1963.5833333333333 30.7 \
+          1963.6666666666667 30.7 \
+          1963.75 30.7 \
+          1963.8333333333333 30.8 \
+          1963.9166666666667 30.8 \
+          1964.0 30.9 \
+          1964.0833333333333 30.9 \
+          1964.1666666666667 30.9 \
+          1964.25 30.9 \
+          1964.3333333333333 30.9 \
+          1964.4166666666667 30.9 \
+          1964.5 31.0 \
+          1964.5833333333333 31.1 \
+          1964.6666666666667 31.0 \
+          1964.75 31.1 \
+          1964.8333333333333 31.1 \
+          1964.9166666666667 31.2 \
+          1965.0 31.2 \
+          1965.0833333333333 31.2 \
+          1965.1666666666667 31.2 \
+          1965.25 31.3 \
+          1965.3333333333333 31.4 \
+          1965.4166666666667 31.4 \
+          1965.5 31.6 \
+          1965.5833333333333 31.6 \
+          1965.6666666666667 31.6 \
+          1965.75 31.6 \
+          1965.8333333333333 31.7 \
+          1965.9166666666667 31.7 \
+          1966.0 31.8 \
+          1966.0833333333333 31.8 \
+          1966.1666666666667 32.0 \
+          1966.25 32.1 \
+          1966.3333333333333 32.3 \
+          1966.4166666666667 32.3 \
+          1966.5 32.4 \
+          1966.5833333333333 32.5 \
+          1966.6666666666667 32.7 \
+          1966.75 32.7 \
+          1966.8333333333333 32.9 \
+          1966.9166666666667 32.9 \
+          1967.0 32.9 \
+          1967.0833333333333 32.9 \
+          1967.1666666666667 32.9 \
+          1967.25 33.0 \
+          1967.3333333333333 33.1 \
+          1967.4166666666667 33.2 \
+          1967.5 33.3 \
+          1967.5833333333333 33.4 \
+          1967.6666666666667 33.5 \
+          1967.75 33.6 \
+          1967.8333333333333 33.7 \
+          1967.9166666666667 33.8 \
+          1968.0 33.9 \
+          1968.0833333333333 34.1 \
+          1968.1666666666667 34.2 \
+          1968.25 34.3 \
+          1968.3333333333333 34.4 \
+          1968.4166666666667 34.5 \
+          1968.5 34.7 \
+          1968.5833333333333 34.9 \
+          1968.6666666666667 35.0 \
+          1968.75 35.1 \
+          1968.8333333333333 35.3 \
+          1968.9166666666667 35.4 \
+          1969.0 35.5 \
+          1969.0833333333333 35.6 \
+          1969.1666666666667 35.8 \
+          1969.25 36.1 \
+          1969.3333333333333 36.3 \
+          1969.4166666666667 36.4 \
+          1969.5 36.6 \
+          1969.5833333333333 36.8 \
+          1969.6666666666667 37.0 \
+          1969.75 37.1 \
+          1969.8333333333333 37.3 \
+          1969.9166666666667 37.5 \
+          1970.0 37.7 \
+          1970.0833333333333 37.8 \
+          1970.1666666666667 38.0 \
+          1970.25 38.2 \
+          1970.3333333333333 38.5 \
+          1970.4166666666667 38.6 \
+          1970.5 38.8 \
+          1970.5833333333333 39.0 \
+          1970.6666666666667 39.0 \
+          1970.75 39.2 \
+          1970.8333333333333 39.4 \
+          1970.9166666666667 39.6 \
+          1971.0 39.8 \
+          1971.0833333333333 39.8 \
+          1971.1666666666667 39.9 \
+          1971.25 40.0 \
+          1971.3333333333333 40.1 \
+          1971.4166666666667 40.3 \
+          1971.5 40.6 \
+          1971.5833333333333 40.7 \
+          1971.6666666666667 40.8 \
+          1971.75 40.8 \
+          1971.8333333333333 40.9 \
+          1971.9166666666667 40.9 \
+          1972.0 41.1 \
+          1972.0833333333333 41.1 \
+          1972.1666666666667 41.3 \
+          1972.25 41.4 \
+          1972.3333333333333 41.5 \
+          1972.4166666666667 41.6 \
+          1972.5 41.7 \
+          1972.5833333333333 41.9 \
+          1972.6666666666667 42.0 \
+          1972.75 42.1 \
+          1972.8333333333333 42.3 \
+          1972.9166666666667 42.4 \
+          1973.0 42.5 \
+          1973.0833333333333 42.6 \
+          1973.1666666666667 42.9 \
+          1973.25 43.3 \
+          1973.3333333333333 43.6 \
+          1973.4166666666667 43.9 \
+          1973.5 44.2 \
+          1973.5833333333333 44.3 \
+          1973.6666666666667 45.1 \
+          1973.75 45.2 \
+          1973.8333333333333 45.6 \
+          1973.9166666666667 45.9 \
+          1974.0 46.2 \
+          1974.0833333333333 46.6 \
+          1974.1666666666667 47.2 \
+          1974.25 47.8 \
+          1974.3333333333333 48.0 \
+          1974.4166666666667 48.6 \
+          1974.5 49.0 \
+          1974.5833333333333 49.4 \
+          1974.6666666666667 50.0 \
+          1974.75 50.6 \
+          1974.8333333333333 51.1 \
+          1974.9166666666667 51.5 \
+          1975.0 51.9 \
+          1975.0833333333333 52.1 \
+          1975.1666666666667 52.5 \
+          1975.25 52.7 \
+          1975.3333333333333 52.9 \
+          1975.4166666666667 53.2 \
+          1975.5 53.6 \
+          1975.5833333333333 54.2 \
+          1975.6666666666667 54.3 \
+          1975.75 54.6 \
+          1975.8333333333333 54.9 \
+          1975.9166666666667 55.3 \
+          1976.0 55.5 \
+          1976.0833333333333 55.6 \
+          1976.1666666666667 55.8 \
+          1976.25 55.9 \
+          1976.3333333333333 56.1 \
+          1976.4166666666667 56.5 \
+          1976.5 56.8 \
+          1976.5833333333333 57.1 \
+          1976.6666666666667 57.4 \
+          1976.75 57.6 \
+          1976.8333333333333 57.9 \
+          1976.9166666666667 58.0 \
+          1977.0 58.2 \
+          1977.0833333333333 58.5 \
+          1977.1666666666667 59.1 \
+          1977.25 59.5 \
+          1977.3333333333333 60.0 \
+          1977.4166666666667 60.3 \
+          1977.5 60.7 \
+          1977.5833333333333 61.0 \
+          1977.6666666666667 61.2 \
+          1977.75 61.4 \
+          1977.8333333333333 61.6 \
+          1977.9166666666667 61.9 \
+          1978.0 62.1 \
+          1978.0833333333333 62.5 \
+          1978.1666666666667 62.9 \
+          1978.25 63.4 \
+          1978.3333333333333 63.9 \
+          1978.4166666666667 64.5 \
+          1978.5 65.2 \
+          1978.5833333333333 65.7 \
+          1978.6666666666667 66.0 \
+          1978.75 66.5 \
+          1978.8333333333333 67.1 \
+          1978.9166666666667 67.4 \
+          1979.0 67.7 \
+          1979.0833333333333 68.3 \
+          1979.1666666666667 69.1 \
+          1979.25 69.8 \
+          1979.3333333333333 70.6 \
+          1979.4166666666667 71.5 \
+          1979.5 72.3 \
+          1979.5833333333333 73.1 \
+          1979.6666666666667 73.8 \
+          1979.75 74.6 \
+          1979.8333333333333 75.2 \
+          1979.9166666666667 75.9 \
+          1980.0 76.7 \
+          1980.0833333333333 77.8 \
+          1980.1666666666667 78.9 \
+          1980.25 80.1 \
+          1980.3333333333333 81.0 \
+          1980.4166666666667 81.8 \
+          1980.5 82.7 \
+          1980.5833333333333 82.7 \
+          1980.6666666666667 83.3 \
+          1980.75 84.0 \
+          1980.8333333333333 84.8 \
+          1980.9166666666667 85.5 \
+          1981.0 86.3 \
+          1981.0833333333333 87.0 \
+          1981.1666666666667 87.9 \
+          1981.25 88.5 \
+          1981.3333333333333 89.1 \
+          1981.4166666666667 89.8 \
+          1981.5 90.6 \
+          1981.5833333333333 91.6 \
+          1981.6666666666667 92.3 \
+          1981.75 93.2 \
+          1981.8333333333333 93.4 \
+          1981.9166666666667 93.7 \
+          1982.0 94.0 \
+          1982.0833333333333 94.3 \
+          1982.1666666666667 94.6 \
+          1982.25 94.5 \
+          1982.3333333333333 94.9 \
+          1982.4166666666667 95.8 \
+          1982.5 97.0 \
+          1982.5833333333333 97.5 \
+          1982.6666666666667 97.7 \
+          1982.75 97.9 \
+          1982.8333333333333 98.2 \
+          1982.9166666666667 98.0 \
+          1983.0 97.6 \
+          1983.0833333333333 97.8 \
+          1983.1666666666667 97.9 \
+          1983.25 97.9 \
+          1983.3333333333333 98.6 \
+          1983.4166666666667 99.2 \
+          1983.5 99.5 \
+          1983.5833333333333 99.9 \
+          1983.6666666666667 100.2 \
+          1983.75 100.7 \
+          1983.8333333333333 101.0 \
+          1983.9166666666667 101.2 \
+          1984.0 101.3 \
+          1984.0833333333333 101.9 \
+          1984.1666666666667 102.4 \
+          1984.25 102.6 \
+          1984.3333333333333 103.1 \
+          1984.4166666666667 103.4 \
+          1984.5 103.7 \
+          1984.5833333333333 104.1 \
+          1984.6666666666667 104.5 \
+          1984.75 105.0 \
+          1984.8333333333333 105.3 \
+          1984.9166666666667 105.3 \
+          1985.0 105.3 \
+          1985.0833333333333 105.5 \
+          1985.1666666666667 106.0 \
+          1985.25 106.4 \
+          1985.3333333333333 106.9 \
+          1985.4166666666667 107.3 \
+          1985.5 107.6 \
+          1985.5833333333333 107.8 \
+          1985.6666666666667 108.0 \
+          1985.75 108.3 \
+          1985.8333333333333 108.7 \
+          1985.9166666666667 109.0 \
+          1986.0 109.3 \
+          1986.0833333333333 109.6 \
+          1986.1666666666667 109.3 \
+          1986.25 108.8 \
+          1986.3333333333333 108.6 \
+          1986.4166666666667 108.9 \
+          1986.5 109.5 \
+          1986.5833333333333 109.5 \
+          1986.6666666666667 109.7 \
+          1986.75 110.2 \
+          1986.8333333333333 110.3 \
+          1986.9166666666667 110.4 \
+          1987.0 110.5 \
+          1987.0833333333333 111.2 \
+          1987.1666666666667 111.6 \
+          1987.25 112.1 \
+          1987.3333333333333 112.7 \
+          1987.4166666666667 113.1 \
+          1987.5 113.5 \
+          1987.5833333333333 113.8 \
+          1987.6666666666667 114.4 \
+          1987.75 115.0 \
+          1987.8333333333333 115.3 \
+          1987.9166666666667 115.4 \
+          1988.0 115.4 \
+          1988.0833333333333 115.7 \
+          1988.1666666666667 116.0 \
+          1988.25 116.5 \
+          1988.3333333333333 117.1 \
+          1988.4166666666667 117.5 \
+          1988.5 118.0 \
+          1988.5833333333333 118.5 \
+          1988.6666666666667 119.0 \
+          1988.75 119.8 \
+          1988.8333333333333 120.2 \
+          1988.9166666666667 120.3 \
+          1989.0 120.5 \
+          1989.0833333333333 121.1 \
+          1989.1666666666667 121.6 \
+          1989.25 122.3 \
+          1989.3333333333333 123.1 \
+          1989.4166666666667 123.8 \
+          1989.5 124.1 \
+          1989.5833333333333 124.4 \
+          1989.6666666666667 124.6 \
+          1989.75 125.0 \
+          1989.8333333333333 125.6 \
+          1989.9166666666667 125.9 \
+          1990.0 126.1 \
+          1990.0833333333333 127.4 \
+          1990.1666666666667 128.0 \
+          1990.25 128.7 \
+          1990.3333333333333 128.9 \
+          1990.4166666666667 129.2 \
+          1990.5 129.9 \
+          1990.5833333333333 130.4 \
+          1990.6666666666667 131.6 \
+          1990.75 132.7 \
+          1990.8333333333333 133.5 \
+          1990.9166666666667 133.8 \
+          1991.0 133.8 \
+          1991.0833333333333 134.6 \
+          1991.1666666666667 134.8 \
+          1991.25 135.0 \
+          1991.3333333333333 135.2 \
+          1991.4166666666667 135.6 \
+          1991.5 136.0 \
+          1991.5833333333333 136.2 \
+          1991.6666666666667 136.6 \
+          1991.75 137.2 \
+          1991.8333333333333 137.4 \
+          1991.9166666666667 137.8 \
+          1992.0 137.9 \
+          1992.0833333333333 138.1 \
+          1992.1666666666667 138.6 \
+          1992.25 139.3 \
+          1992.3333333333333 139.5 \
+          1992.4166666666667 139.7 \
+          1992.5 140.2 \
+          1992.5833333333333 140.5 \
+          1992.6666666666667 140.9 \
+          1992.75 141.3 \
+          1992.8333333333333 141.8 \
+          1992.9166666666667 142.0 \
+          1993.0 141.9 \
+          1993.0833333333333 142.6 \
+          1993.1666666666667 143.1 \
+          1993.25 143.6 \
+          1993.3333333333333 144.0 \
+          1993.4166666666667 144.2 \
+          1993.5 144.4 \
+          1993.5833333333333 144.4 \
+          1993.6666666666667 144.8 \
+          1993.75 145.1 \
+          1993.8333333333333 145.7 \
+          1993.9166666666667 145.8 \
+          1994.0 145.8 \
+          1994.0833333333333 146.2 \
+          1994.1666666666667 146.7 \
+          1994.25 147.2 \
+          1994.3333333333333 147.4 \
+          1994.4166666666667 147.5 \
+          1994.5 148.0 \
+          1994.5833333333333 148.4 \
+          1994.6666666666667 149.0 \
+          1994.75 149.4 \
+          1994.8333333333333 149.5 \
+          1994.9166666666667 149.7 \
+          1995.0 149.7 \
+          1995.0833333333333 150.3 \
+          1995.1666666666667 150.9 \
+          1995.25 151.4 \
+          1995.3333333333333 151.9 \
+          1995.4166666666667 152.2 \
+          1995.5 152.5 \
+          1995.5833333333333 152.5 \
+          1995.6666666666667 152.9 \
+          1995.75 153.2 \
+          1995.8333333333333 153.7 \
+          1995.9166666666667 153.6 \
+          1996.0 153.5 \
+          1996.0833333333333 154.4 \
+          1996.1666666666667 154.9 \
+          1996.25 155.7 \
+          1996.3333333333333 156.3 \
+          1996.4166666666667 156.6 \
+          1996.5 156.7 \
+          1996.5833333333333 157.0 \
+          1996.6666666666667 157.3 \
+          1996.75 157.8 \
+          1996.8333333333333 158.3 \
+          1996.9166666666667 158.6 \
+          1997.0 158.6 \
+          1997.0833333333333 159.1 \
+          1997.1666666666667 159.6 \
+          1997.25 160.0 \
+          1997.3333333333333 160.2 \
+          1997.4166666666667 160.1 \
+          1997.5 160.3 \
+          1997.5833333333333 160.5 \
+          1997.6666666666667 160.8 \
+          1997.75 161.2 \
+          1997.8333333333333 161.6 \
+          1997.9166666666667 161.5 \
+          1998.0 161.3 \
+          1998.0833333333333 161.6 \
+          1998.1666666666667 161.9 \
+          1998.25 162.2 \
+          1998.3333333333333 162.5 \
+          1998.4166666666667 162.8 \
+          1998.5 163.0 \
+          1998.5833333333333 163.2 \
+          1998.6666666666667 163.4 \
+          1998.75 163.6 \
+          1998.8333333333333 164.0 \
+          1998.9166666666667 164.0 \
+          1999.0 163.9 \
+          1999.0833333333333 164.3 \
+          1999.1666666666667 164.5 \
+          1999.25 165.0 \
+          1999.3333333333333 166.2 \
+          1999.4166666666667 166.2 \
+          1999.5 166.2 \
+          1999.5833333333333 166.7 \
+          1999.6666666666667 167.1 \
+          1999.75 167.9 \
+          1999.8333333333333 168.2 \
+          1999.9166666666667 168.3 \
+          2000.0 168.3 \
+          2000.0833333333333 168.8 \
+          2000.1666666666667 169.8 \
+          2000.25 171.2 \
+          2000.3333333333333 171.3 \
+          2000.4166666666667 171.5 \
+          2000.5 172.4 \
+          2000.5833333333333 172.8 \
+          2000.6666666666667 172.8 \
+          2000.75 173.7 \
+          2000.8333333333333 174.0 \
+          2000.9166666666667 174.1 \
+          2001.0 174.0 \
+          2001.0833333333333 175.1 \
+          2001.1666666666667 175.8 \
+          2001.25 176.2 \
+          2001.3333333333333 176.9 \
+          2001.4166666666667 177.7 \
+          2001.5 178.0 \
+          2001.5833333333333 177.5 \
+          2001.6666666666667 177.5 \
+          2001.75 178.3 \
+          2001.8333333333333 177.7 \
+          2001.9166666666667 177.4 \
+          2002.0 176.7 \
+          2002.0833333333333 177.1 \
+          2002.1666666666667 177.8 \
+          2002.25 178.8 \
+          2002.3333333333333 179.8 \
+          2002.4166666666667 179.8 \
+          2002.5 179.9 \
+          2002.5833333333333 180.1 \
+          2002.6666666666667 180.7 \
+          2002.75 181.0 \
+          2002.8333333333333 181.3 \
+          2002.9166666666667 181.3 \
+          2003.0 180.9 \
+          2003.0833333333333 181.7 \
+          2003.1666666666667 183.1 \
+          2003.25 184.2 \
+          2003.3333333333333 183.8 \
+          2003.4166666666667 183.5 \
+          2003.5 183.7 \
+          2003.5833333333333 183.9 \
+          2003.6666666666667 184.6 \
+          2003.75 185.2 \
+          2003.8333333333333 185.0 \
+          2003.9166666666667 184.5 \
+          2004.0 184.3 \
+          2004.0833333333333 185.2 \
+          2004.1666666666667 186.2 \
+          2004.25 187.4 \
+          2004.3333333333333 188.0 \
+          2004.4166666666667 189.1 \
+          2004.5 189.7 \
+          2004.5833333333333 189.4 \
+          2004.6666666666667 189.5 \
+          2004.75 189.9 \
+          2004.8333333333333 190.9 \
+          2004.9166666666667 191.0 \
+          2005.0 190.3 \
+          2005.0833333333333 190.7 \
+          2005.1666666666667 191.8 \
+          2005.25 193.3 \
+          2005.3333333333333 194.6 \
+          2005.4166666666667 194.4 \
+          2005.5 194.5 \
+          2005.5833333333333 195.4 \
+          2005.6666666666667 196.4 \
+          2005.75 198.8 \
+          2005.8333333333333 199.2 \
+          2005.9166666666667 197.6 \
+          2006.0 196.8 \
+          2006.0833333333333 198.3 \
+          2006.1666666666667 198.7 \
+          2006.25 199.8 \
+          2006.3333333333333 201.5 \
+          2006.4166666666667 202.5 \
+          2006.5 202.9 \
+          2006.5833333333333 203.5 \
+          2006.6666666666667 203.9 \
+          2006.75 202.9 \
+          2006.8333333333333 201.8 \
+          2006.9166666666667 201.5 \
+          2007.0 201.8 \
+          2007.0833333333333 202.416 \
+          2007.1666666666667 203.499 \
+          2007.25 205.352 \
+          2007.3333333333333 206.686 \
+          2007.4166666666667 207.949 \
+          2007.5 208.352 \
+          2007.5833333333333 208.299 \
+          2007.6666666666667 207.917 \
+          2007.75 208.490 \
+          2007.8333333333333 208.936 \
+          2007.9166666666667 210.177 \
+          2008.0 210.036 \
+          2008.0833333333333 211.080 \
+          2008.1666666666667 211.693 \
+          2008.25 213.528 \
+          2008.3333333333333 214.823 \
+          2008.4166666666667 216.632 \
+          2008.5 218.815 \
+          2008.5833333333333 219.964 \
+          2008.6666666666667 219.086 \
+          2008.75 218.783 \
+          2008.8333333333333 216.573 \
+          2008.9166666666667 212.425 \
+          2009.0 210.228 \
+          2009.0833333333333 211.143 \
+          2009.1666666666667 212.193 \
+          2009.25 212.709 \
+          2009.3333333333333 213.240 \
+          2009.4166666666667 213.856 \
+          2009.5 215.693 \
+          2009.5833333333333 215.351 \
+          2009.6666666666667 215.834 \
+          2009.75 215.969 \
+          2009.8333333333333 216.177 \
+          2009.9166666666667 216.330 \
+          2010.0 215.949 \
+          2010.0833333333333 216.687 \
+          2010.1666666666667 216.741 \
+          2010.25 217.631 \
+          2010.3333333333333 218.009 \
+          2010.4166666666667 218.178 \
+          2010.5 217.965 \
+          2010.5833333333333 218.011 \
+          2010.6666666666667 218.312 \
+          2010.75 218.439 \
+          2010.8333333333333 218.711 \
+          2010.9166666666667 218.803 \
+          2011.0 219.179 \
+          2011.0833333333333 220.223 \
+          2011.1666666666667 221.309 \
+          2011.25 223.467 \
+          2011.3333333333333 224.906 \
+          2011.4166666666667 225.964 \
+          2011.5 225.722 \
+          2011.5833333333333 225.922 \
+          2011.6666666666667 226.545 \
+          2011.75 226.889 \
+          2011.8333333333333 226.421 \
+          2011.9166666666667 226.230 \
+          2012.0 225.672 \
+          2012.0833333333333 226.665 \
+          2012.1666666666667 227.663 \
+          2012.25 229.392 \
+          2012.3333333333333 230.085 \
+          2012.4166666666667 229.815 \
+          2012.5 229.478 \
+          2012.5833333333333 229.104 \
+          2012.6666666666667 230.379 \
+          2012.75 231.407 \
+          2012.8333333333333 231.317 \
+          2012.9166666666667 230.221 \
+          2013.0 229.601 \
+          2013.0833333333333 230.280 \
+          2013.1666666666667 232.166 \
+          2013.25 232.773 \
+          2013.3333333333333 232.531 \
+          2013.4166666666667 232.945 \
+          2013.5 233.504 \
+          2013.5833333333333 233.596 \
+          2013.6666666666667 233.877 \
+          2013.75 234.149 \
+          2013.8333333333333 233.546 \
+          2013.9166666666667 233.069 \
+          2014.0 233.049 \
+          2014.0833333333333 233.916 \
+          2014.1666666666667 234.781 \
+          2014.25 236.293 \
+          2014.3333333333333 237.072 \
+          2014.4166666666667 237.900 \
+          2014.5 238.343 \
+          2014.5833333333333 238.250 \
+          2014.6666666666667 237.852 \
+          2014.75 238.031 \
+          2014.8333333333333 237.433 \
+          2014.9166666666667 236.151 \
+          2015.0 234.812 \
+          2015.0833333333333 233.707 \
+          2015.1666666666667 234.722 \
+          2015.25 236.119 \
+          2015.3333333333333 236.599 \
+          2015.4166666666667 237.805 \
+          2015.5 238.638 \
+          2015.5833333333333 238.654 \
+          2015.6666666666667 238.316 \
+          2015.75 237.945 \
+          2015.8333333333333 237.838 \
+          2015.9166666666667 237.336 \
+          2016.0 236.525 \
+          2016.0833333333333 236.916 \
+          2016.1666666666667 237.111 \
+          2016.25 238.132 \
+          2016.3333333333333 239.261 \
+          2016.4166666666667 240.229 \
+          2016.5 241.018 \
+          2016.5833333333333 240.628 \
+          2016.6666666666667 240.849 \
+          2016.75 241.428 \
+          2016.8333333333333 241.729 \
+          2016.9166666666667 241.353 \
+          2017.0 241.432 \
+          2017.0833333333333 242.839 \
+          2017.1666666666667 243.603 \
+          2017.25 243.801 \
+          2017.3333333333333 244.524 \
+          2017.4166666666667 244.733 \
+          2017.5 244.955 \
+          2017.5833333333333 244.786 \
+          2017.6666666666667 245.519 \
+          2017.75 246.819 \
+          2017.8333333333333 246.663 \
+          2017.9166666666667 246.669 \
+          2018.0 246.524 \
+          2018.0833333333333 247.867 \
+          2018.1666666666667 248.991 \
+          2018.25 249.554 \
+          2018.3333333333333 250.546 \
+          2018.4166666666667 251.588 \
+          2018.5 251.989 \
+          2018.5833333333333 252.006 \
+          2018.6666666666667 252.146 \
+          2018.75 252.439 \
+          2018.8333333333333 252.885 \
+          2018.9166666666667 252.038 \
+          2019.0 251.233 \
+          2019.0833333333333 251.712 \
+          2019.1666666666667 252.776 \
+          2019.25 254.202 \
+          2019.3333333333333 255.548 \
+          2019.4166666666667 256.092 \
+          2019.5 256.143 \
+          2019.5833333333333 256.571 \
+          2019.6666666666667 256.558 \
+          2019.75 256.759 \
+          2019.8333333333333 257.346 \
+          2019.9166666666667 257.208 \
+          2020.0 256.974 \
+          2020.0833333333333 257.971 \
+          2020.1666666666667 258.678 \
+          2020.25 258.115 \
+          2020.3333333333333 256.389 \
+          2020.4166666666667 256.394 \
+          2020.5 257.797 \
+          2020.5833333333333 259.101 \
+          2020.6666666666667 259.918 \
+          2020.75 260.280 \
+          2020.8333333333333 260.388 \
+          2020.9166666666667 260.229 \
+          2021.0 260.474 \
+          2021.0833333333333 261.582 \
+          2021.1666666666667 263.014 \
+          2021.25 264.877 \
+          2021.3333333333333 267.054 \
+          2021.4166666666667 269.195 \
+          2021.5 271.696 \
+          2021.5833333333333 273.003 \
+          2021.6666666666667 273.567 \
+          2021.75 274.310 \
+          2021.8333333333333 276.589 \
+          2021.9166666666667 277.948 \
+          2022.0 278.802 \
+          2022.0833333333333 281.148 \
+          2022.1666666666667 283.716 \
+          2022.25 287.504 \
+          2022.3333333333333 289.109 \
+          2022.4166666666667 292.296 \
+          2022.5 296.311 \
+          2022.5833333333333 296.276 \
+          2022.6666666666667 296.171 \
+          2022.75 296.808 \
+          2022.8333333333333 298.012 \
+          2022.9166666666667 297.711 \
+          2023.0 296.797 \
+          2023.0833333333333 299.170 \
+          2023.1666666666667 300.840 \
+          2023.25 301.836 \
+          2023.3333333333333 303.363 \
+          2023.4166666666667 304.127 \
+          2023.5 305.109 \
+          2023.5833333333333 305.691 \
+          2023.6666666666667 307.026 \
+          2023.75 307.789 \
+          2023.8333333333333 307.671 \
+          2023.9166666666667 307.051 \
+          2024.0 306.746 \
+          2024.0833333333333 308.417 \
+
+UScpi_now          308.417
+UScpi_lastdate     2024.0833333333333
+
+USdollars_in(date) units=[1;$] domain=[1913.0833333333333,2024.0833333333333] \
+                               range=[1,31.471122448979585] \
+                               US$ UScpi_now / UScpi(date) ;\
+                               ~UScpi(US$ UScpi_now / USdollars_in)
+USinflation_since(date) units=[1;1] domain=[1913.0833333333333,2024.0833333333333] \
+                               range=[1,31.471122448979585] \
+                                UScpi_now / UScpi(date) ;\
+                               ~UScpi(UScpi_now / USinflation_since)
+
diff --git a/currency.units b/currency.units
new file mode 100644
index 0000000..d4009f7
--- /dev/null
+++ b/currency.units
@@ -0,0 +1,378 @@
+# ISO Currency Codes
+
+ATS                    austriaschilling
+BEF                    belgiumfranc
+CYP                    cypruspound
+EEK                    estoniakroon
+FIM                    finlandmarkka
+FRF                    francefranc
+DEM                    germanymark
+GRD                    greecedrachma
+IEP                    irelandpunt
+ITL                    italylira
+LVL                    latvialats
+LTL                    lithuanialitas
+LUF                    luxembourgfranc
+MTL                    maltalira
+SKK                    slovakiakoruna
+SIT                    sloveniatolar
+ESP                    spainpeseta
+NLG                    netherlandsguilder
+PTE                    portugalescudo
+CVE                    capeverdeescudo
+BGN                    bulgarialev
+BAM                    bosniaconvertiblemark
+KMF                    comorosfranc
+XOF                    westafricafranc
+XPF                    cfpfranc
+XAF                    centralafricacfafranc
+AED                    uaedirham
+AFN                    afghanistanafghani
+ALL                    albanialek
+AMD                    armeniadram
+ANG                    antillesguilder
+AOA                    angolakwanza
+ARS                    argentinapeso
+AUD                    australiadollar
+AWG                    arubaflorin
+AZN                    azerbaijanmanat
+BBD                    barbadosdollar
+BDT                    bangladeshtaka
+BHD                    bahraindinar
+BIF                    burundifranc
+BMD                    bermudadollar
+BND                    bruneidollar
+BOB                    boliviaboliviano
+BRL                    brazilreal
+BSD                    bahamasdollar
+BTN                    bhutanngultrum
+BWP                    botswanapula
+BYN                    belarusruble
+BYR                    oldbelarusruble
+BZD                    belizedollar
+CAD                    canadadollar
+CDF                    drcfranccongolais
+CHF                    swissfranc
+CLP                    chilepeso
+CNY                    chinayuan
+COP                    colombiapeso
+CRC                    costaricacolon
+CUP                    cubapeso
+CZK                    czechiakoruna
+DJF                    djiboutifranc
+DKK                    denmarkkrone
+DOP                    dominicanrepublicpeso
+DZD                    algeriadinar
+EGP                    egyptpound
+ERN                    eritreanakfa
+ETB                    ethiopiabirr
+EUR                    euro
+FJD                    fijidollar
+FKP                    falklandislandspound
+GBP                    ukpound
+GEL                    georgialari
+GHS                    ghanacedi
+GIP                    gibraltarpound
+GMD                    gambiadalasi
+GNF                    guineafranc
+GTQ                    guatemalaquetzal
+GYD                    guyanadollar
+HKD                    hongkongdollar
+HNL                    honduraslempira
+HRK                    croatiakuna
+HTG                    haitigourde
+HUF                    hungaryforint
+IDR                    indonesiarupiah
+ILS                    israelnewshekel
+INR                    indiarupee
+IQD                    iraqdinar
+IRR                    iranrial
+ISK                    icelandkrona
+JMD                    jamaicadollar
+JOD                    jordandinar
+JPY                    japanyen
+KES                    kenyaschilling
+KGS                    kyrgyzstansom
+KHR                    cambodiariel
+KRW                    southkoreawon
+KWD                    kuwaitdinar
+KYD                    caymanislandsdollar
+KZT                    kazakhstantenge
+LAK                    laokip
+LBP                    lebanonpound
+LKR                    srilankarupee
+LRD                    liberiadollar
+LSL                    lesotholoti
+LYD                    libyadinar
+MAD                    moroccodirham
+MDL                    moldovaleu
+MGA                    madagascarariary
+MKD                    macedoniadenar
+MMK                    myanmarkyat
+MNT                    mongoliatugrik
+MOP                    macaupataca
+MRO                    mauritaniaoldouguiya
+MRU                    mauritaniaouguiya
+MUR                    mauritiusrupee
+MVR                    maldiverufiyaa
+MWK                    malawikwacha
+MXN                    mexicopeso
+MYR                    malaysiaringgit
+MZN                    mozambiquemetical
+NAD                    namibiadollar
+NGN                    nigerianaira
+NIO                    nicaraguacordobaoro
+NOK                    norwaykrone
+NPR                    nepalrupee
+NZD                    newzealanddollar
+OMR                    omanrial
+PAB                    panamabalboa
+PEN                    perunuevosol
+PGK                    papuanewguineakina
+PHP                    philippinepeso
+PKR                    pakistanrupee
+PLN                    polandzloty
+PYG                    paraguayguarani
+QAR                    qatarrial
+RON                    romanianewlei
+RSD                    serbiadinar
+RUB                    russiaruble
+RWF                    rwandafranc
+SAR                    saudiarabiariyal
+SBD                    solomonislandsdollar
+SCR                    seychellesrupee
+SDG                    sudanpound
+SEK                    swedenkrona
+SGD                    singaporedollar
+SHP                    sainthelenapound
+SOS                    somaliaschilling
+SRD                    surinamedollar
+SSP                    southsudanpound
+STN                    saotome&principedobra
+SYP                    syriapound
+SZL                    swazilandlilangeni
+THB                    thailandbaht
+TJS                    tajikistansomoni
+TMT                    turkmenistanmanat
+TND                    tunisiadinar
+TOP                    tongapa'anga
+TRY                    turkeylira
+TTD                    trinidadandtobagodollar
+TWD                    taiwandollar
+TZS                    tanzaniashilling
+UAH                    ukrainehryvnia
+UGX                    ugandaschilling
+USD                    US$
+UYU                    uruguaypeso
+UZS                    uzbekistansum
+VES                    venezuelabolivarsoberano
+VND                    vietnamdong
+VUV                    vanuatuvatu
+WST                    samoatala
+XCD                    eastcaribbeandollar
+XDR                    specialdrawingrights
+YER                    yemenrial
+ZAR                    southafricarand
+ZMW                    zambiakwacha
+ZWL                    zimbabwedollar
+FOK                    faroeislandskróna
+GGP                    guernseypound
+IMP                    isleofmanpound
+JEP                    jerseypound
+KID                    kiribatidollar
+TVD                    tuvaludollar
+
+# Currency exchange rates source 
+
+!message Currency exchange rates from exchangerate-api.com (USD base) on 2024-02-18
+
+austriaschilling          1|13.7603 euro
+belgiumfranc              1|40.3399 euro
+cypruspound               1|0.585274 euro
+estoniakroon              1|15.6466 euro # Equal to 1|8 germanymark
+finlandmarkka             1|5.94573 euro
+francefranc               1|6.55957 euro
+germanymark               1|1.95583 euro
+greecedrachma             1|340.75 euro
+irelandpunt               1|0.787564 euro
+italylira                 1|1936.27 euro
+latvialats                1|0.702804 euro
+lithuanialitas            1|3.4528 euro
+luxembourgfranc           1|40.3399 euro
+maltalira                 1|0.4293 euro
+slovakiakoruna            1|30.1260 euro
+sloveniatolar             1|239.640 euro
+spainpeseta               1|166.386 euro
+netherlandsguilder        1|2.20371 euro
+portugalescudo            1|200.482 euro
+capeverdeescudo           1|102.32146 USD
+bulgarialev               1|1.81495 USD
+bosniaconvertiblemark     1|1.814931 USD
+comorosfranc              1|456.526173 USD
+westafricafranc           1|655.957 euro
+cfpfranc                  1|119.33 euro
+centralafricacfafranc     1|608.701564 USD
+uaedirham                 1|3.6725 USD
+afghanistanafghani        1|73.656876 USD
+albanialek                1|96.383995 USD
+armeniadram               1|405.371152 USD
+antillesguilder           1|1.79 USD
+angolakwanza              1|834.493035 USD
+argentinapeso             1|835.05 USD
+australiadollar           1|1.530787 USD
+arubaflorin               1|1.79 USD
+azerbaijanmanat           1|1.700074 USD
+barbadosdollar            1|2 USD
+bangladeshtaka            1|109.733401 USD
+bahraindinar              1|0.376 USD
+burundifranc              1|2848.719924 USD
+bermudadollar             1|1 USD
+bruneidollar              1|1.346678 USD
+boliviaboliviano          1|6.914341 USD
+brazilreal                1|4.969881 USD
+bahamasdollar             1|1 USD
+bhutanngultrum            1|83.047418 USD
+botswanapula              1|13.70455 USD
+belarusruble              1|3.263231 USD
+oldbelarusruble           1|10000 BYN
+belizedollar              1|2 USD
+canadadollar              1|1.348296 USD
+drcfranccongolais         1|2734.755348 USD
+swissfranc                1|0.88091 USD
+chilepeso                 1|970.009669 USD
+chinayuan                 1|7.20772 USD
+colombiapeso              1|3902.397252 USD
+costaricacolon            1|515.061411 USD
+cubapeso                  1|24 USD
+czechiakoruna             1|23.6119 USD
+djiboutifranc             1|177.721 USD
+denmarkkrone              1|6.920726 USD
+dominicanrepublicpeso     1|58.466527 USD
+algeriadinar              1|134.550108 USD
+egyptpound                1|30.900171 USD
+eritreanakfa              1|15 USD
+ethiopiabirr              1|56.698745 USD
+euro                      1|0.927918 USD
+fijidollar                1|2.245823 USD
+falklandislandspound      1|0.793301 USD
+ukpound                   1|0.79329 USD
+georgialari               1|2.640683 USD
+ghanacedi                 1|12.494914 USD
+gibraltarpound            1|0.793301 USD
+gambiadalasi              1|67.296088 USD
+guineafranc               1|8559.047472 USD
+guatemalaquetzal          1|7.798364 USD
+guyanadollar              1|209.358835 USD
+hongkongdollar            1|7.821361 USD
+honduraslempira           1|24.642038 USD
+croatiakuna               1|6.991711 USD
+haitigourde               1|132.095521 USD
+hungaryforint             1|361.305161 USD
+indonesiarupiah           1|15646.731916 USD
+israelnewshekel           1|3.608087 USD
+indiarupee                1|83.047432 USD
+iraqdinar                 1|1309.627139 USD
+iranrial                  1|42008.843651 USD
+icelandkrona              1|138.489372 USD
+jamaicadollar             1|156.661345 USD
+jordandinar               1|0.709 USD
+japanyen                  1|150.199114 USD
+kenyaschilling            1|146.902355 USD
+kyrgyzstansom             1|89.452226 USD
+cambodiariel              1|4101.202091 USD
+southkoreawon             1|1332.971611 USD
+kuwaitdinar               1|0.307917 USD
+caymanislandsdollar       1|0.833333 USD
+kazakhstantenge           1|450.042707 USD
+laokip                    1|20644.611968 USD
+lebanonpound              1|15000 USD
+srilankarupee             1|312.293776 USD
+liberiadollar             1|191.965946 USD
+lesotholoti               1|18.880532 USD
+libyadinar                1|4.84801 USD
+moroccodirham             1|10.064941 USD
+moldovaleu                1|17.820146 USD
+madagascarariary          1|4537.101842 USD
+macedoniadenar            1|57.321607 USD
+myanmarkyat               1|2094.525805 USD
+mongoliatugrik            1|3417.236499 USD
+macaupataca               1|8.056002 USD
+mauritaniaoldouguiya      1|10 MRU
+mauritaniaouguiya         1|39.552376 USD
+mauritiusrupee            1|46.81275 USD
+maldiverufiyaa            1|15.425573 USD
+malawikwacha              1|1684.468367 USD
+mexicopeso                1|17.056723 USD
+malaysiaringgit           1|4.780422 USD
+mozambiquemetical         1|63.844874 USD
+namibiadollar             1|18.880532 USD
+nigerianaira              1|1505.315289 USD
+nicaraguacordobaoro       1|36.75264 USD
+norwaykrone               1|10.518312 USD
+nepalrupee                1|132.875868 USD
+newzealanddollar          1|1.631571 USD
+omanrial                  1|0.384497 USD
+panamabalboa              1|1 USD
+perunuevosol              1|3.843057 USD
+papuanewguineakina        1|3.787609 USD
+philippinepeso            1|55.944862 USD
+pakistanrupee             1|279.287134 USD
+polandzloty               1|4.024592 USD
+paraguayguarani           1|7298.359344 USD
+qatarrial                 1|3.64 USD
+romanianewlei             1|4.62094 USD
+serbiadinar               1|108.77002 USD
+russiaruble               1|92.124682 USD
+rwandafranc               1|1272.20422 USD
+saudiarabiariyal          1|3.75 USD
+solomonislandsdollar      1|8.524333 USD
+seychellesrupee           1|13.695244 USD
+sudanpound                1|458.444066 USD
+swedenkrona               1|10.436076 USD
+singaporedollar           1|1.346679 USD
+sainthelenapound          1|0.793301 USD
+somaliaschilling          1|571.016202 USD
+surinamedollar            1|36.374469 USD
+southsudanpound           1|1196.079061 USD
+saotome&principedobra     1|22.735009 USD
+syriapound                1|12911.027088 USD
+swazilandlilangeni        1|18.880532 USD
+thailandbaht              1|36.003485 USD
+tajikistansomoni          1|10.943604 USD
+turkmenistanmanat         1|3.498274 USD
+tunisiadinar              1|3.136987 USD
+tongapa'anga              1|2.336437 USD
+turkeylira                1|30.867129 USD
+trinidadandtobagodollar   1|6.766115 USD
+taiwandollar              1|31.327633 USD
+tanzaniashilling          1|2541.150426 USD
+ukrainehryvnia            1|37.960827 USD
+ugandaschilling           1|3891.664634 USD
+US$                       !        # Base unit, the primitive unit of currency
+uruguaypeso               1|39.090863 USD
+uzbekistansum             1|12517.91071 USD
+venezuelabolivarsoberano  1|36.3358 USD
+vietnamdong               1|24526.378093 USD
+vanuatuvatu               1|120.592113 USD
+samoatala                 1|2.727298 USD
+eastcaribbeandollar       1|2.7 USD
+specialdrawingrights      1|0.754888 USD
+yemenrial                 1|250.227651 USD
+southafricarand           1|18.880541 USD
+zambiakwacha              1|25.057128 USD
+zimbabwedollar            1|12390.655539 USD
+faroeislandskróna         DKK
+guernseypound             GBP
+isleofmanpound            GBP
+jerseypound               GBP
+kiribatidollar            AUD
+tuvaludollar              AUD
+bitcoin                   51884.75 US$ # From services.packetizer.com/btc
+
+
+# Precious metals prices from Packetizer (services.packetizer.com/spotprices)
+
+silverprice        23.42 US$/troyounce
+goldprice          2013.78 US$/troyounce
+platinumprice      915.15 US$/troyounce
+
diff --git a/definitions.units b/definitions.units
new file mode 100644
index 0000000..ddc3423
--- /dev/null
+++ b/definitions.units
@@ -0,0 +1,8418 @@
+#
+# This file is the units database for use with GNU units, a units conversion
+# program by Adrian Mariano adrianm@gnu.org
+#
+# Febuary 2024 Version 3.19
+# last updated 16 February 2024
+#
+# Copyright (C) 1996-2002, 2004-2020, 2022, 2024
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA  02110-1301  USA
+#
+############################################################################
+#
+# Improvements and corrections are welcome.
+#
+# See the end of this file for a list of items we have chosen to exclude
+# or have decided are out of scope for GNU units.  
+#
+# Fundamental constants in this file are the 2018 CODATA recommended values.
+#
+# Most units data was drawn from
+#            1. NIST Special Publication 811, Guide for the
+#                 Use of the International System of Units (SI).
+#                 Barry N. Taylor. 2008
+#                 https://www.nist.gov/pml/special-publication-811
+#            2. CRC Handbook of Chemistry and Physics 70th edition
+#            3. Oxford English Dictionary
+#            4. Webster's New Universal Unabridged Dictionary
+#            5. Units of Measure by Stephen Dresner
+#            6. A Dictionary of English Weights and Measures by Ronald Zupko
+#            7. British Weights and Measures by Ronald Zupko
+#            8. Realm of Measure by Isaac Asimov
+#            9. United States standards of weights and measures, their
+#                   creation and creators by Arthur H. Frazier.
+#           10. French weights and measures before the Revolution: a
+#                   dictionary of provincial and local units by Ronald Zupko
+#           11. Weights and Measures: their ancient origins and their
+#                   development in Great Britain up to AD 1855 by FG Skinner
+#           12. The World of Measurements by H. Arthur Klein
+#           13. For Good Measure by William Johnstone
+#           14. NTC's Encyclopedia of International Weights and Measures
+#                   by William Johnstone
+#           15. Sizes by John Lord
+#           16. Sizesaurus by Stephen Strauss
+#           17. CODATA Recommended Values of Physical Constants available at
+#                   http://physics.nist.gov/cuu/Constants/index.html
+#           18. How Many?  A Dictionary of Units of Measurement.  Available at
+#                   http://www.ibiblio.org/units/
+#           19. Numericana.  http://www.numericana.com
+#           20. UK history of measurement
+#                   https://metrication.uk/more/timeline/
+#           21. NIST Handbook 44, Specifications, Tolerances, and
+#                 Other Technical Requirements for Weighing and Measuring
+#                 Devices. 2011
+#           22. NIST Special Publication 447, Weights and Measures Standards
+#                 of the United States: a brief history. Lewis V. Judson.
+#                 1963; rev. 1976
+#           23. CRC Handbook of Chemistry and Physics, 96th edition
+#           24. Dictionary of Scientific Units, 6th ed.  H.G.  Jerrard and D.B.
+#                 McNeill. 1992
+#           25. NIST Special Publication 330, The International System of
+#                 Units (SI). ed. Barry N. Taylor and Ambler Thompson. 2008
+#                 https://www.nist.gov/pml/special-publication-330
+#           26. BIPM Brochure, The International System of Units (SI).
+#                 9th ed., 2019
+#                 https://www.bipm.org/en/publications/si-brochure/
+#
+###########################################################################
+#
+# If units you use are missing or defined incorrectly, please contact me.
+# If your country's local units are missing and you are willing to supply
+# them, please send me a list.
+#
+###########################################################################
+
+###########################################################################
+#
+# Brief Philosophy of this file
+#
+# Most unit definitions are made in terms of integers or simple fractions of
+# other definitions.  The typical exceptions are when converting between two
+# different unit systems, or the values of measured physical constants.  In
+# this file definitions are given in the most natural and revealing way in
+# terms of integer factors.
+#
+# If you make changes be sure to run 'units --check' to check your work.
+#
+# The file is USA-centric, but there is some modest effort to support other
+# countries.  This file is now coded in UTF-8.  To support environments where
+# UTF-8 is not available, definitions that require this character set are
+# wrapped in !utf8 directives.
+#
+# When a unit name is used in different countries with the different meanings
+# the system should be as follows:
+#
+# Suppose countries ABC and XYZ both use the "foo".  Then globally define
+#
+#   ABCfoo  <some value>
+#   XYZfoo  <different value>
+#
+# Then, using the !locale directive, define the "foo" appropriately for each of
+# the two countries with a definition like
+#
+# !locale ABC
+#    foo  ABCfoo
+# !endlocale
+#
+###########################################################################
+
+!locale en_US
+!  set UNITS_ENGLISH US
+!endlocale
+
+!locale en_GB
+!  set UNITS_ENGLISH GB
+!endlocale
+
+!set UNITS_ENGLISH US   # Default setting for English units
+
+!set UNITS_SYSTEM default   # Set a default value
+
+!varnot UNITS_SYSTEM si emu esu gaussian gauss hlu natural natural-gauss hartree planck planck-red default 
+!message Unknown unit system given with -u or UNITS_SYSTEM environment variable
+!message Valid systems: si, emu, esu, gauss[ian], hlu, natural, natural-gauss
+!message                planck, planck-red, hartree
+!message Using SI
+!prompt (SI)
+!endvar
+
+!var UNITS_SYSTEM si
+!message SI units selected
+!prompt (SI)
+!endvar
+
+###########################################################################
+#                                                                         #
+# Primitive units.  Any unit defined to contain a '!' character is a      #
+# primitive unit which will not be reduced any further.  All units should #
+# reduce to primitive units.                                              #
+#                                                                         #
+###########################################################################
+
+#
+# SI units
+#
+# On 20 May 2019, the SI was revised to define the units by fixing the
+# values of physical constants that depend on those units.
+#
+# https://www.nist.gov/si-redefinition/
+#
+# The BIPM--the International Bureau of Weights and Measures--provides a
+# succinct description of the new SI in its Concise Summary:
+#
+# https://www.bipm.org/utils/common/pdf/si-brochure/SI-Brochure-9-concise-EN.pdf
+#
+#     The SI is the system of units in which:
+#
+#       *  the unperturbed ground state hyperfine transition frequency of the
+#          caesium 133 atom is delta nu_Cs = 9 192 631 770 Hz,
+#       *  the speed of light in vacuum, c, is 299 792 458 m/s,
+#       *  the Planck constant, h, is 6.626 070 15 * 10^-34 J s,
+#       *  the elementary charge, e, is 1.602 176 634 * 10^-19 C,
+#       *  the Boltzmann constant, k, is 1.380 649 * 10^-23 J/K,
+#       *  the Avogadro constant, N_A, is 6.022 140 76 * 10^23 mol^-1,
+#       *  the luminous efficacy of monochromatic radiation of frequency
+#          540 * 10^12 Hz, K_cd, is 683 lm/W,
+#
+#     where the hertz, joule, coulomb, lumen, and watt, with unit symbols Hz,
+#     J, C, lm, and W, respectively, are related to the units second, metre,
+#     kilogram, ampere, kelvin, mole, and candela, with unit symbols s, m, kg,
+#     A, K, mol, and cd, respectively, according to Hz = s^-1, J = kg m^2 s^-2,
+#     C = A s, lm = cd m^2 m^-2 = cd sr, and W = kg m^2 s^-3.
+# 
+#     These definitions specify the exact numerical value of each constant when
+#     its value is expressed in the corresponding SI unit.  By fixing the exact
+#     numerical value the unit becomes defined, since the product of the
+#     numerical value and the unit has to equal the value of the constant,
+#     which is invariant.
+# 
+#     The defining constants have been chosen such that, when taken together,
+#     their units cover all of the units of the SI.  In general, there is no
+#     one-to-one correspondence between the defining constants and the SI base
+#     units.  Any SI unit is a product of powers of these seven constants and a
+#     dimensionless factor.
+# 
+# Until 2018, the SI was defined in terms of base units and derived units.
+# These categories are no longer essential in the SI, but they are maintained
+# in view of their convenience and widespread use.  They are arguably more
+# intuitive than the new definitions.  (They are also essential to the
+# operation of GNU units.)  The definitions of the base units, which follow
+# from the definition of the SI in terms of the seven defining constants, are
+# given below.
+#
+
+s         !      # The second, symbol s, is the SI unit of time.  It is defined
+second    s      # by taking the fixed numerical value of the unperturbed
+                 # ground-state hyperfine transition frequency of the
+                 # cesium-133 atom to be 9 192 631 770 when expressed in the
+                 # unit Hz, which is equal to 1/s.  
+                 #
+                 # This definition is a restatement of the previous one, the
+                 # duration of 9192631770 periods of the radiation corresponding
+                 # to the cesium-133 transition.
+
+nu_133Cs  9192631770 Hz   # Cesium-133 transition frequency (exact)
+
+c_SI      299792458
+c         299792458 m/s   # speed of light in vacuum (exact)
+
+m         !      # The metre, symbol m, is the SI unit of length.  It is
+meter     m      # defined by taking the fixed numerical value of the speed
+metre     m      # of light in vacuum, c, to be 299 792 458 when expressed in
+                 # units of m/s.
+                 #
+                 # This definition is a rewording of the previous one and is
+                 # equivalent to defining the meter as the distance light
+                 # travels in 1|299792458 seconds.  The meter was originally
+                 # intended to be 1e-7 of the length along a meridian from the
+                 # equator to a pole.
+
+h_SI      6.62607015e-34
+h         6.62607015e-34 J s # Planck constant (exact)
+
+kg        !      # The kilogram, symbol kg, is the SI unit of mass.  It is
+kilogram  kg     # defined by taking the fixed numerical value of the Planck
+                 # constant, h, to be 6.626 070 15 * 10^-34 when expressed in
+                 # the unit J s which is equal to kg m^2 / s.
+                 # 
+                 # One advantage of fixing h to define the kilogram is that this
+                 # affects constants used to define the ampere.  If the kg were
+                 # defined by directly fixing the mass of something, then h
+                 # would be subject to error.
+                 #
+                 # The previous definition of the kilogram was the mass of the
+                 # international prototype kilogram.  The kilogram was the last
+                 # unit whose definition relied on reference to an artifact.
+                 #
+                 # It is not obvious what this new definition means, or
+                 # intuitively how fixing Planck's constant defines the
+                 # kilogram.  To define the kilogram we need to give the mass
+                 # of some reference in kilograms.  Previously the prototype in
+                 # France served as this reference, and it weighed exactly 1
+                 # kg.  But the reference can have any weight as long as you
+                 # know the weight of the reference.  The new definition uses
+                 # the "mass" of a photon, or more accurately, the mass
+                 # equivalent of the energy of a photon.  The energy of a
+                 # photon depends on its frequency.  If you pick a frequency,
+                 # f, then the energy of the photon is hf, and hence the mass
+                 # equivalent is hf/c^2.  If we reduce this expression using
+                 # the constant defined values for h and c the result is a
+                 # value in kilograms for the mass-equivalent of a photon of
+                 # frequency f, which can therefore define the size of the
+                 # kilogram.
+                 #
+                 # For more on the relationship between mass an Planck's
+                 # constant:
+                 #
+                 # https://www.nist.gov/si-redefinition/kilogram-mass-and-plancks-constant
+                 # This definition may still seem rather abstract: you can't
+                 # place a "kilogram of radiation" on one side of a balance.
+                 # Metrologists realize the kilogram using a Kibble Balance, a
+                 # device which relates mechanical energy to electrical energy
+                 # and can measure mass with extreme accuracy if h is known.
+                 #
+                 # For more on the Kibble Balance see
+                 #
+                 # https://www.nist.gov/si-redefinition/kilogram-kibble-balance
+                 # https://en.wikipedia.org/wiki/Kibble_balance
+
+k_SI      1.380649e-23
+boltzmann 1.380649e-23 J/K   # Boltzmann constant (exact)
+k         boltzmann
+
+K         !      # The kelvin, symbol K, is the SI unit of thermodynamic
+kelvin    K      # temperature.  It is defined by taking the fixed numerical
+                 # value of the Boltzmann constant, k, to be 1.380 649 * 10^-23
+                 # when expressed in the unit J/K, which is equal to 
+                 # kg m^2/s^2 K.
+                 #
+                 # The boltzmann constant establishes the relationship between
+                 # energy and temperature.  The average thermal energy carried
+                 # by each degree of freedom is kT/2.  A monatomic ideal gas
+                 # has three degrees of freedom corresponding to the three
+                 # spatial directions, which means its thermal energy is
+                 # (3/2) k T.
+                 #
+                 # The previous definition of the kelvin was based on the
+                 # triple point of water.  The change in the definition of the
+                 # kelvin will not have much effect on measurement practice.
+                 # Practical temperature calibration makes use of two scales,
+                 # the International Temperature Scale of 1990 (ITS-90), which
+                 # covers the range of 0.65 K to 1357.77K and the Provisional
+                 # Low Temperature Scale of 2000 (PLTS-2000), which covers the
+                 # range of 0.9 mK to 1 K.
+                 # https://www.bipm.org/en/committees/cc/cct/publications-cc.html
+                 #
+                 # The ITS-90 contains 17 reference points including things
+                 # like the triple point of hydrogen (13.8033 K) or the
+                 # freezing point of gold (1337.33 K), and of course the triple
+                 # point of water.  The PLTS-2000 specifies four reference
+                 # points, all based on properties of helium-3.
+                 #
+                 # The redefinition of the kelvin will not affect the values of
+                 # these reference points, which have been determined by
+                 # primary thermometry, using thermometers that rely only on
+                 # relationships that allow temperature to be calculated
+                 # directly without using any unknown quantities. Examples
+                 # include acoustic thermometers, which measure the speed of
+                 # sound in a gas, or electronic thermometers, which measure
+                 # tiny voltage fluctuations in resistors. Both variables
+                 # depend directly on temperature.
+
+e_SI      1.602176634e-19
+e         1.602176634e-19 C  # electron charge (exact)
+
+A         !      # The ampere, symbol A, is the SI unit of electric current.
+ampere    A      # It is defined by taking the fixed numerical value of the
+amp       ampere # elementary charge, e, to be 1.602 176 634 * 10^-19 when
+                 # expressed in the unit C, which is equal to A*s.
+                 #
+                 # The previous definition was the current which produces a
+                 # force of 2e-7 N/m between two infinitely long wires a meter
+                 # apart.  This definition was difficult to realize accurately.
+                 #
+                 # The ampere is actually realized by establishing the volt and
+                 # the ohm, since A = V / ohm.  These measurements can be done
+                 # using the Josephson effect and the quantum Hall effect,
+                 # which accurately measure voltage and resistance, respectively,
+                 # with reference to two fixed constants, the Josephson
+                 # constant, K_J=2e/h and the von Klitzing constant, R_K=h/e^2.
+                 # Under the previous SI system, these constants had official
+                 # fixed values, defined in 1990.  This created a situation
+                 # where the standard values for the volt and ohm were in some
+                 # sense outside of SI because they depended primarily on
+                 # constants different from the ones used to define SI. After
+                 # the revision, since e and h have exact definitions, the
+                 # Josephson and von Klitzing constants will also have exact
+                 # definitions that derive from SI instead of the conventional
+                 # 1990 values.
+                 #
+                 # In fact we know that there is a small offset between the
+                 # conventional values of the electrical units based on the
+                 # conventional 1990 values and the SI values.  The new
+                 # definition, which brings the practical electrical units back
+                 # into SI, will lead to a one time change of +0.1ppm for
+                 # voltage values and +0.02ppm for resistance values.
+                 #
+                 # The previous definition resulted in fixed exact values for
+                 # the vacuum permeability (mu0), the impedance of free space
+                 # (Z0), the vacuum permittivity (epsilon0), and the Coulomb
+                 # constant. With the new definition, these four values are
+                 # subject to experimental error.
+
+avogadro  6.02214076e23 / mol # Size of a mole (exact)
+N_A       avogadro
+
+mol       !      # The mole, symbol mol, is the SI unit of amount of
+mole      mol    # substance.  One mole contains exactly 6.022 140 76 * 10^23
+                 # elementary entities.  This number is the fixed numerical
+                 # value of the Avogadro constant, N_A, when expressed in the
+                 # unit 1/mol and is called the Avogadro number.  The amount of
+                 # substance, symbol n, of a system is a measure of the number
+                 # of specified elementary entities.  An elementary entity may
+                 # be an atom, a molecule, an ion, an electron, any other
+                 # particle or specified group of particles.
+                 #
+                 # The atomic mass unit (u) is defined as 1/12 the mass of
+                 # carbon-12.  Previously the mole was defined so that a mole
+                 # of carbon-12 weighed exactly 12g, or N_A u = 1 g/mol
+                 # exactly. This relationship is now an experimental, 
+                 # approximate relationship.
+                 #
+                 # To determine the size of the mole, researchers used spheres
+                 # of very pure silicon-28 that weighed a kilogram.  They
+                 # measured the molar mass of Si-28 using mass spectrometry and
+                 # used X-ray diffraction interferometry to determine the
+                 # spacing of the silicon atoms in the sphere.  Using the
+                 # sphere's volume it was then possible to determine the number
+                 # of silicon atoms in the sphere, and hence determine the
+                 # Avogadro constant.  The results of this experiment were used 
+                 # to define N_A, which is henceforth a fixed, unchanging
+                 # quantity.  
+
+cd        !      # The candela, symbol cd, is the SI unit of luminous intensity
+candela   cd     # in a given direction.  It is defined by taking the fixed
+                 # numerical value of the luminous efficacy of monochromatic
+                 # radiation of the frequency 540e12 Hz to be 683 when
+                 # expressed in the unit lumen/watt, which is equal to
+                 # cd sr/W, or cd sr s^3/kg m^2
+                 #
+                 # This definition is a rewording of the previous definition.
+                 # Luminous intensity differs from radiant intensity (W/sr) in
+                 # that it is adjusted for human perceptual dependence on
+                 # wavelength.  The frequency of 540e12 Hz (yellow;
+                 # wavelength approximately 555 nm in vacuum) is where human
+                 # perception is most efficient.
+                 
+K_cd 683 lumen/W # Luminous efficiency at 540e12 Hz (exact)
+
+# Angular Measure
+#
+# The radian and steradian are defined as dimensionless primitive units.
+# The radian is equal to m/m and the steradian to m^2/m^2 so these units are
+# dimensionless.  Retaining them as named units is useful because it allows
+# clarity in expressions and makes the meaning of unit definitions more clear.
+# These units will reduce to 1 in conversions but not for sums of units or for
+# arguments to functions.
+#
+
+radian    !dimensionless   # Plane angle subtended at the center of a circle by
+                           #   an arc equal in length to the radius of the
+                           #   circle.  
+                           #   Dimension: LENGTH (of arc) / DISTANCE (radius)
+
+sr        !dimensionless   # Solid angle which cuts off an area of the surface
+steradian sr               #   of the sphere equal to that of a square with
+                           #   sides of length equal to the radius of the
+                           #   sphere.
+                           #   Dimension: AREA (of surface) / DISTANCE^2
+                           #                                         (radius^2)
+#
+# A primitive non-SI unit
+#
+
+bit       !      # Basic unit of information (entropy).  The entropy in bits
+                 #   of a random variable over a finite alphabet is defined
+                 #   to be the sum of -p(i)*log2(p(i)) over the alphabet where
+                 #   p(i) is the probability that the random variable takes
+                 #   on the value i.
+
+#
+# Currency: the primitive unit of currency is defined in currency.units. 
+# It is usually the US$ or the euro, but it is user selectable.  
+#
+
+#
+# Absolute value
+#
+
+abs(x)                  noerror sqrt(x^2)  
+
+###########################################################################
+#                                                                         #
+# Prefixes (longer names must come first)                                 #
+#                                                                         #
+###########################################################################
+
+quetta-                 1e30     # Allegedly from "q" plus Greek "deka" (ten)
+ronna-                  1e27     # Allegedly from "r" plus Greek "ennea" (nine)
+yotta-                  1e24     # Greek or Latin "octo" (eight)
+zetta-                  1e21     # Latin "septem" (seven)
+exa-                    1e18     # Greek "hex" (six)
+peta-                   1e15     # Greek "pente" (five)
+tera-                   1e12     # Greek "teras" (monster)
+giga-                   1e9      # Greek "gigas" (giant)
+mega-                   1e6      # Greek "megas" (large)
+myria-                  1e4      # Not an official SI prefix
+kilo-                   1e3      # Greek "chilioi" (thousand)
+hecto-                  1e2      # Greek "hekaton" (hundred)
+deca-                   1e1      # Greek "deka" (ten)
+deka-                   deca
+deci-                   1e-1     # Latin "decimus" (tenth)
+centi-                  1e-2     # Latin "centum" (hundred)
+milli-                  1e-3     # Latin "mille" (thousand)
+micro-                  1e-6     # Latin "micro" or Greek "mikros" (small)
+nano-                   1e-9     # Latin "nanus" or Greek "nanos" (dwarf)
+pico-                   1e-12    # Spanish "pico" (a bit)
+femto-                  1e-15    # Danish-Norwegian "femten" (fifteen)
+atto-                   1e-18    # Danish-Norwegian "atten" (eighteen)
+zepto-                  1e-21    # Latin "septem" (seven)
+yocto-                  1e-24    # Greek or Latin "octo" (eight)
+ronto-                  1e-27    # Allegedly "r" plus Latin "novum" (nine)
+quecto-                 1e-30    # Allegedly "q" plus Latin "decim" (ten)
+
+quarter-                1|4
+semi-                   0.5
+demi-                   0.5
+hemi-                   0.5
+half-                   0.5
+double-                 2
+triple-                 3
+treble-                 3
+
+kibi-                   2^10     # In response to the improper and confusing
+mebi-                   2^20     # use of SI prefixes for powers of two,
+gibi-                   2^30     # the International Electrotechnical
+tebi-                   2^40     # Commission aproved these binary prefixes
+pebi-                   2^50     # in IEC 60027-2 Amendment 2 (1999).
+exbi-                   2^60
+zebi-                   2^70     # Zebi- and yobi- were added in the 2005 ed.,
+yobi-                   2^80     # later superseded by ISO/IEC 80000-13:2008.
+robi-                   2^90
+quebi-                  2^100
+Ki-                     kibi
+Mi-                     mebi
+Gi-                     gibi
+Ti-                     tebi
+Pi-                     pebi
+Ei-                     exbi
+Zi-                     zebi
+Yi-                     yobi
+Ri-                     robi
+Qi-                     quebi
+
+Q-                      quetta
+R-                      ronna
+Y-                      yotta
+Z-                      zetta
+E-                      exa
+P-                      peta
+T-                      tera
+G-                      giga
+M-                      mega
+k-                      kilo
+h-                      hecto
+da-                     deka
+d-                      deci
+c-                      centi
+m-                      milli
+u-                      micro   # it should be a mu but u is easy to type
+n-                      nano
+p-                      pico
+f-                      femto
+a-                      atto
+z-                      zepto
+y-                      yocto
+r-                      ronto
+q-                      quecto
+
+#
+# Names of some numbers
+#
+
+one                     1
+two                     2
+double                  2
+couple                  2
+three                   3
+triple                  3
+four                    4
+quadruple               4
+five                    5
+quintuple               5
+six                     6
+seven                   7
+eight                   8
+nine                    9
+ten                     10
+eleven                  11
+twelve                  12
+thirteen                13
+fourteen                14
+fifteen                 15
+sixteen                 16
+seventeen               17
+eighteen                18
+nineteen                19
+twenty                  20
+thirty                  30
+forty                   40
+fifty                   50
+sixty                   60
+seventy                 70
+eighty                  80
+ninety                  90
+hundred                 100
+thousand                1000
+million                 1e6
+
+twoscore                two score
+threescore              three score
+fourscore               four score
+fivescore               five score
+sixscore                six score
+sevenscore              seven score
+eightscore              eight score
+ninescore               nine score
+tenscore                ten score
+twelvescore             twelve score
+
+# These number terms were described by N. Chuquet and De la Roche in the 16th
+# century as being successive powers of a million.  These definitions are still
+# used in most European countries.  The current US definitions for these
+# numbers arose in the 17th century and don't make nearly as much sense.  These
+# numbers are listed in the CRC Concise Encyclopedia of Mathematics by Eric
+# W. Weisstein.
+
+shortbillion               1e9
+shorttrillion              1e12
+shortquadrillion           1e15
+shortquintillion           1e18
+shortsextillion            1e21
+shortseptillion            1e24
+shortoctillion             1e27
+shortnonillion             1e30
+shortnoventillion          shortnonillion
+shortdecillion             1e33
+shortundecillion           1e36
+shortduodecillion          1e39
+shorttredecillion          1e42
+shortquattuordecillion     1e45
+shortquindecillion         1e48
+shortsexdecillion          1e51
+shortseptendecillion       1e54
+shortoctodecillion         1e57
+shortnovemdecillion        1e60
+shortvigintillion          1e63
+
+centillion              1e303
+googol                  1e100
+
+longbillion               million^2
+longtrillion              million^3
+longquadrillion           million^4
+longquintillion           million^5
+longsextillion            million^6
+longseptillion            million^7
+longoctillion             million^8
+longnonillion             million^9
+longnoventillion          longnonillion
+longdecillion             million^10
+longundecillion           million^11
+longduodecillion          million^12
+longtredecillion          million^13
+longquattuordecillion     million^14
+longquindecillion         million^15
+longsexdecillion          million^16
+longseptdecillion         million^17
+longoctodecillion         million^18
+longnovemdecillion        million^19
+longvigintillion          million^20
+
+# These numbers fill the gaps left by the long system above.
+
+milliard                1000 million
+billiard                1000 million^2
+trilliard               1000 million^3
+quadrilliard            1000 million^4
+quintilliard            1000 million^5
+sextilliard             1000 million^6
+septilliard             1000 million^7
+octilliard              1000 million^8
+nonilliard              1000 million^9
+noventilliard           nonilliard
+decilliard              1000 million^10
+
+# For consistency
+
+longmilliard              milliard
+longbilliard              billiard
+longtrilliard             trilliard
+longquadrilliard          quadrilliard
+longquintilliard          quintilliard
+longsextilliard           sextilliard
+longseptilliard           septilliard
+longoctilliard            octilliard
+longnonilliard            nonilliard
+longnoventilliard         noventilliard
+longdecilliard            decilliard
+
+# The long centillion would be 1e600.  The googolplex is another
+# familiar large number equal to 10^googol.  These numbers give overflows.
+
+#
+# The short system prevails in English speaking countries
+#
+
+billion                 shortbillion
+trillion                shorttrillion
+quadrillion             shortquadrillion
+quintillion             shortquintillion
+sextillion              shortsextillion
+septillion              shortseptillion
+octillion               shortoctillion
+nonillion               shortnonillion
+noventillion            shortnoventillion
+decillion               shortdecillion
+undecillion             shortundecillion
+duodecillion            shortduodecillion
+tredecillion            shorttredecillion
+quattuordecillion       shortquattuordecillion
+quindecillion           shortquindecillion
+sexdecillion            shortsexdecillion
+septendecillion         shortseptendecillion
+octodecillion           shortoctodecillion
+novemdecillion          shortnovemdecillion
+vigintillion            shortvigintillion
+
+#
+# Numbers used in India 
+#
+
+lakh                    1e5
+crore                   1e7
+arab                    1e9
+kharab                  1e11
+neel                    1e13
+padm                    1e15
+shankh                  1e17
+
+#############################################################################
+#                                                                           #
+# Derived units which can be reduced to the primitive units                 #
+#                                                                           #
+#############################################################################
+
+
+
+#
+# Named SI derived units (officially accepted)
+#
+
+newton                  kg m / s^2   # force
+N                       newton
+pascal                  N/m^2        # pressure or stress
+Pa                      pascal
+joule                   N m          # energy
+J                       joule
+watt                    J/s          # power
+W                       watt
+coulomb                 A s          # charge
+C                       coulomb
+volt                    W/A          # potential difference
+V                       volt
+ohm                     V/A          # electrical resistance
+siemens                 A/V          # electrical conductance
+S                       siemens
+farad                   C/V          # capacitance
+F                       farad
+weber                   V s          # magnetic flux
+Wb                      weber
+henry                   V s / A      # inductance, also Wb/A, but needs to be
+H                       henry        #   defined this way for CGS units
+tesla                   Wb/m^2       # magnetic flux density
+T                       tesla
+hertz                   /s           # frequency
+Hz                      hertz
+
+#
+# Dimensions.  These are here to help with dimensional analysis and
+# because they will appear in the list produced by hitting '?' at the
+# "You want:" prompt to tell the user the dimension of the unit.
+#
+
+LENGTH                  meter
+AREA                    LENGTH^2
+VOLUME                  LENGTH^3
+MASS                    kilogram
+AMOUNT                  mole
+ANGLE                   radian
+SOLID_ANGLE             steradian
+MONEY                   US$
+FORCE                   newton
+PRESSURE                FORCE / AREA
+STRESS                  FORCE / AREA
+FREQUENCY               hertz
+WAVELENGTH              LENGTH
+WAVENUMBER              1/WAVELENGTH          # number of waves per distance
+VELOCITY                DISPLACEMENT / TIME   # a vector (includes direction)
+SPEED                   DISTANCE / TIME       # a scalar 
+ACCELERATION            VELOCITY / TIME
+MOMENTUM                MASS VELOCITY    # Also ENERGY / VELOCITY or IMPULSE
+IMPULSE                 FORCE TIME
+DISPLACEMENT            LENGTH
+DISTANCE                LENGTH
+ELONGATION              LENGTH
+STRAIN                  ELONGATION / LENGTH
+ENERGY                  joule
+POWER                   watt
+WORK                    FORCE DISTANCE
+DENSITY                 MASS / VOLUME
+LINEAR_DENSITY          MASS / LENGTH
+SPECIFIC_ENERGY         ENERGY / MASS
+VISCOSITY               FORCE TIME / AREA
+KINEMATIC_VISCOSITY     VISCOSITY / DENSITY
+CURRENT                 ampere
+CHARGE                  coulomb
+CAPACITANCE             farad
+RESISTANCE              ohm
+CONDUCTANCE             siemens
+# It may be easier to understand the relationship by considering
+# an object with specified dimensions and resistivity, whose
+# resistance is given by the resistivity * length / area. 
+RESISTIVITY             RESISTANCE AREA / LENGTH
+CONDUCTIVITY            CONDUCTANCE LENGTH / AREA
+INDUCTANCE              henry
+E_FIELD                 ELECTRIC_POTENTIAL / LENGTH
+B_FIELD                 tesla
+# The D and H fields are related to the E and B fields by factors of
+# epsilon and mu respectively, so their units can be found by
+# multiplying/dividing by the epsilon0 and mu0.  The more complex
+# definitions below make it possible to use D_FIELD and E_FIELD to
+# convert between SI and CGS units for these dimensions.
+D_FIELD                 E_FIELD epsilon0 / epsilon0_SI  #   mu0_SI c^2 F / m
+H_FIELD                 B_FIELD / (mu0/mu0_SI)
+ELECTRIC_DIPOLE_MOMENT  C m
+MAGNETIC_DIPOLE_MOMENT  J / T
+POLARIZATION            ELECTRIC_DIPOLE_MOMENT / VOLUME
+MAGNETIZATION           MAGNETIC_DIPOLE_MOMENT / VOLUME
+ELECTRIC_POTENTIAL      ENERGY / CHARGE #volt
+VOLTAGE                 ELECTRIC_POTENTIAL
+E_FLUX                  E_FIELD AREA    
+D_FLUX                  D_FIELD AREA
+B_FLUX                  B_FIELD AREA    
+H_FLUX                  H_FIELD AREA
+
+#
+# units derived easily from SI units
+#
+
+gram                    millikg
+gm                      gram
+g                       gram
+tonne                   1000 kg
+t                       tonne
+metricton               tonne
+sthene                  tonne m / s^2
+funal                   sthene
+pieze                   sthene / m^2
+quintal                 100 kg
+bar                     1e5 Pa     # About 1 atm
+b                       bar
+vac                     millibar
+micron                  micrometer # One millionth of a meter
+bicron                  picometer  # One brbillionth of a meter
+cc                      cm^3
+are                     100 m^2
+a                       are
+liter                   1000 cc       # The liter was defined in 1901 as the
+oldliter                1.000028 dm^3 # space occupied by 1 kg of pure water at
+L                       liter         # the temperature of its maximum density
+l                       liter         # under a pressure of 1 atm.  This was
+                                      # supposed to be 1000 cubic cm, but it
+                                      # was discovered that the original
+                                      # measurement was off.  In 1964, the
+                                      # liter was redefined to be exactly 1000
+                                      # cubic centimeters.
+Ah                      amp hour   # Unit of charge                                      
+mho                     siemens    # Inverse of ohm, hence ohm spelled backward
+galvat                  ampere     # Named after Luigi Galvani
+angstrom                1e-10 m    # Convenient for describing molecular sizes
+xunit                   xunit_cu      # Used for measuring x-ray wavelengths.
+siegbahn                xunit         # Originally defined to be 1|3029.45 of
+xunit_cu             1.00207697e-13 m # the spacing of calcite planes at 18
+xunit_mo             1.00209952e-13 m # degC.  It was intended to be exactly
+                                      # 1e-13 m, but was later found to be
+                                      # slightly off.  Current usage is with
+                                      # reference to common x-ray lines, either
+                                      # the K-alpha 1 line of copper or the
+                                      # same line of molybdenum.
+angstromstar   1.00001495 angstrom # Defined by JA Bearden in 1965 to replace 
+                                   #   the X unit.  The wavelength of the
+                                   #   tungsten K alpha1 line was defined as
+                                   #   exactly 0.20901 angstrom star, with the
+                                   #   value chosen to try to make the new
+                                   #   unit close to the angstrom.  
+silicon_d220     1.920155716e-10 m # Silicon lattice spacing
+siliconlattice sqrt(8) silicon_d220# Silicon lattice parameter, (a), the side
+                                   #   length of the unit cell for the diamond
+                                   #   centered cubic structure of silicon.
+fermi                   1e-15 m    # Convenient for describing nuclear sizes
+                                   #   Nuclear radius is from 1 to 10 fermis
+barn                    1e-28 m^2  # Used to measure cross section for
+                                   #   particle physics collision, said to
+                                   #   have originated in the phrase "big as
+                                   #   a barn".
+shed                    1e-24 barn # Defined to be a smaller companion to the
+                                   #   barn, but it's too small to be of
+                                   #   much use.
+brewster                micron^2/N # measures stress-optical coef
+diopter                 /m         # measures reciprocal of lens focal length
+fresnel                 1e12 Hz    # occasionally used in spectroscopy
+shake                   1e-8 sec
+svedberg                1e-13 s    # Used for measuring the sedimentation
+                                   # coefficient for centrifuging.
+gamma                   microgram  # Also used for 1e-9 tesla
+lambda                  microliter
+spat                    1e12 m     # Rarely used for astronomical measurements
+preece                  1e13 ohm m # resistivity
+planck                  J s        # action of one joule over one second
+sturgeon                /henry     # magnetic reluctance
+daraf                   1/farad    # elastance (farad spelled backwards)
+leo                     10 m/s^2
+poiseuille              N s / m^2  # viscosity
+mayer                   J/g K      # specific heat
+mired                   / microK   # reciprocal color temperature.  The name
+                                   #   abbreviates micro reciprocal degree.
+crocodile               megavolt   # used informally in UK physics labs
+metricounce             25 g
+mounce                  metricounce
+finsenunit              1e5 W/m^2  # Measures intensity of ultraviolet light
+                                   # with wavelength 296.7 nm.
+fluxunit                1e-26 W/m^2 Hz # Used in radio astronomy to measure
+                                       #   the energy incident on the receiving
+                                       #   body across a specified frequency
+                                       #   bandwidth.  [12]
+jansky                  fluxunit   # K. G. Jansky identified radio waves coming
+Jy                      jansky     # from outer space in 1931.
+flick       W / cm^2 sr micrometer # Spectral radiance or irradiance
+pfu                    / cm^2 sr s # particle flux unit -- Used to measure
+                                   #   rate at which particles are received by
+                                   #   a spacecraft as particles per solid
+                                   #   angle per detector area per second. [18]
+pyron            cal_IT / cm^2 min # Measures heat flow from solar radiation,
+                                   #   from Greek work "pyr" for fire.
+katal                   mol/sec    # Measure of the amount of a catalyst.  One
+kat                     katal      #   katal of catalyst enables the reaction
+                                   #   to consume or produce one mol/sec.
+solarluminosity         382.8e24 W # A common yardstick for comparing the
+                                   #   output of different stars.
+                # http://nssdc.gsfc.nasa.gov/planetary/factsheet/sunfact.html
+# at mean Earth-Sun distance
+solarirradiance         solarluminosity / (4 pi sundist^2)
+solarconstant           solarirradiance
+TSI                     solarirradiance         # total solar irradiance
+
+#
+# time
+#
+
+sec                     s
+minute                  60 s
+min                     minute
+hour                    60 min
+hr                      hour
+day                     24 hr
+d                       day
+da                      day
+week                    7 day
+wk                      week
+sennight                7 day
+fortnight               14 day
+blink                   1e-5 day   # Actual human blink takes 1|3 second
+ce                      1e-2 day
+cron                    1e6 years
+watch                   4 hours    # time a sentry stands watch or a ship's
+                                   # crew is on duty.
+bell                    1|8 watch  # Bell would be sounded every 30 minutes.
+
+# French Revolutionary Time or Decimal Time.  It was Proposed during
+# the French Revolution.  A few clocks were made, but it never caught
+# on.  In 1998 Swatch defined a time measurement called ".beat" and
+# sold some watches that displayed time in this unit.
+
+decimalhour             1|10 day            
+decimalminute           1|100 decimalhour   
+decimalsecond           1|100 decimalminute 
+beat                    decimalminute          # Swatch Internet Time
+
+#
+# angular measure
+#
+
+circle                  2 pi radian
+degree                  1|360 circle
+deg                     degree
+arcdeg                  degree
+arcmin                  1|60 degree
+arcminute               arcmin
+'                       arcmin
+arcsec                  1|60 arcmin
+arcsecond               arcsec
+"                       arcsec
+''                      "
+rightangle              90 degrees
+quadrant                1|4 circle
+quintant                1|5 circle
+sextant                 1|6 circle
+
+sign                    1|12 circle # Angular extent of one sign of the zodiac
+turn                    circle
+revolution              turn
+rev                     turn
+pulsatance              radian / sec
+gon                     1|100 rightangle  # measure of grade
+grade                   gon
+centesimalminute        1|100 grade
+centesimalsecond        1|100 centesimalminute
+milangle                1|6400 circle     # Official NIST definition.
+                                          # Another choice is 1e-3 radian.
+pointangle              1|32 circle  # Used for reporting compass readings
+centrad                 0.01 radian  # Used for angular deviation of light
+                                     # through a prism.
+mas                     milli arcsec # Used by astronomers
+seclongitude            circle (seconds/day) # Astronomers measure longitude
+                                     # (which they call right ascension) in
+                                     # time units by dividing the equator into
+                                     # 24 hours instead of 360 degrees.
+#
+# Some geometric formulas
+#
+
+circlearea(r)   units=[m;m^2] range=[0,) pi r^2 ; sqrt(circlearea/pi)
+spherevolume(r) units=[m;m^3] range=[0,) 4|3 pi r^3 ; \
+                                         cuberoot(spherevolume/4|3 pi)
+spherevol()     spherevolume
+square(x)       range=[0,)          x^2 ; sqrt(square)
+
+#
+# Solid angle measure
+#
+
+sphere                  4 pi sr
+squaredegree            1|180^2 pi^2 sr
+squareminute            1|60^2 squaredegree
+squaresecond            1|60^2 squareminute
+squarearcmin            squareminute
+squarearcsec            squaresecond
+sphericalrightangle     1|8 sphere
+octant                  1|8 sphere
+
+#
+# Concentration measures
+#
+
+percent                 0.01
+%                       percent
+mill                    0.001     # Originally established by Congress in 1791
+                                  # as a unit of money equal to 0.001 dollars,
+                                  # it has come to refer to 0.001 in general.
+                                  # Used by some towns to set their property
+                                  # tax rate, and written with a symbol similar
+                                  # to the % symbol but with two 0's in the
+                                  # denominator.  [18]
+proof                   1|200     # Alcohol content measured by volume at
+                                  # 60 degrees Fahrenheit.  This is a USA
+                                  # measure.  In Europe proof=percent.
+ppm                     1e-6
+partspermillion         ppm
+ppb                     1e-9
+partsperbillion         ppb       # USA billion
+ppt                     1e-12
+partspertrillion        ppt       # USA trillion
+karat                   1|24      # measure of gold purity
+caratgold               karat
+gammil                  mg/l
+basispoint              0.01 %    # Used in finance
+fine                    1|1000    # Measure of gold purity
+
+# The pH scale is used to measure the concentration of hydronium (H3O+) ions in
+# a solution.  A neutral solution has a pH of 7 as a result of dissociated
+# water molecules.
+
+pH(x) units=[1;mol/liter] range=(0,) 10^(-x) mol/liter ; (-log(pH liters/mol))
+
+
+#
+# Temperature
+#
+# Two types of units are defined: units for converting temperature differences
+# and functions for converting absolute temperatures.  Conversions for
+# differences start with "deg" and conversions for absolute temperature start
+# with "temp".
+#
+# If the temperature inside is 72 degrees Fahrenheit and you want to
+# convert this to degrees Celsius then you need absolute temperature:
+#
+# You have: tempF(72)
+# You want: tempC
+#         22.222222
+#
+# If the temperature rose 72 degrees Fahrenheit during the chemical reaction
+# then this is a temperature difference:
+#
+# You have: 72 degF
+# You want: degC
+#        * 40
+#        / 0.025
+#
+
+TEMPERATURE             kelvin
+TEMPERATURE_DIFFERENCE  kelvin
+
+# In 1741 Anders Celsius introduced a temperature scale with water boiling at
+# 0 degrees and freezing at 100 degrees at standard pressure. After his death
+# the fixed points were reversed and the scale was called the centigrade
+# scale.  Due to the difficulty of accurately measuring the temperature of
+# melting ice at standard pressure, the centigrade scale was replaced in 1954
+# by the Celsius scale which is defined by subtracting 273.15 from the
+# temperature in Kelvins.  This definition differed slightly from the old
+# centigrade definition, but the Kelvin scale depends on the triple point of
+# water rather than a melting point, so it can be measured accurately.
+
+tempC(x) units=[1;K] domain=[-273.15,) range=[0,) \
+                             x K + stdtemp ; (tempC +(-stdtemp))/K
+tempcelsius() tempC
+degcelsius              K
+degC                    K
+
+# Fahrenheit defined his temperature scale by setting 0 to the coldest
+# temperature he could produce in his lab with a salt water solution and by
+# setting 96 degrees to body heat.  In Fahrenheit's words:
+#
+#    Placing the thermometer in a mixture of sal ammoniac or sea
+#    salt, ice, and water a point on the scale will be found which
+#    is denoted as zero. A second point is obtained if the same
+#    mixture is used without salt. Denote this position as 30. A
+#    third point, designated as 96, is obtained if the thermometer
+#    is placed in the mouth so as to acquire the heat of a healthy
+#    man."  (D. G. Fahrenheit, Phil. Trans. (London) 33, 78, 1724)
+
+tempF(x) units=[1;K] domain=[-459.67,) range=[0,) \
+                (x+(-32)) degF + stdtemp ; (tempF+(-stdtemp))/degF + 32
+tempfahrenheit() tempF
+degfahrenheit           5|9 degC
+degF                    5|9 degC
+
+
+degreesrankine          degF              # The Rankine scale has the
+degrankine              degreesrankine    # Fahrenheit degree, but its zero
+degreerankine           degF              # is at absolute zero.
+degR                    degrankine
+tempR                   degrankine
+temprankine             degrankine
+
+tempreaumur(x)    units=[1;K] domain=[-218.52,) range=[0,) \
+                  x degreaumur+stdtemp ; (tempreaumur+(-stdtemp))/degreaumur
+degreaumur              10|8 degC # The Reaumur scale was used in Europe and
+                                  # particularly in France.  It is defined
+                                  # to be 0 at the freezing point of water
+                                  # and 80 at the boiling point.  Reaumur
+                                  # apparently selected 80 because it is
+                                  # divisible by many numbers.
+
+degK                    K         # "Degrees Kelvin" is forbidden usage.
+tempK                   K         # For consistency
+
+# Gas mark is implemented below but in a terribly ugly way.  There is
+# a simple formula, but it requires a conditional which is not
+# presently supported.
+#
+# The formula to convert to degrees Fahrenheit is:
+#
+# 25 log2(gasmark) + k_f   gasmark<=1
+# 25 (gasmark-1) + k_f     gasmark>=1
+#
+# k_f = 275
+#
+gasmark[degR] \
+  .0625    634.67 \
+  .125     659.67 \
+  .25      684.67 \
+  .5       709.67 \
+  1        734.67 \
+  2        759.67 \
+  3        784.67 \
+  4        809.67 \
+  5        834.67 \
+  6        859.67 \
+  7        884.67 \
+  8        909.67 \
+  9        934.67 \
+  10       959.67
+
+
+# The Beaufort wind force scale was developed from 1805-1807 by Sir Francis
+# Beaufort to categorize wind conditions at sea. It is normally defined from
+# Beaufort 0, also called "Force 0," through Beaufort 12. Beaufort numbers
+# 13-17 were later defined for tropical cyclones but are rarely used. The
+# original Beaufort scale was qualitative and did not relate directly to wind
+# speed. In 1906, George Simpson of the British Met Office fit wind-speed
+# measurements to visual Beaufort estimates made from five coastal and inland
+# stations in Britain. Simpson's formula was adopted by the World Meterological
+# Organization in 1946 to produce a table, known as WMO Code 1100, giving mean
+# (and min/max) wind speed equivalents at a height of 10 meters for each
+# Beaufort number. This is the "operational" Beaufort scale that mariners
+# use. Meterological and climatic researchers typically use a "scientific"
+# Beaufort scale based on more recent and comprehensive fits. See Wallbrink and
+# Cook, Historical Wind Speed Equivalents Of The Beaufort Scale, 1850-1950, at
+# https://icoads.noaa.gov/reclaim/pdf/Hisklim13.pdf
+#
+beaufort_WMO1100(B) units=[1;m/s] domain=[0,17] range=[0,) \
+                    0.836 B^3|2 m/s; (beaufort_WMO1100 s / 0.836 m)^2|3
+
+beaufort(B) units=[1;m/s] domain=[0,17] range=[0,) \
+            beaufort_WMO1100(B); ~beaufort_WMO1100(beaufort)
+
+# Units cannot handle wind chill or heat index because they are two-variable
+# functions, but they are included here for your edification.  Clearly these
+# equations are the result of a model fitting operation.
+#
+# wind chill index (WCI) a measurement of the combined cooling effect of low
+#      air temperature and wind on the human body. The index was first defined
+#      by the American Antarctic explorer Paul Siple in 1939. As currently used
+#      by U.S. meteorologists, the wind chill index is computed from the
+#      temperature T (in  deg F) and wind speed V (in mi/hr) using the formula:
+#          WCI = 0.0817(3.71 sqrt(V) + 5.81 - 0.25V)(T - 91.4) + 91.4.
+#      For very low wind speeds, below 4 mi/hr, the WCI is actually higher than
+#      the air temperature, but for higher wind speeds it is lower than the air
+#      temperature.
+#
+# heat index (HI or HX) a measure of the combined effect of heat and
+#      humidity on the human body. U.S. meteorologists compute the index
+#      from the temperature T (in  deg F) and the relative humidity H (as a
+#      value from 0 to 1).
+#        HI = -42.379 + 2.04901523 T + 1014.333127 H - 22.475541 TH
+#             - .00683783 T^2 - 548.1717 H^2 + 0.122874 T^2 H + 8.5282 T H^2
+#             - 0.0199 T^2 H^2.
+
+#
+# Physical constants
+#
+
+# Basic constants
+
+pi                      3.14159265358979323846
+tau                     2 pi
+phi                     (sqrt(5)+1)/2
+light                   c
+coulombconst          alpha hbar c / e^2 # Coulomb constant          
+k_C                     coulombconst     #   Gets overridden in CGS modes
+k_C_SI       alpha hbar_SI c_SI / e_SI^2 
+epsilon0_SI             1 / 4 pi k_C_SI  # Vacuum electric permittivity
+epsilon0                1 / 4 pi k_C     #   Also overridden in CGS modes
+mu0_SI            1 / epsilon0_SI c_SI^2 # Vacuum magnetic permeability
+mu0                     1 / epsilon0 c^2 #   Also overridden in CGS modes
+Z0                      4 pi k_C / c     # Free space impedance
+energy                  c^2              # Convert mass to energy
+hbar                    h / 2 pi
+hbar_SI                 h_SI / 2 pi
+spin                    hbar
+G_SI            6.67430e-11
+G               6.67430e-11 N m^2 / kg^2 # Newtonian gravitational constant
+
+# Physico-chemical constants
+
+atomicmassunit_SI   1.66053906660e-27    # Unified atomic mass unit, defined as
+atomicmassunit      1.66053906660e-27 kg # Unified atomic mass unit, defined as
+u                       atomicmassunit   #   1|12 of the mass of carbon 12.
+amu                     atomicmassunit   #   The relationship N_A u = 1 g/mol
+dalton                  u                #   is approximately, but not exactly 
+Da                      dalton           #   true (with the 2019 SI).
+                                         #   Previously the mole was defined to
+                                         #   make this relationship exact.
+amu_chem                1.66026e-27 kg   # 1|16 of the weighted average mass of
+                                         #   the 3 naturally occuring neutral
+                                         #   isotopes of oxygen
+amu_phys                1.65981e-27 kg   # 1|16 of the mass of a neutral
+                                         #   oxygen 16 atom
+gasconstant             k N_A            # Molar gas constant (exact)
+R                       gasconstant
+kboltzmann              boltzmann
+molarvolume             R stdtemp / atm  # Volume occupied by one mole of an
+V_m                     molarvolume      #   ideal gas at STP. (exact)
+loschmidt         avogadro / molarvolume # Molecules per cubic meter of an
+n0                      loschmidt        #   ideal gas at STP.  Loschmidt did
+                                         #   work similar to Avogadro.
+molarvolume_si  N_A siliconlattice^3 / 8 # Volume of a mole of crystalline
+                                         #   silicon. The unit cell contains 8
+                                         #   silicon atoms and has a side
+                                         #   length of siliconlattice.
+stefanboltzmann pi^2 k^4 / 60 hbar^3 c^2 # The power per area radiated by a
+sigma                   stefanboltzmann  #   blackbody at temperature T is
+                                         #   given by sigma T^4. (exact)
+wiendisplacement     (h c/k)/4.9651142317442763  # Wien's Displacement Law gives
+                                         #   the frequency at which the
+                                         #   Planck spectrum has maximum
+                                         #   intensity.  The relation is lambda
+                                         #   T = b where lambda is wavelength,
+                                         #   T is temperature and b is the Wien
+                                         #   displacement.  This relation is
+                                         #   used to determine the temperature
+                                         #   of stars.  The constant is the
+                                         #   solution to x=5(1-exp(-x)).
+                                         #   This expression has no experimental
+                                         #   error, and x is defined exactly
+                                         #   by the equation above, so it is
+                                         #   an exact definition.  
+K_J90 483597.9 GHz/V    # Direct measurement of the volt is difficult.  Until
+K_J   2e/h              #   recently, laboratories kept Weston cadmium cells as
+                        #   a reference, but they could drift.  In 1987 the
+                        #   CGPM officially recommended the use of the
+                        #   Josephson effect as a laboratory representation of
+                        #   the volt.  The Josephson effect occurs when two
+                        #   superconductors are separated by a thin insulating
+                        #   layer.  A "supercurrent" flows across the insulator
+                        #   with a frequency that depends on the potential
+                        #   applied across the superconductors.  This frequency
+                        #   can be very accurately measured.  The Josephson
+                        #   constant K_J relates the measured frequency to the
+                        #   potential.  Two values given, the conventional
+                        #   (exact) value from 1990, which was used until the
+                        #   2019 SI revision, and the current exact value.
+R_K90 25812.807 ohm     # Measurement of the ohm also presents difficulties.
+R_K   h/e^2             #   The old approach involved maintaining resistances
+                        #   that were subject to drift.  The new standard is
+                        #   based on the Hall effect.  When a current carrying
+                        #   ribbon is placed in a magnetic field, a potential
+                        #   difference develops across the ribbon.  The ratio
+                        #   of the potential difference to the current is
+                        #   called the Hall resistance.  Klaus von Klitzing
+                        #   discovered in 1980 that the Hall resistance varies
+                        #   in discrete jumps when the magnetic field is very
+                        #   large and the temperature very low.  This enables
+                        #   accurate realization of the resistance h/e^2 in the
+                        #   lab.  The 1990 value was an exact conventional 
+                        #   value used until the SI revision in 2019. This value
+                        #   did not agree with measurements.  The new value
+                        #   is exact.
+
+# The 2019 update to SI gives exact definitions for R_K and K_J.  Previously
+# the electromagnetic units were realized using the 1990 conventional values
+# for these constants, and as a result, the standard definitions were in some
+# sense outside of SI.  The revision corrects this problem.  The definitions
+# below give the 1990 conventional values for the electromagnetic units in
+# terms of 2019 SI.  
+
+ampere90 (K_J90 R_K90 / K_J R_K) A
+coulomb90 (K_J90 R_K90 / K_J R_K) C
+farad90 (R_K90/R_K) F
+henry90 (R_K/R_K90) H
+ohm90 (R_K/R_K90) ohm
+volt90 (K_J90/K_J) V
+watt90 (K_J90^2 R_K90 / K_J^2 R_K) W
+
+# Various conventional values
+
+gravity                 9.80665 m/s^2    # std acceleration of gravity (exact)
+                                         #   Established by the 3rd CGPM in
+                                         #   1901.  This is a nominal midrange
+                                         #   value, originally based on the
+                                         #   acceleration of a body at sea
+                                         #   level at 45 degrees latitude.
+                                         #   The value was actually determined
+                                         #   by measuring at the International
+                                         #   Bureau and correcting the
+                                         #   measurement by a theoretical
+                                         #   cofficient to get the 45 deg
+                                         #   latitude sea level value.
+                                         #   (Wikipedia: Standard gravity)
+force                   gravity          # use to turn masses into forces
+atm                     101325 Pa        # Standard atmospheric pressure
+atmosphere              atm
+Hg             13.5951 gram force / cm^3 # Standard weight of mercury (exact)
+water                   gram force/cm^3  # Standard weight of water (exact)
+waterdensity            gram / cm^3      # Density of water
+H2O                     water
+wc                      water            # water column
+mach                    331.46 m/s       # speed of sound in dry air at STP
+standardtemp            273.15 K         # standard temperature
+stdtemp                 standardtemp
+normaltemp              tempF(70)        # for gas density, from NIST
+normtemp                normaltemp       # Handbook 44
+
+# Weight of mercury and water at different temperatures using the standard
+# force of gravity.
+
+Hg10C         13.5708 force gram / cm^3  # These units, when used to form
+Hg20C         13.5462 force gram / cm^3  # pressure measures, are not accurate
+Hg23C         13.5386 force gram / cm^3  # because of considerations of the
+Hg30C         13.5217 force gram / cm^3  # revised practical temperature scale.
+Hg40C         13.4973 force gram / cm^3
+Hg60F         13.5574 force gram / cm^3
+H2O0C         0.99987 force gram / cm^3
+H2O5C         0.99999 force gram / cm^3
+H2O10C        0.99973 force gram / cm^3
+H2O15C        0.99913 force gram / cm^3
+H2O18C        0.99862 force gram / cm^3
+H2O20C        0.99823 force gram / cm^3
+H2O25C        0.99707 force gram / cm^3
+H2O50C        0.98807 force gram / cm^3
+H2O100C       0.95838 force gram / cm^3
+
+# Atomic constants
+
+hartree            4.3597447222071e-18 J # Approximate electric potential energy
+E_h                     hartree          #   of the hydrogen atom in its ground
+                                         #   state, and approximately twice its
+                                         #   ionization energy.  The hartree
+                                         #   energy is traditionally defined as
+                                         #      coulombconst^2 m_e e^4 / hbar^2,
+                                         #   but it can be measured to greater
+                                         #   precision using the relationship
+                                         #      hartree = 2 h c Rinfinity
+                                         #   because Rinfinity is one of the
+                                         #   most accurately measured physical
+                                         #   constants.  Because h and c are
+                                         #   exact we can choose either hartree
+                                         #   or Rinfinity from CODATA to use as
+                                         #   the primary value without
+                                         #   affecting the precision.  
+Rinfinity               hartree / 2 h c  # The wavelengths of a spectral series
+R_H          Rinfinity m_p / (m_e + m_p) #   can be expressed as
+                                         #     1/lambda = R (1/m^2 - 1/n^2).
+                                         #   where R is a number that various
+                                         #   slightly from element to element.
+                                         #   For hydrogen, R_H is the value,
+                                         #   and for heavy elements, the value
+                                         #   approaches Rinfinity, which can be
+                                         #   computed from
+                                         #      Rinfinity = m_e c alpha^2 / 2 h
+                                         #   with loss of precision. Rinfinity
+                                         #   is one of the most accurately
+                                         #   measured physical constants and is
+                                         #   known to higher precision than m_e
+                                         #   or alpha.
+alpha                    7.2973525693e-3 # The fine structure constant was
+                                         #   introduced to explain fine
+                                         #   structure visible in spectral
+                                         #   lines.  
+bohrradius              hbar / alpha m_e c
+a0                      bohrradius
+prout                   185.5 keV        # nuclear binding energy equal to 1|12
+                                         #   binding energy of the deuteron
+conductancequantum      e^2 / pi hbar
+G0                      conductancequantum
+magneticfluxquantum     pi hbar / e
+Phi0                    magneticfluxquantum
+
+# Particle radius
+
+electronradius    coulombconst e^2 / electronmass c^2   # Classical
+deuteronchargeradius    2.12799e-15 m
+protonchargeradius      0.8751e-15 m
+
+# Masses of elementary particles
+
+electronmass_SI         electronmass_u atomicmassunit_SI
+electronmass_u          5.48579909065e-4
+electronmass            5.48579909065e-4 u
+m_e                     electronmass
+muonmass                0.1134289259 u
+m_mu                    muonmass
+taumass                 1.90754 u
+m_tau                   taumass
+protonmass              1.007276466621 u
+m_p                     protonmass
+neutronmass             1.00866491595 u
+m_n                     neutronmass
+deuteronmass            2.013553212745 u    # Nucleus of deuterium, one
+m_d                     deuteronmass        #   proton and one neutron
+alphaparticlemass       4.001506179127 u    # Nucleus of He, two protons
+m_alpha                 alphaparticlemass   #   and two neutrons
+tritonmass              3.01550071621 u     # Nucleus of H3, one proton
+m_t                     tritonmass          #   and two neutrons
+helionmass              3.014932247175 u    # Nucleus of He3, two protons
+m_h                     helionmass          #   and one neutron
+
+# particle wavelengths: the compton wavelength of a particle is
+# defined as h / m c where m is the mass of the particle.
+
+electronwavelength      h / m_e c
+lambda_C                electronwavelength
+protonwavelength        h / m_p c
+lambda_C,p              protonwavelength
+neutronwavelength       h / m_n c
+lambda_C,n              neutronwavelength
+muonwavelength          h / m_mu c
+lambda_C,mu             muonwavelength
+
+# The g-factor or dimensionless magnetic moment is a quantity that
+# characterizes the magnetic moment of a particle.  The electron g-factor is
+# one of the most precisely measured values in physics, with a relative
+# uncertainty of 1.7e-13.  
+
+g_d                     0.8574382338       # Deuteron g-factor
+g_e                    -2.00231930436256   # Electron g-factor
+g_h                    -4.255250615        # Helion g-factor
+g_mu                   -2.0023318418       # Muon g-factor
+g_n                    -3.82608545         # Neutron g-factor
+g_p                     5.5856946893       # Proton g-factor
+g_t                     5.957924931        # Triton g-factor
+
+fermicoupling           1.1663787e-5 / GeV^2
+
+# Magnetic moments (derived from the more accurate g-factors)
+#
+# The magnetic moment is g * mu_ref * spin where in most cases
+# the reference is the nuclear magneton, and all of the particles
+# except the deuteron have spin 1/2.  
+
+bohrmagneton            e hbar / 2 electronmass  # Reference magnetic moment for
+mu_B                    bohrmagneton             #   the electron
+mu_e                    g_e mu_B / 2             # Electron spin magnet moment
+mu_mu                 g_mu mu_B m_e / 2 muonmass # Muon spin magnetic moment
+nuclearmagneton         mu_B m_e / protonmass    # Convenient reference magnetic
+mu_N                    nuclearmagneton          #   moment for heavy particles
+mu_p                    g_p mu_N / 2             # Proton magnetic moment
+mu_n                    g_n mu_N / 2             # Neutron magnetic moment
+mu_d                    g_d mu_N            # Deuteron magnetic moment, spin 1
+mu_t                    g_t mu_N / 2             # Triton magnetic moment
+mu_h                    g_h mu_N / 2             # Helion magnetic moment
+
+#
+# Units derived from physical constants
+#
+
+kgf                     kg force
+technicalatmosphere     kgf / cm^2
+at                      technicalatmosphere
+hyl                     kgf s^2 / m   # Also gram-force s^2/m according to [15]
+mmHg                    mm Hg
+torr                    atm / 760  # The torr, named after Evangelista
+                                   # Torricelli, and is very close to the mm Hg
+tor                     Pa         # Suggested in 1913 but seldom used [24].
+                                   # Eventually renamed the Pascal.  Don't 
+                                   # confuse the tor with the torr.  
+inHg                    inch Hg    
+inH2O                   inch water
+mmH2O                   mm water
+eV                      e V      # Energy acquired by a particle with charge e
+electronvolt            eV       #   when it is accelerated through 1 V
+lightyear               c julianyear # The 365.25 day year is specified in
+ly                      lightyear    # NIST publication 811
+lightsecond             c s
+lightminute             c min
+parsec                  au / tan(arcsec)    # Unit of length equal to distance
+pc                      parsec              #   from the Sun to a point having
+                                            #   heliocentric parallax of 1
+                                            #   arcsec (derived from parallax
+                                            #   second).  A distant object with
+                                            #   parallax theta will be about
+                                            #   (arcsec/theta) parsecs from the
+                                            #   Sun (using the approximation
+                                            #   that tan(theta) = theta).
+rydberg                 1|2 hartree         # Rydberg energy
+crith                   0.089885 gram       # The crith is the mass of one
+                                            #   liter of hydrogen at standard
+                                            #   temperature and pressure.
+amagat                  N_A / molarvolume   # Used to measure gas as a number
+amagatvolume            mol molarvolume     #   density
+lorentz                 bohrmagneton / h c  # Used to measure the extent
+                                            #   that the frequency of light
+                                            #   is shifted by a magnetic field.
+cminv                   h c / cm            # Unit of energy used in infrared
+invcm                   cminv               #   spectroscopy.
+wavenumber              1/cm                # 
+kcal_mol                kcal_th / mol N_A   # kcal/mol is used as a unit of
+                                            #   energy by physical chemists.
+#
+# CGS system based on centimeter, gram and second
+#
+
+dyne                    cm gram / s^2   # force
+dyn                     dyne
+erg                     cm dyne         # energy
+poise                   gram / cm s     # viscosity, honors Jean Poiseuille
+P                       poise
+rhe                     /poise          # reciprocal viscosity
+stokes                  cm^2 / s        # kinematic viscosity
+St                      stokes
+stoke                   stokes
+lentor                  stokes          # old name
+Gal                     cm / s^2        # acceleration, used in geophysics
+galileo                 Gal             # for Earth's gravitational field
+                                        # (note that "gal" is for gallon
+                                        # but "Gal" is the standard symbol
+                                        # for the gal which is evidently a
+                                        # shortened form of "galileo".)
+barye                   dyne/cm^2       # pressure
+barad                   barye           # old name
+kayser                  1/cm            # Proposed as a unit for wavenumber
+balmer                  kayser          # Even less common name than "kayser"
+kine                    cm/s            # velocity
+bole                    g cm / s        # momentum
+pond                    gram force
+glug                gram force s^2 / cm # Mass which is accelerated at
+                                        #   1 cm/s^2 by 1 gram force
+darcy           centipoise cm^2 / s atm # Measures permeability to fluid flow.
+                                        #   One darcy is the permeability of a
+                                        #   medium that allows a flow of cc/s
+                                        #   of a liquid of centipoise viscosity
+                                        #   under a pressure gradient of
+                                        #   atm/cm.  Named for H. Darcy.
+mobileohm               cm / dyn s      # mobile ohm, measure of mechanical
+                                        #   mobility
+mechanicalohm           dyn s / cm      # mechanical resistance
+acousticalohm           dyn s / cm^5    # ratio of the sound pressure of
+                                        #   1 dyn/cm^2 to a source of strength
+                                        #   1 cm^3/s
+ray                     acousticalohm
+rayl                    dyn s / cm^3    # Specific acoustical resistance
+eotvos                  1e-9 Gal/cm     # Change in gravitational acceleration
+                                        #   over horizontal distance
+#
+# Electromagnetic CGS Units
+# 
+# For measuring electromagnetic quantities in SI, we introduce the new base
+# dimension of current, define the ampere to measure current, and derive the
+# other electromagnetic units from the ampere.  With the CGS units one approach
+# is to use the basic equations of electromagnetism to define units that
+# eliminate constants from those equations.  Coulomb's law has the form
+#
+#          F = k_C q1 q2 / r^2
+#
+# where k_C is the Coulomb constant equal to 1|4 pi epsilon0 in SI units.
+# Ampere's force law takes the form
+#
+#          dF/dl = 2 k_A I1 I2 / r
+#
+# where k_A is the ampere constant.  In the CGS system we force either k_C or
+# k_A to 1 which then defines either a unit for charge or a unit for current.
+# The other unit then becomes a derived unit.  When k_C is 1 the ESU system
+# results.  When k_A is 1 the EMU system results.  Note that these parameters
+# are not independent of each other: Maxwell's equations indicate that
+#         
+#           k_C / k_A = c^2
+#
+# where c is the speed of light. 
+#
+# One more choice is needed to define a complete system.  Using Coulomb's law
+# we define the electric field as the force per unit charge
+#
+#           E = k_C 1 / r^2.
+#
+# But what about the magnetic field?  It is derived from Ampere's law but we
+# have the option of adding a proportionality constant, k_B, that may have
+# dimensions:
+#
+#           B = 2 k_A k_B I / r
+#
+# We can choose k_B = 1, which is done in the SI, ESU and EMU systems.  But if
+# instead we give k_B units of length/time then the magnetic field has
+# the same units as the electric field.  This choice leads to the Gaussian
+# and Heaviside-Lorentz systems.
+#
+# The relations above are used to determine the dimensions, but the units are
+# derived from the base units of CGS, not directly from those formulas.  We
+# will use the notation [unit] to refer to the dimension of the unit in
+# brackets.  This same process gives rise to the SI units such as the tesla, 
+# which is defined by
+#
+#         [tesla] = [2 (1/4 pi c^2 epsilon0) amp / m] = [(mu0 / 2) amp / m]
+#
+# which gives kg / A s^2 as expected.  
+#
+# References:
+#
+# Classical Electrodynamics by John David Jackson, 3rd edition. 
+# Cardarelli, Francois. 1999.  Scientific Unit Conversion. 2nd ed.  Trans.
+#     M.J.  Shields.  London: Springer-Verlag. ISBN 1-85233-043-0
+#
+#
+# All of the CGS systems result in electromagnetic units that involve the square
+# roots of the centimeter and gram.  This requires a change in the primitive
+# units.
+# 
+
+!var UNITS_SYSTEM esu emu gaussian gauss hlu
+sqrt_cm                 ! 
+sqrt_centimeter         sqrt_cm
++m                      100 sqrt_cm^2
+sqrt_g                  !
+sqrt_gram               sqrt_g
++kg                     kilo sqrt_g^2
+!endvar
+
+# Electrostatic CGS (ESU)
+#
+# This system uses the statcoulomb as the fundamental unit of charge, with
+# derived units that parallel the conventional terminology but use the stat-
+# prefix.  The statcoulomb is derived from Coulomb's law based on the dyne
+#
+#                      dyne = statcoulomb^2 / k_C cm^2.
+#
+# and in the EUS system, k_C=1.  The statcoulomb is also called the
+# franklin or esu.
+#
+# The ESU system was specified by a committee report in 1873 and rarely used.
+
+statcoulomb             sqrt(dyne cm^2/k_C)   # Charge such that two charges
+esu                     statcoulomb           # of 1 statC separated by 1 cm
+statcoul                statcoulomb           # exert a force of 1 dyne
+statC                   statcoulomb
+stC                     statcoulomb
+franklin                statcoulomb
+Fr                      franklin
+
+!var UNITS_SYSTEM esu
+!message CGS-ESU units selected
+!prompt (ESU)
++coulombconst           1
++epsilon0               1 / k_C               # SI relation: 1 / 4 pi k_C
++A                      10 c_SI statamp
+!endvar
+
+statampere              statcoulomb / s
+statamp                 statampere
+statA                   statampere
+stA                     statampere
+statvolt                dyne cm / statamp sec
+statV                   statvolt
+stV                     statvolt
+statfarad               statamp sec / statvolt
+statF                   statfarad
+stF                     statfarad
+cmcapacitance           statfarad
+stathenry               statvolt sec / statamp
+statH                   stathenry
+stH                     stathenry
+statohm                 statvolt / statamp
+stohm                   statohm
+statmho                 /statohm
+stmho                   statmho
+statweber               statvolt sec
+statWb                  statweber
+stWb                    statweber
+stattesla               statWb/cm^2   # Defined by analogy with SI; rarely
+statT                   stattesla     #   if ever used
+stT                     stattesla
+debye                   1e-10 statC angstrom # unit of electrical dipole moment
+helmholtz               debye/angstrom^2     # Dipole moment per area
+jar                     1000 statfarad       # approx capacitance of Leyden jar
+
+# Electromagnetic CGS (EMU)
+#
+# The abampere is the fundamental unit of this system, with the derived units
+# using the ab- prefix.  The dimensions of the abampere are defined by assuming
+# that k_A=1, which 
+#
+#            [dyne / cm]  = [2 abampere^2 / cm]
+#
+# where the brackets indicate taking the dimension of the unit in base units
+# and discarding any constant factors.  This results in the definition from
+# base CGS units of:
+#
+#            abampere = sqrt(dyne). 
+#
+# The abampere is also called the biot.  The magnetic field unit (the gauss)
+# follows from the assumption that k_B=1, which means
+#
+#            B = 2 I / r,
+#
+# and hence the dimensions of the gauss are given by
+#
+#            [gauss] = [2 abampere / cm]
+#
+# or rewriting in terms of the base units
+#
+#            gauss = abampere / cm.
+#
+# The definition given below is different because it is in a form that
+# gives a valid reduction for SI and ESU and still gives the correct 
+# result in EMU.  (It can be derived from Faraday's law.)  
+#
+# The EMU system was developed by Gauss and Weber and formalized as a system in
+# a committee report by the British Association for the Advancement of Science
+# in 1873.  
+
+abampere                10 A            # Current which produces a force of
+abamp                   abampere        #   2 dyne/cm between two infinitely
+aA                      abampere        #   long wires that are 1 cm apart
+abA                     abampere
+biot                    abampere
+Bi                      biot
+
+!var UNITS_SYSTEM emu
+!message CGS-EMU units selected
+!prompt (EMU)
++coulombconst           c^2
++epsilon0               1 / k_C        # SI relation: 1 / 4 pi k_C
++abampere               sqrt(dyne)
++A                      0.1 abamp
+!endvar
+
+abcoulomb               abamp sec
+abcoul                  abcoulomb
+abC                     abcoulomb
+abfarad                 abampere sec / abvolt
+abF                     abfarad
+abhenry                 abvolt sec / abamp
+abH                     abhenry
+abvolt                  dyne cm  / abamp sec
+abV                     abvolt
+abohm                   abvolt / abamp
+abmho                   /abohm
+maxwell                 erg / abamp       # Also called the "line"
+Mx                      maxwell
+gauss                   maxwell / cm^2    # The magnetic field 2 cm from a wire
+Gs                      gauss             # carrying a current of 1 abampere
+oersted                 gauss / mu0   # From the relation H = B / mu
+Oe                      oersted      
+gilbert                 gauss cm / mu0
+Gb                      gilbert
+Gi                      gilbert
+unitpole                4 pi maxwell    # unit magnetic pole
+emu                     erg/gauss  # "electro-magnetic unit", a measure of
+                                   # magnetic moment, often used as emu/cm^3
+                                   # to specify magnetic moment density.
+
+# Electromagnetic CGS (Gaussian)
+#
+# The Gaussian system uses the statcoulomb and statamp from the ESU system
+# derived by setting k_C=1, but it defines the magnetic field unit differently
+# by taking k_B=c instead of k_B=1.  As noted above, k_C and k_A are not
+# independent.  With k_C=1 we must have k_A=c^-2.  This results in the magnetic
+# field unit, the gauss, having dimensions give by:
+#
+#         [gauss] = [2 (c^-2) c statamp / cm] = [statamp / c cm]
+#
+# We then define the gauss using base CGS units to obtain
+#
+#         gauss = statamp / ((cm/s) cm) = statcoulomb / cm^2.
+#
+# Note that this definition happens to give the same result as the definition
+# for the EMU system, so the definitions of the gauss are consistent.
+#
+# This definition gives the same dimensions for the E and B fields and was also
+# known as the "symmetric system".  This system was proposed by Hertz in 1888.
+
+!var UNITS_SYSTEM gaussian gauss
+!message CGS-Gaussian units selected
+!prompt (Gaussian)
+!endvar
+!var UNITS_SYSTEM gaussian gauss natural-gauss 
++coulombconst           1
++A                      10 c_SI statamp
+      # Some SI-based definitions need re-scaling
+      # by factors of "c" and/or "4 pi":
++epsilon0               1 / k_C        # SI relation: 1 / 4 pi k_C
++mu0                    1 / epsilon0   # SI relation: 1 / epsilon0 c^2
++bohrmagneton           (e hbar / 2 electronmass) / c
++magneticfluxquantum    c (pi hbar / e)
++maxwell                c (erg / abamp)
++weber                  c (J / A)
+!endvar
+
+# Electromagnetic CGS (Heaviside-Lorentz)
+
+# The Heaviside-Lorentz system is similar to the Gaussian system, but it is
+# "rationalized" so that factors of 4 pi do not appear in Maxwell's equations.
+# The SI system is similarly rationalized, but the other CGS systems are not.
+#
+# The factor of 4 pi appears instead in Coulomb's law, so in this system
+# k_C = 1 / 4 pi, which means the charge unit is defined by
+#
+#                      dyne = (1 / 4 pi) hlu_charge^2 / cm^2.
+# 
+# Since we have the leading constant of (1 / 4pi) the numerical value of the
+# charge number is larger by sqrt(4pi), which in turns means that the HLU
+# charge unit is smaller by this multiple.  But note that the dimensions of the
+# charge unit are the same as the Gaussian system, so both systems measure
+# charge with cm^(3/2) g^(1/2) / s, but the amount of charge for this dimension
+# differs by a factor of sqrt(4pi) between the two systems.
+#
+# Ampere's law for the Heaviside-Lorentz system has the form
+#
+#                B = 1/(2 pi c) * I/r
+
+# The Heaviside-Lorentz system does not appear to have any named units, so we
+# use "hlu" for "Heaviside-Lorentz unit" so we can define values for the basic
+# units in this system.
+
+hlu_charge    statcoulomb / sqrt(4 pi) 
+hlu_current   hlu_charge / sec
+hlu_volt      erg / hlu_charge
+hlu_efield    hlu_volt / cm
+hlu_bfield    sqrt(4 pi) gauss
+
+!var UNITS_SYSTEM hlu
+!message CGS-Heaviside-Lorentz Units selected
+!prompt (HLU)
+!endvar
+!var UNITS_SYSTEM hlu natural planck planck-red
++coulombconst           1 / 4 pi
++A                      10 c_SI statamp 
+        # Some SI-based magnetism definitions
+        # need re-scaling by factors of "c":
++mu0                    1 / epsilon0   # SI relation: 1 / epsilon0 c^2
++bohrmagneton           (e hbar / 2 electronmass) / c
++magneticfluxquantum    c (pi hbar / e)
++weber                  c (J / A)
++maxwell                c (erg / abamp)
+!endvar
+
+# "Natural units" (high energy physics and cosmology)
+#
+# In particle physics "natural units" (which don't seem to have a more specific
+# name) are defined by setting hbar = c = boltzmann = 1.  In this system the
+# electron volt is the only base unit.  The electromagnetic units can be 
+# derived from the rationalized Heaviside-Lorentz units or from Gaussian units.
+# The default form is the rationalized HLU derived version.
+#
+# The basic mechanical and thermodynamic definitions for the natural
+# units are identical in both systems.  These appear below.  The
+# natural-gauss system has additional electromagnetic redefinitions
+# that appear above in the "Electromagnetic CGS (Gaussian)" Section.
+
+# These are the Heaviside-Lorentz natural units
+
+natural_energy          eV
+natural_charge          e / sqrt(4 pi alpha)
+natural_time            hbar / natural_energy
+natural_length          natural_time c
+natural_mass            natural_energy / c^2
+natural_temp            natural_energy / boltzmann
+natural_force           natural_energy / natural_length
+natural_power           natural_energy / natural_time
+natural_volt            natural_energy / natural_charge
+natural_Efield          natural_volt   / natural_length
+natural_Bfield          natural_Efield / c
+natural_current         natural_charge / natural_time
+
+!var UNITS_SYSTEM natural
+!message Natural units selected (Heavyside-Lorentz based)
+!prompt (natural)
+!endvar
+
+!var UNITS_SYSTEM natural-gauss
+!message Natural units selected (Gaussian based)
+!prompt (natgauss)
+!endvar
+
+# These definitions are the same in both natural unit systems
+
+!var UNITS_SYSTEM natural natural-gauss
++eV                     !
++h                      2 pi
++c                      1
++boltzmann              1
++m                      e_SI / hbar_SI c_SI eV
++kg                     (c_SI^2 / e_SI) eV
++s                      e_SI / hbar_SI eV
++K                      (k_SI / e_SI) eV
+!endvar
+
+#
+# Planck units
+#
+# Planck units are a set of "natural" units based on physical constants c, G,
+# hbar, boltzmann's constant, and epsilon0, often used when working with
+# gravitational theory.  In planck units, all quantities are dimensionless.
+# Some variations are possible for exactly how the units are defined.  We
+# provide two variations, the rationalized planck units and the
+# rationalized-reduced planck units.
+#
+# In both forms the units are defined by c = hbar = boltzmann = 1. 
+# But the choice of rationalized and reduced affects how epsilon0 and G
+# are treated.  
+#
+# In the "rationalized" units, factors of 4 pi do not appear in Maxwell's 
+# equation, and Coulomb's law bears a factor of 1/4 pi.  See the section on
+# the Heaviside-Lorentz units for more about this.  The choice of rationalized 
+# units means that epsilon0 = 1.  (In the unrationalized case, which is not
+# supported, 1/(4 pi epsilon0) = 1.)  
+#
+# The "reduced" units similarly are defined to eliminate factors of 8 pi
+# from the Einstein field equations for gravitation.  With reduced units
+# we set 8 pi G = 1 and with the unreduced units, simply G = 1.
+
+# Rationalized, unreduced planck units
+
+planckmass              sqrt(hbar c / G)
+m_P                     planckmass
+planckenergy            planckmass c^2
+E_P                     planckenergy
+plancktime              hbar / planckenergy
+t_P                     plancktime
+plancklength            plancktime c
+l_P                     plancklength
+plancktemperature       planckenergy / k
+T_P                     plancktemperature
+planckforce             planckenergy / plancklength
+planckcharge            sqrt(epsilon0 hbar c)
+planckcurrent           planckcharge / plancktime
+planckvolt              planckenergy / planckcharge
+planckEfield            planckvolt / plancklength
+planckBfield            planckEfield / c
+
+# Rationalized, reduced planck units
+
+planckmass_red          sqrt(hbar c / 8 pi G)
+planckenergy_red        planckmass_red c^2
+plancktime_red          hbar / planckenergy_red
+plancklength_red        plancktime_red c
+plancktemperature_red   planckenergy_red / k
+planckforce_red         planckenergy_red / plancklength_red
+planckcharge_red        sqrt(epsilon0 hbar c)
+planckcurrent_red       planckcharge_red / plancktime_red
+planckvolt_red          planckenergy_red / planckcharge_red
+planckEfield_red        planckvolt_red / plancklength_red
+planckBfield_red        planckEfield_red /c
+
+
+!var UNITS_SYSTEM planck
+!message Planck units selected
+!prompt (planck)
++c 1
++h 2 pi
++G 1
++boltzmann 1
++kg sqrt(G_SI / hbar_SI c_SI)
++s  c_SI^2 / hbar_SI kg 
++m  s / c_SI            
++K  k_SI / hbar_SI s    
+!endvar
+
+
+!var UNITS_SYSTEM planck-red
+!message Reduced planck units selected
+!prompt (planck reduced)
++c 1
++h 2 pi
++G 1/8 pi
++boltzmann 1
++kg sqrt(8 pi G_SI / hbar_SI c_SI)
++s  c_SI^2 / hbar_SI kg
++m  s / c_SI           
++K  k_SI / hbar_SI s   
+!endvar
+
+#
+# Some historical electromagnetic units
+#
+
+intampere               0.999835 A    # Defined as the current which in one
+intamp                  intampere     #   second deposits .001118 gram of
+                                      #   silver from an aqueous solution of
+                                      #   silver nitrate.
+intfarad                0.999505 F
+intvolt                 1.00033 V
+intohm                  1.000495 ohm  # Defined as the resistance of a
+                                      #   uniform column of mercury containing
+                                      #   14.4521 gram in a column 1.063 m
+                                      #   long and maintained at 0 degC.
+daniell                 1.042 V       # Meant to be electromotive force of a
+                                      #   Daniell cell, but in error by .04 V
+faraday                 N_A e mol     # Charge that must flow to deposit or
+faraday_phys            96521.9 C     #   liberate one gram equivalent of any
+faraday_chem            96495.7 C     #   element.  (The chemical and physical
+faradayconst            N_A e         #   values are off slightly from what is
+                                      #   obtained by multiplying by amu_chem
+                                      #   or amu_phys.  These values are from
+                                      #   a 1991 NIST publication.)  Note that
+                                      #   there is also a Faraday constant,
+                                      #   which has units of C/mol.  
+kappline                6000 maxwell  # Named by and for Gisbert Kapp
+siemensunit             0.9534 ohm    # Resistance of a meter long column of
+                                      #   mercury with a 1 mm cross section.
+#
+# Printed circuit board units.  
+#
+# Iowa State University Center for Nondestructive Evaluation
+# Electrical Conductivity and Resistivity
+# https://www.nde-ed.org/Physics/Materials/Physical_Chemical/Electrical.xhtml
+#
+# Conductivity is often expressed as a percentage of IACS.  A copper wire a
+# meter long with a 1 mm^2 cross section has a resistance of 1|58 ohm at 
+# 20 deg C.  Copper density also has a standard IACS value at that temperature.
+#
+
+copperconductivity      58 siemens m / mm^2     # A wire a meter long with
+IACS                    copperconductivity      #   a 1 mm^2 cross section
+copperdensity           8.89 g/cm^3             # The "ounce" measures the     
+ouncecopper             oz / ft^2 copperdensity #   thickness of copper used   
+ozcu                    ouncecopper             #   in circuitboard fabrication
+                                                
+#
+# Photometric units
+#
+
+LUMINOUS_INTENSITY      candela
+LUMINOUS_FLUX           lumen
+LUMINOUS_ENERGY         talbot
+ILLUMINANCE             lux
+EXITANCE                lux
+
+candle                  1.02 candela  # Standard unit for luminous intensity
+hefnerunit              0.9 candle    #   in use before candela
+hefnercandle            hefnerunit    #
+violle                  20.17 cd      # luminous intensity of 1 cm^2 of
+                                      #   platinum at its temperature of
+                                      #   solidification (2045 K)
+
+lumen                   cd sr         # Luminous flux (luminous energy per
+lm                      lumen         #    time unit)
+
+talbot                  lumen s       # Luminous energy
+lumberg                 talbot        # References give these values for
+lumerg                  talbot        #    lumerg and lumberg both.  Note that
+                                      #    a paper from 1948 suggests that
+                                      #    lumerg should be 1e-7 talbots so
+                                      #    that lumergs/erg = talbots/joule.
+                                      #    lumerg = luminous erg
+lux                     lm/m^2        # Illuminance or exitance (luminous
+lx                      lux           #   flux incident on or coming from
+phot                    lumen / cm^2  #   a surface)
+ph                      phot          #
+footcandle              lumen/ft^2    # Illuminance from a 1 candela source
+                                      #    at a distance of one foot
+metercandle             lumen/m^2     # Illuminance from a 1 candela source
+                                      #    at a distance of one meter
+
+mcs                     metercandle s # luminous energy per area, used to
+                                      #    measure photographic exposure
+
+nox                     1e-3 lux      # These two units were proposed for
+skot                    1e-3 apostilb # measurements relating to dark adapted
+                                      # eyes.
+# Luminance measures
+
+LUMINANCE               nit
+
+nit                     cd/m^2        # Luminance: the intensity per projected
+stilb                   cd / cm^2     # area of an extended luminous source.
+sb                      stilb         # (nit is from latin nitere = to shine.)
+
+apostilb                cd/pi m^2
+asb                     apostilb
+blondel                 apostilb      # Named after a French scientist.
+
+# Equivalent luminance measures.  These units are units which measure
+# the luminance of a surface with a specified exitance which obeys
+# Lambert's law.  (Lambert's law specifies that luminous intensity of
+# a perfectly diffuse luminous surface is proportional to the cosine
+# of the angle at which you view the luminous surface.)
+
+equivalentlux           cd / pi m^2   # luminance of a 1 lux surface
+equivalentphot          cd / pi cm^2  # luminance of a 1 phot surface
+lambert                 cd / pi cm^2
+footlambert             cd / pi ft^2
+
+# The bril is used to express "brilliance" of a source of light on a
+# logarithmic scale to correspond to subjective perception.  An increase of 1
+# bril means doubling the luminance.  A luminance of 1 lambert is defined to
+# have a brilliance of 1 bril.
+
+bril(x) units=[1;lambert]  2^(x+-100) lamberts ;log2(bril/lambert)+100
+
+# Some luminance data from the IES Lighting Handbook, 8th ed, 1993
+
+sunlum                  1.6e9 cd/m^2  # at zenith
+sunillum                100e3 lux     # clear sky
+sunillum_o              10e3 lux      # overcast sky
+sunlum_h                6e6 cd/m^2    # value at horizon
+skylum                  8000 cd/m^2   # average, clear sky
+skylum_o                2000 cd/m^2   # average, overcast sky
+moonlum                 2500 cd/m^2
+
+#
+# Photographic Exposure Value
+# This section by Jeff Conrad (jeff_conrad@msn.com)
+#
+# The Additive system of Photographic EXposure (APEX) proposed in ASA
+# PH2.5-1960 was an attempt to simplify exposure determination for people who
+# relied on exposure tables rather than exposure meters.  Shortly thereafter,
+# nearly all cameras incorporated exposure meters, so the APEX system never
+# caught on, but the concept of exposure value remains in use.  Though given as
+# 'Ev' in ASA PH2.5-1960, it is now more commonly indicated by 'EV'.  EV is
+# related to exposure parameters by
+#
+#            A^2   LS   ES
+#     2^EV = --- = -- = --
+#             t    K    C
+#
+# Where
+#     A = Relative aperture (f-number)
+#     t = Exposure time in seconds
+#     L = Scene luminance in cd/m2
+#     E = Scene illuminance in lux
+#     S = Arithmetic ISO speed
+#     K = Reflected-light meter calibration constant
+#     C = Incident-light meter calibration constant
+#
+# Strictly, an exposure value is a combination of aperture and exposure time,
+# but it's also commonly used to indicate luminance (or illuminance).
+# Conversion to luminance or illuminance units depends on the ISO speed and the
+# meter calibration constant.  Common practice is to use an ISO speed of 100.
+# Calibration constants vary among camera and meter manufacturers: Canon,
+# Nikon, and Sekonic use a value of 12.5 for reflected-light meters, while
+# Kenko (formerly Minolta) and Pentax use a value of 14.  Kenko and Sekonic use
+# a value of 250 for incident-light meters with flat receptors.
+#
+# The values for in-camera meters apply only averaging, weighted-averaging, or
+# spot metering--the multi-segment metering incorporated in most current
+# cameras uses proprietary algorithms that evaluate many factors related to the
+# luminance distribution of what is being metered; they are not amenable to
+# simple conversions, and are usually not disclosed by the manufacturers.
+
+s100                    100 / lx s            # ISO 100 speed
+iso100                  s100
+
+# Reflected-light meter calibration constant with ISO 100 speed
+
+k1250                   12.5 (cd/m2) / lx s   # For Canon, Nikon, and Sekonic
+k1400                   14   (cd/m2) / lx s   # For Kenko (Minolta) and Pentax
+
+# Incident-light meter calibration constant with ISO 100 film
+
+c250                    250 lx / lx s         # flat-disc receptor
+
+# Exposure value to scene luminance with ISO 100 imaging media
+
+# For Kenko (Minolta) or Pentax
+#ev100(x) units=[;cd/m^2] range=(0,) 2^x k1400 / s100; log2(ev100 s100/k1400)
+# For Canon, Nikon, or Sekonic
+ev100(x) units=[1;cd/m^2] range=(0,) 2^x k1250 / s100; log2(ev100 s100/k1250)
+EV100()  ev100
+
+# Exposure value to scene illuminance with ISO 100 imaging media
+
+iv100(x) units=[1;lx] range=(0,) 2^x c250 / s100; log2(iv100 s100 / c250)
+
+# Other Photographic Exposure Conversions
+# 
+# As part of APEX, ASA PH2.5-1960 proposed several logarithmic quantities
+# related by
+#
+#    Ev = Av + Tv = Bv + Sv
+#
+# where
+#  Av = log2(A^2)       Aperture value
+#  Tv = log2(1/t)       Time value
+#  Sv = log2(N Sx)      Speed value
+#  Bv = log2(B S / K)   Luminance ("brightness") value
+#  Iv = log2(I S / C)   Illuminance value
+#
+# and
+#  A  = Relative aperture (f-number)
+#  t  = Exposure time in seconds
+#  Sx = Arithmetic ISO speed in 1/lux s
+#  B  = luminance in cd/m2
+#  I  = luminance in lux
+
+# The constant N derives from the arcane relationship between arithmetic
+# and logarithmic speed given in ASA PH2.5-1960.  That relationship
+# apparently was not obvious--so much so that it was thought necessary
+# to explain it in PH2.12-1961.  The constant has had several values
+# over the years, usually without explanation for the changes.  Although
+# APEX had little impact on consumer cameras, it has seen a partial
+# resurrection in the Exif standards published by the Camera & Imaging
+# Products Association of Japan.
+
+#N_apex         2^-1.75 lx s    # precise value implied in ASA PH2.12-1961,
+                                # derived from ASA PH2.5-1960.
+#N_apex         0.30 lx s       # rounded value in ASA PH2.5-1960, 
+                                # ASA PH2.12-1961, and ANSI PH2.7-1986
+#N_apex         0.3162 lx s     # value in ANSI PH2.7-1973
+N_exif          1|3.125 lx s    # value in Exif 2.3 (2010), making Sv(5) = 100
+K_apex1961      11.4 (cd/m2) / lx s    # value in ASA PH2.12-1961
+K_apex1971      12.5 (cd/m2) / lx s    # value in ANSI PH3.49-1971; more common
+C_apex1961      224 lx / lx s   # value in PH2.12-1961 (20.83 for I in 
+                                #   footcandles; flat sensor?)
+C_apex1971      322 lx / lx s   # mean value in PH3.49-1971 (30 +/- 5 for I in
+                                # footcandles; hemispherical sensor?)
+N_speed         N_exif
+K_lum           K_apex1971
+C_illum         C_apex1961
+
+# Units for Photographic Exposure Variables
+#
+# Practical photography sometimes pays scant attention to units for exposure
+# variables.  In particular, the "speed" of the imaging medium is treated as if
+# it were dimensionless when it should have units of reciprocal lux seconds;
+# this practice works only because "speed" is almost invariably given in
+# accordance with international standards (or similar ones used by camera
+# manufacturers)--so the assumed units are invariant.  In calculating
+# logarithmic quantities--especially the time value Tv and the exposure value
+# EV--the units for exposure time ("shutter speed") are often ignored; this
+# practice works only because the units of exposure time are assumed to be in
+# seconds, and the missing units that make the argument to the logarithmic
+# function dimensionless are silently provided.
+#
+# In keeping with common practice, the definitions that follow treat "speeds"
+# as dimensionless, so ISO 100 speed is given simply as '100'.  When
+# calculating the logarithmic APEX quantities Av and Tv, the definitions
+# provide the missing units, so the times can be given with any appropriate
+# units.  For example, giving an exposure time of 1 minute as either '1 min' or
+# '60 s' will result in Tv of -5.9068906.
+#
+# Exposure Value from f-number and Exposure Time
+# 
+# Because nonlinear unit conversions only accept a single quantity,
+# there is no direct conversion from f-number and exposure time to
+# exposure value EV.  But the EV can be obtained from a combination of
+# Av and Tv.  For example, the "sunny 16" rule states that correct
+# exposure for a sunlit scene can achieved by using f/16 and an exposure
+# time equal to the reciprocal of the ISO speed in seconds; this can be
+# calculated as
+#
+#    ~Av(16) + ~Tv(1|100 s),
+#
+# which gives 14.643856.  These conversions may be combined with the
+# ev100 conversion:
+#
+#    ev100(~Av(16) + ~Tv(1|100 s))
+#
+# to yield the assumed average scene luminance of 3200 cd/m^2.
+
+# convert relative aperture (f-number) to aperture value
+Av(A)           units=[1;1] domain=[-2,) range=[0.5,)  2^(A/2); 2 log2(Av)
+# convert exposure time to time value
+Tv(t)           units=[1;s] range=(0,)  2^(-t) s; log2(s / Tv)
+# convert logarithmic speed Sv in ASA PH2.5-1960 to ASA/ISO arithmetic speed;
+# make arithmetic speed dimensionless
+# 'Sv' conflicts with the symbol for sievert; you can uncomment this function
+# definition if you don't need that symbol
+#Sv(S)    units=[1;1] range=(0,) 2^S / (N_speed/lx s); log2((N_speed/lx s) Sv)
+Sval(S)   units=[1;1] range=(0,) 2^S / (N_speed/lx s); log2((N_speed/lx s) Sval)
+
+# convert luminance value Bv in ASA PH2.12-1961 to luminance
+Bv(x)           units=[1;cd/m^2] range=(0,) \
+                2^x K_lum N_speed ; log2(Bv / (K_lum N_speed))
+
+# convert illuminance value Iv in ASA PH2.12-1961 to illuminance
+Iv(x)           units=[1;lx] range=(0,) \
+                2^x C_illum N_speed ; log2(Iv / (C_illum N_speed))
+
+# convert ASA/ISO arithmetic speed Sx to ASA logarithmic speed in 
+# ASA PH2.5-1960; make arithmetic speed dimensionless
+Sx(S)           units=[1;1] domain=(0,) \
+                log2((N_speed/lx s) S); 2^Sx / (N_speed/lx s)
+
+# convert DIN speed/ISO logarithmic speed in ISO 6:1993 to arithmetic speed
+# for convenience, speed is treated here as if it were dimensionless
+Sdeg(S)         units=[1;1] range=(0,) 10^((S - 1) / 10) ; (1 + 10 log(Sdeg))
+Sdin()          Sdeg
+
+# Numerical Aperture and f-Number of a Lens
+#
+# The numerical aperture (NA) is given by
+#
+#   NA = n sin(theta)
+#
+# where n is the index of refraction of the medium and theta is half
+# of the angle subtended by the aperture stop from a point in the image
+# or object plane. For a lens in air, n = 1, and
+#
+#   NA = 0.5 / f-number
+#
+# convert NA to f-number
+numericalaperture(x) units=[1;1] domain=(0,1] range=[0.5,) \
+                     0.5 / x ; 0.5 / numericalaperture
+NA()            numericalaperture
+#
+# convert f-number to itself; restrict values to those possible
+fnumber(x)      units=[1;1] domain=[0.5,) range=[0.5,) x ; fnumber
+
+# Referenced Photographic Standards
+#
+# ASA PH-2.5-1960. USA Standard, Method for Determining (Monochrome,
+#    Continuous-Tone) Speed of Photographic Negative Materials.
+# ASA PH2.12-1961. American Standard, General-Purpose Photographic
+#    Exposure Meters (photoelectric type).
+# ANSI PH3.49-1971. American National Standard for general-purpose
+#    photographic exposure meters (photoelectric type).
+# ANSI PH2.7-1973. American National Standard Photographic Exposure Guide.
+# ANSI PH2.7-1986. American National Standard for Photography --
+#    Photographic Exposure Guide.
+# CIPA DC-008-2010. Exchangeable image file format for digital still
+#    cameras: Exif Version 2.3
+# ISO 6:1993.  International Standard, Photography -- Black-and-white
+#    pictorial still camera negative film/process systems --
+#    Determination of ISO Speed.
+
+
+#
+# Astronomical time measurements
+#
+# Astronomical time measurement is a complicated matter.  The length of the
+# true day at a given place can be 21 seconds less than 24 hours or 30 seconds
+# over 24 hours.  The two main reasons for this are the varying speed of
+# Earth in its elliptical orbit and the fact that the Sun moves on the ecliptic
+# instead of along the celestial equator.  To devise a workable system for time
+# measurement, Simon Newcomb (1835-1909) used a fictitious "mean Sun".
+# Consider a first fictitious Sun traveling along the ecliptic at a constant
+# speed and coinciding with the true Sun at perigee and apogee.  Then
+# considering a second fictitious Sun traveling along the celestial equator at
+# a constant speed and coinciding with the first fictitious Sun at the
+# equinoxes.  The second fictitious Sun is the "mean Sun".  From this equations
+# can be written out to determine the length of the mean day, and the tropical
+# year.  The length of the second was determined based on the tropical year
+# from such a calculation and was officially used from 1960-1967 until atomic
+# clocks replaced astronomical measurements for a standard of time.  All of the
+# values below give the mean time for the specified interval.
+#
+# See "Mathematical Astronomy Morsels" by Jean Meeus for more details
+# and a description of how to compute the correction to mean time.
+#
+
+TIME                    second
+
+anomalisticyear         365.2596 days       # The time between successive
+                                            #   perihelion passages of 
+                                            #   Earth.
+siderealyear            365.256360417 day   # The time for Earth to make
+                                            #   one revolution around the Sun
+                                            #   relative to the stars.
+tropicalyear            365.242198781 day   # The time needed for the mean Sun
+                                            #   as defined above to increase
+                                            #   its longitude by 360 degrees.
+                                            #   Most references defined the
+                                            #   tropical year as the interval
+                                            #   between vernal equinoxes, but
+                                            #   this is misleading.  The length
+                                            #   of the season changes over time
+                                            #   because of the eccentricity of
+                                            #   Earth's orbit.  The time
+                                            #   between vernal equinoxes is
+                                            #   approximately 365.24237 days
+                                            #   around the year 2000.  See
+                                            #   "Mathematical Astronomy
+                                            #   Morsels" for more details.
+eclipseyear             346.62 days         # The line of nodes is the
+                                            #   intersection of the plane of
+                                            #   Earth's orbit around the Sun
+                                            #   with the plane of the Moon's
+                                            #   orbit around Earth.  Eclipses
+                                            #   can only occur when the Moon
+                                            #   and Sun are close to this
+                                            #   line.  The line rotates and
+                                            #   appearances of the Sun on the
+                                            #   line of nodes occur every
+                                            #   eclipse year.
+saros                   223 synodicmonth    # The Earth, Moon and Sun appear in
+                                            #   the same arrangement every
+                                            #   saros, so if an eclipse occurs,
+                                            #   then one saros later, a similar
+                                            #   eclipse will occur.  (The saros
+                                            #   is close to 19 eclipse years.)
+                                            #   The eclipse will occur about
+                                            #   120 degrees west of the
+                                            #   preceding one because the
+                                            #   saros is not an even number of
+                                            #   days.  After 3 saros, an
+                                            #   eclipse will occur at
+                                            #   approximately the same place.
+solarday                day                 # Time from noon to noon
+siderealday             86164.09054 s       # The sidereal day is the interval
+siderealhour            1|24 siderealday    #   between two successive transits
+siderealminute          1|60 siderealhour   #   of a star over the meridian,
+siderealsecond          1|60 siderealminute #   or the time required  for
+                                            #   Earth to make one rotation
+                                            #   relative to the stars.  Another
+                                            #   way to think about it is to
+                                            #   imagine looking down at the
+                                            #   solar system and noting when
+                                            #   Earth has made a rotation.
+                                            #   The more usual solar day is the
+                                            #   time required to make a
+                                            #   rotation relative to the Sun,
+                                            #   which means the same point on
+                                            #   Earth faces the Sun again.
+                                            #   Because Earth moves in its
+                                            #   orbit, it has to rotate a bit
+                                            #   more to face the Sun again,
+                                            #   hence the solar day is slightly
+                                            #   longer than the sidereal day.
+                                            #   The value given here is the
+                                            #   mean day length taken from
+                                            #   ssd.jpl.nasa.gov/astro_par.html
+                                            #   which in turn cites the
+                                            #   "Explanatory Supplement to the
+                                            #   Astronomical Almanac", 1992.
+anomalisticmonth        27.55454977 day     # Time for the Moon to travel from
+                                            #   perigee to perigee
+nodicalmonth            27.2122199 day      # The nodes are the points where
+draconicmonth           nodicalmonth        #   an orbit crosses the ecliptic.
+draconiticmonth         nodicalmonth        #   This is the time required to
+                                            #   travel from the ascending node
+                                            #   to the next ascending node.
+siderealmonth           27.321661 day       # Time required for the Moon to
+                                            #   orbit the Earth
+lunarmonth              29 days + 12 hours + 44 minutes + 2.8 seconds
+                                            # Mean time between full moons.
+synodicmonth            lunarmonth          #   Full moons occur when the Sun
+lunation                synodicmonth        #   and Moon are on opposite sides
+lune                    1|30 lunation       #   of the Earth.  Since the Earth
+lunour                  1|24 lune           #   moves around the Sun, the Moon
+                                            #   has to move a bit further in its
+                                            #   orbit to return to the full moon
+                                            #   configuration.
+year                    tropicalyear
+yr                      year
+month                   1|12 year
+mo                      month
+lustrum                 5 years             # The Lustrum was a Roman
+                                            #   purification ceremony that took
+                                            #   place every five years.
+                                            #   Classically educated Englishmen
+                                            #   used this term.
+decade                  10 years
+century                 100 years
+millennium              1000 years
+millennia               millennium
+solaryear               year
+lunaryear               12 lunarmonth
+calendaryear            365 day
+commonyear              365 day
+leapyear                366 day
+ 
+# The Julian year is The length of an average year over a 4-year cycle in the
+# Julian calendar.  The calendar was proposed by Julius Caesar in 46 BCE and
+# took effect the following year. It has a normal year of 365 days and a leap
+# year of 366 days every four years.  Though this calendar was used in
+# Europe for more than 1600 years, it drifts from the topical year by
+# about 1 day every 128 years, which became noticeable over its period
+# of use.
+
+# This growing discrepancy between the seasons and the calendar was perhaps
+# confusing but was also of concern to the Catholic Church because it led to a
+# shift in the date of Easter.  To correct this discrepancy, Pope Gregory XIII
+# introduced the more accurate Gregorian calendar in 1582.  The Gregorian year
+# is the length of an average year over a 400-year cycle in the Gregorian
+# calendar.  Every year that is exactly divisible by four is a
+# leap year, except for years that are exactly divisible by 100, unless these
+# centurial years are exactly divisible by 400.  This calendar was adopted by
+# many Catholic countries when it was proclaimed, but was not adopted by many
+# other countries until much later; Britain and the British Empire, including
+# what is now the eastern part of the United States, adopted it in 1752.  See
+# https://en.wikipedia.org/wiki/List_of_adoption_dates_of_the_Gregorian_calendar_by_country
+# for additional details.
+
+julianyear              365.25 days
+gregorianyear           365.2425 days
+                                            
+islamicyear             354 day          # A year of 12 lunar months. They
+islamicleapyear         355 day          # began counting on July 16, AD 622
+                                         # when Muhammad emigrated to Medina
+                                         # (the year of the Hegira).  They need
+                                         # 11 leap days in 30 years to stay in
+                                         # sync with the lunar year which is a
+                                         # bit longer than the 29.5 days of the
+                                         # average month.  The months do not
+                                         # keep to the same seasons, but
+                                         # regress through the seasons every
+                                         # 32.5 years.
+islamicmonth            1|12 islamicyear # They have 29 day and 30 day months.
+
+# The Hebrew year is also based on lunar months, but synchronized to the solar
+# calendar.  The months vary irregularly between 29 and 30 days in length, and
+# the years likewise vary.  The regular year is 353, 354, or 355 days long.  To
+# keep up with the solar calendar, a leap month of 30 days is inserted every
+# 3rd, 6th, 8th, 11th, 14th, 17th, and 19th years of a 19 year cycle.  This
+# gives leap years that last 383, 384, or 385 days.
+
+#
+# Planetary data from JPL's planet fact sheets.  Each planet has its
+# own sheet at https://nssdc.gsfc.nasa.gov/planetary/factsheet/<name>fact.html
+# The source for data on the fact sheets is described at
+# https://nssdc.gsfc.nasa.gov/planetary/factsheet/fact_notes.html
+# and they also indicate that the values listed are not "official" values:
+# there is no single set of agreed upon values.  
+
+# Sidereal days.  The sidereal day is the time required for a planet to make a
+# revolution relative to the stars.  This is the default day value.  
+
+mercuryday              mercuryday_sidereal
+venusday                venusday_sidereal
+earthday                earthday_sidereal
+marsday                 marsday_sidereal
+jupiterday              jupiterday_sidereal
+saturnday               saturnday_sidereal
+uranusday               uranusday_sidereal
+neptuneday              neptuneday_sidereal
+plutoday                plutoday_sidereal
+
+mercuryday_sidereal     1407.6 hr   # Mercury is in a 3:2 resonance lock
+                                    # where it makes 3 rotations per 2 orbits
+                                    # so 3 sidereal days = 2 years
+venusday_sidereal       5832.6 hr   # Retrograde
+earthday_sidereal       siderealday
+marsday_sidereal        24.6229 hr
+jupiterday_sidereal     9.9250 hr
+saturnday_sidereal      10.656 hr
+uranusday_sidereal      17.24 hr    # Retrograde
+neptuneday_sidereal     16.11 hr
+plutoday_sidereal       153.2928 hr # Retrograde
+
+# In astronomy, an object's rotation is "prograde" if it rotates in
+# the same direction as the primary object it orbits.  Prograde
+# rotation is the more common case: in Earth's solar system, Mercury,
+# Earth, Mars, Jupiter, Saturn, and Neptune have prograde rotation.
+# When an object rotates opposite the direction of its primary object,
+# the object's rotation is "retrograde".  Venus, Uranus, and Pluto have
+# retrograde rotation.
+#
+# The solar (or synodic) day is the time from noon to noon on a planet.  This
+# is different from the sidereal day because the planet has moved in its orbit,
+# so (if its rotation is prograde) it needs additional rotation to return to
+# the same orientation relative to the Sun.  In one orbital period (a year),
+# this amounts to one additional complete rotation, so the number of sidereal
+# days in a year is one greater than the number of solar days.
+#
+# If the planet's rotation is retrograde, less rotation is needed to return to
+# the same orientation relative to the Sun, and the number of sidereal days in
+# a year is one fewer than the number of solar days.
+#
+# The solar day can be computed from the sidereal day in the typical prograde
+# case by:
+#       solar_day = sidereal_day year / (year - sidereal_day)
+# If the planet's rotation is retrograde like Venus then the formula is
+#       solar_day = sidereal_day year / (year + sidereal_day)
+# If the sidereal day and year are the same length then the same face of the
+# planet faces the Sun and there is no solar day.
+
+mercuryday_solar        4222.6 hr 
+venusday_solar          2802.0 hr
+earthday_solar          24 hr
+marsday_solar           24.6597 hr
+jupiterday_solar        9.9259 hr
+saturnday_solar         10.656 hr
+uranusday_solar         17.24 hr
+neptuneday_solar        16.11 hr
+plutoday_solar          153.2820 hr
+
+# Sidereal years 
+
+mercuryyear             87.969 day
+venusyear               224.701 day
+earthyear               siderealyear
+marsyear                686.980 day
+jupiteryear             4332.589 day
+saturnyear              10759.22 day
+uranusyear              30685.4 day
+neptuneyear             60189 day
+plutoyear               90560 day
+
+# Equatorial radii for the planets from JPL fact sheets
+
+mercuryradius           2440.5 km
+venusradius             6051.8 km
+earthradius             6378.137 km
+marsradius              3396.2 km
+jupiterradius           71492 km   # 1 bar level
+saturnradius            60268 km   # 1 bar level
+uranusradius            25559 km   # 1 bar level
+neptuneradius           24764 km   # 1 bar level
+plutoradius             1188 km
+
+# Volumetric mean radii
+
+mercuryradius_mean      2440.5 km
+venusradius_mean        6051.8 km
+earthradius_mean        6371 km
+marsradius_mean         3389.5 km
+jupiterradius_mean      69911 km
+saturnradius_mean       58232 km
+uranusradius_mean       25362 km
+neptuneradius_mean      24622 km
+plutoradius_mean        1188 km
+
+# Polar radii
+
+mercuryradius_polar      2438.3 km
+venusradius_polar        6051.8 km
+marsradius_polar         3376.2 km
+jupiterradius_polar      66854 km
+saturnradius_polar       54364 km
+uranusradius_polar       24973 km
+neptuneradius_polar      24341 km
+plutoradius_polar        1188 km
+
+mercurysundist_min      46.000 Gm
+mercurysundist_max      69.818 Gm
+venussundist_min        107.480 Gm
+venussundist_max        108.941 Gm
+earthsundist_min        sundist_min
+earthsundist_max        sundist_max
+marssundist_min         206.650 Gm
+marssundist_max         249.261 Gm
+jupitersundist_min      740.595 Gm
+jupitersundist_max      816.363 Gm
+saturnsundist_min       1357.554 Gm
+saturnsundist_max       1506.527 Gm
+uranussundist_min       2732.696 Gm
+uranussundist_max       3001.390 Gm
+neptunesundist_min      4471.050 Gm
+neptunesundist_max      4558.857 Gm
+plutosundist_min        4434.987 Gm
+plutosundist_max        7304.326 Gm
+
+sundist                 1.0000010178 au # mean Earth-Sun distance
+moondist                384400 km       # mean Earth-Moon distance
+sundist_near            147.095 Gm      # Earth-Sun distance at perihelion
+sundist_min             sundist_near
+sundist_far             152.100 Gm      # Earth-Sun distance at aphelion
+sundist_max             sundist_far
+
+# The Earth-Moon distances at perigee and apogee are different for every
+# lunation.  The values here are the extremes for 1500-2500 according to
+# Jean Meeus's Astronomical Algorithms (1991, 332).
+
+moondist_min            356371 km      # minimum distance at perigee 1500-2500
+moondist_max            406720 km      # maximum distance at apogee 1500-2500
+
+# Objects on Earth are charted relative to a perfect ellipsoid whose
+# dimensions are specified by different organizations.  The ellipsoid is
+# specified by an equatorial radius and a flattening value which defines the
+# polar radius.
+
+earthflattening         IERS_earthflattening
+earthradius_equatorial  IERS_earthradius_equatorial
+earthradius_polar       (1-earthflattening) earthradius_equatorial
+
+# The World Geodetic System maintains a standard, WGS84, which is used by the
+# the GPS system.  This system uses a conventional ellipsoid that was fixed in
+# 1984 and has remained constant so that data collected at different times is
+# referenced to the same ellipsoid.   https://epsg.io/4326
+
+WGS84_earthflattening         1|298.257223563
+WGS84_earthradius_equatorial  6378137 m
+WGS84_earthradius_polar   (1-WGS84_earthflattening) WGS84_earthradius_equatorial
+
+# The International Earth Rotation Service (IERS) attempts to
+# maintain an accurate model of Earth, with updates to maintain the highest
+# possible accuracy, even though this makes it more difficult to relate geodetic
+# measurements made at different times.
+# IERS Conventions, Chapter 1, General definitions and numerical standards (16 November 2017)
+# https://iers-conventions.obspm.fr/content/chapter1/icc1.pdf
+
+IERS_earthflattening        1|298.25642
+IERS_earthradius_equatorial 6378136.6 m
+IERS_earthradius_polar      (1-IERS_earthflattening) IERS_earthradius_equatorial
+
+
+landarea                148.847e6 km^2
+oceanarea               361.254e6 km^2
+
+moonradius              1738 km         # mean value
+sunradius               6.96e8 m
+
+# Many astronomical values can be measured most accurately in a system of units
+# using the astronomical unit and the mass of the Sun as base units.  The
+# uncertainty in the gravitational constant makes conversion to SI units
+# significantly less accurate.
+
+# The astronomical unit was defined to be the length of the of the semimajor
+# axis of a massless object with the same year as Earth.  With such a
+# definition in force, and with the mass of the Sun set equal to one, Kepler's
+# third law can be used to solve for the value of the gravitational constant.
+
+# Kepler's third law says that (2 pi / T)^2 a^3 = G M where T is the orbital
+# period, a is the size of the semimajor axis, G is the gravitational constant
+# and M is the mass.  With M = 1 and T and a chosen for Earth's orbit, we
+# find sqrt(G) = (2 pi / T) sqrt(AU^3).  This constant is called the Gaussian
+# gravitational constant, apparently because Gauss originally did the
+# calculations.  However, when the original calculation was done, the value
+# for the length of Earth's year was inaccurate.  The value used is called
+# the Gaussian year.  Changing the astronomical unit to bring it into
+# agreement with more accurate values for the year would have invalidated a
+# lot of previous work, so instead the astronomical unit has been kept equal
+# to this original value.  This is accomplished by using a standard value for
+# the Gaussian gravitational constant.  This constant is called k.
+
+gauss_k                 0.01720209895   # This beast has dimensions of
+                                        # au^(3|2) / day and is exact.
+gaussianyear      (2 pi / gauss_k) days # Year that corresponds to the Gaussian
+                                        # gravitational constant. This is a
+                                        # fictional year, and doesn't
+                                        # correspond to any celestial event.
+astronomicalunit         149597870700 m # IAU definition from 2012, exact
+au                     astronomicalunit # ephemeris for the above described
+                                        # astronomical unit.  (See the NASA
+                                        # site listed above.)
+GMsun    132712440041.279419 km^3 / s^2 # heliocentric gravitational constant
+solarmass                       GMsun/G # is known more accurately than G.
+sunmass                       solarmass # Estimated from DE440
+
+
+# The following are masses for planetary systems, not just the planet itself,
+# except for the case of Earth, where the Moon is excluded.  Masses are
+# relative to G because they are known much more accurately than G.
+#
+# See https://ssd.jpl.nasa.gov/astro_par.html.  Values are from
+# the DE440 Ephemeris: https://ssd.jpl.nasa.gov/doc/Park.2021.AJ.DE440.pdf
+
+mercurymass             22031.868551 km^3 / s^2 G
+venusmass               324858.592000 km^3 / s^2 G
+marsmass                42828.375816 km^3 / s^2 G
+jupitermass             126712764.100000 km^3 / s^2 G
+saturnmass              37940584.841800 km^3 / s^2 G
+uranusmass              5794556.400000 km^3 / s^2 G
+neptunemass             6836527.100580 km^3 / s^2 G
+plutomass               975.500000 km^3 / s^2 G
+ceresmass               62.62890 km^3 / s^2 G
+vestamass               17.288245 km^3 / s^2 G
+
+earthmass               398600.435507 km^3 / s^2 G    # Earth alone
+moonmass                4902.800118 km^3 / s^2 G
+moonearthmassratio      moonmass/earthmass
+earthmoonmass           earthmass+moonmass
+
+moongravity             1.62 m/s^2
+
+# Earth gravity values at the equator and poles.  These values are
+# obtained from the WGS84 model.
+
+gravity_equatorial      9.7803263359 m / s^2
+gravity_polar           9.8321849378 m / s^2
+
+# The Hubble constant gives the speed at which distance galaxies are moving
+# away from Earth according to v = H0*d, where H0 is the hubble constant
+# and d is the distance to the galaxy.
+
+hubble                  70 km/s/Mpc        # approximate
+H0                      hubble
+
+# Parallax is the angular difference between the topocentric (on Earth's
+# surface) and geocentric (at Earth's center) direction toward a celestial body
+# when the body is at a given altitude.  When the body is on the horizon, the
+# parallax is the horizontal parallax; when the body is on the horizon and the
+# observer is on the equator, the parallax is the equatorial horizontal
+# parallax.  When the body is at zenith, the parallax is zero.
+
+lunarparallax  asin(earthradius_equatorial / moondist) # Moon equatorial 
+moonhp         lunarparallax                           # horizontal parallax
+                                                       # at mean distance
+
+# Light from celestial objects is attenuated by passage through Earth's
+# atmosphere.  A body near the horizon passes through much more air than an
+# object at zenith, and is consequently less bright.  Air mass is the ratio of
+# the length of the optical path at a given altitude (angle above the horizon)
+# to the length at zenith.  Air mass at zenith is by definition unity; at the
+# horizon, air mass is approximately 38, though the latter value can vary
+# considerably with atmospheric conditions.  The general formula is # E = E0
+# exp(-c X), where E0 is the value outside Earth's atmosphere, E is the value
+# seen by an observer, X is the air mass and c is the extinction coefficient.
+# A common value for c in reasonably clear air is 0.21, but values can be
+# considerably greater in urban areas.  Apparent altitude is that perceived by
+# an observer; it includes the effect of atmospheric refraction.  There is no
+# shortage of formulas for air mass
+# (https://en.wikipedia.org/wiki/Air_mass_(astronomy)); all are subject to
+# variations in local atmospheric conditions.  The formula used here is simple
+# and is in good agreement with rigorously calculated values under standard
+# conditions.
+#
+# Extraterrestrial illuminance or luminance of an object at a given altitude
+# determined with vmag() or SB_xxx() below can be multiplied by
+# atm_transmission() or atm_transmissionz() to estimate the terrestrial value.
+#
+# Kasten and Young (1989) air mass formula. alt is apparent altitude
+# Reference:
+# Kasten, F., and A.T. Young. 1989. "Revised Optical Air Mass Tables
+#     and Approximation Formula."  Applied Optics.  Vol. 28, 4735-4738.
+#     Bibcode:1989ApOpt..28.4735K. doi:10.1364/AO.28.004735.
+
+airmass(alt) units=[degree;1] domain=[0,90] noerror \
+    1 / (sin(alt) + 0.50572 (alt / degree + 6.07995)^-1.6364)
+
+# zenith is apparent zenith angle (zenith = 90 deg - alt)
+airmassz(zenith) units=[degree;1] domain=[0,90] noerror \
+    1 / (cos(zenith) + 0.50572 (96.07995 - zenith / degree)^-1.6364)
+
+# For reasonably clear air at sea level; values may need adjustment for
+# elevation and local atmospheric conditions
+# for scotopic vision (510 nm), appropriate for the dark-adapted eye
+# extinction_coeff           0.26
+# for photopic vision, appropriate for observing brighter objects such
+# as the full moon
+extinction_coeff        0.21
+
+atm_transmission(alt) units=[degree;1] domain=[0,90] noerror \
+                        exp(-extinction_coeff airmass(alt))
+
+# in terms of zenith angle (zenith = 90 deg - alt)
+atm_transmissionz(zenith) units=[degree;1] domain=[0,90] noerror \
+                        exp(-extinction_coeff airmassz(zenith))
+
+# Moon and Sun data at mean distances
+moonvmag               -12.74  # Moon apparent visual magnitude at mean distance
+sunvmag                -26.74  # Sun apparent visual magnitude at mean distance
+moonsd  asin(moonradius / moondist) # Moon angular semidiameter at mean distance
+sunsd     asin(sunradius / sundist) # Sun angular semidiameter at mean distance
+
+# Visual magnitude of star or other celestial object.  The system of stellar
+# magnitudes, developed in ancient Greece, assigned magnitudes from 1
+# (brightest) to 6 (faintest visible to the naked eye).  In 1856, British
+# astronomer Norman Pogson made the system precise, with a magnitude 1 object
+# 100 times as bright as a magnitude 6 object, and each magnitude differing
+# from the next by a constant ratio; the ratio, sometimes known as Pogson's
+# ratio, is thus 100^0.2, or approximately 2.5119.  The logarithm of 100^0.2 is
+# 0.4, hence the common use of powers of 10 and base-10 logarithms.
+#
+# Reference:
+# Allen, C.W. 1976.  Astrophysical Quantities, 3rd ed. 1973, reprinted
+#     with corrections, 1976.  London: Athlone.
+#
+# The function argument is the (dimensionless) visual magnitude; reference
+# illuminance of 2.54e-6 lx is from Allen (2000, 21), and is for outside
+# Earth's atmosphere.  Illuminance values can be adjusted to terrestrial values
+# by multiplying by one of the atm_transmission functions above.
+
+# Illuminance from apparent visual magnitude
+vmag(mag) units=[1;lx] domain=[,]  range=(0,] \
+                       2.54e-6 lx 10^(-0.4 mag); -2.5 log(vmag / (2.54e-6 lx))
+
+# Surface brightness of a celestial object of a given visual magnitude
+# is a logarithmic measure of the luminance the object would have if its
+# light were emitted by an object of specified solid angle; it is
+# expressed in magnitudes per solid angle.  Surface brightness can be
+# obtained from the visual magnitude by
+#    S = m + 2.5 log(pi pi k a b),
+# where k is the phase (fraction illuminated), a is the equatorial
+# radius, and b is the polar radius.  For 100% illumination (e.g., full
+# moon), this is often simplified to
+#    S = m + 2.5 log(pi k s^2),
+# where s is the object's angular semidiameter; the units of s determine
+# the units of solid angle.  The visual magnitude and semidiameter must
+# be appropriate for the object's distance; for other than 100%
+# illumination, the visual magnitude must be appropriate for the phase.
+# Luminance values are for outside Earth's atmosphere; they can be
+# adjusted to terrestrial values by multiplying by one of the atm_transmission
+# functions above.
+
+# luminance from surface brightness in magnitudes per square degree
+SB_degree(sb) units=[1;cd/m^2] domain=[,] range=(0,] \
+    vmag(sb) / squaredegree ; \
+    ~vmag(SB_degree squaredegree)
+
+# luminance from surface brightness in magnitudes per square minute
+SB_minute(sb) units=[1;cd/m^2] domain=[,] range=(0,] \
+    vmag(sb) / squareminute ; \
+    ~vmag(SB_minute squareminute)
+
+# luminance from surface brightness in magnitudes per square second
+SB_second(sb) units=[1;cd/m^2] domain=[,] range=(0,] \
+    vmag(sb) / squaresecond ; \
+    ~vmag(SB_second squaresecond)
+
+# luminance from surface brightness in magnitudes per steradian
+SB_sr(sb) units=[1;cd/m^2] domain=[,] range=(0,] \
+    vmag(sb) / sr ; \
+    ~vmag(SB_sr sr)
+
+SB()            SB_second
+SB_sec()        SB_second
+SB_min()        SB_minute
+SB_deg()        SB_degree
+
+# The brightness of one tenth-magnitude star per square degree outside
+# Earth's atmosphere; often used for night sky brightness.
+S10     SB_degree(10)
+
+# Examples for magnitude and surface brightness functions
+# Sun illuminance from visual magnitude
+#     You have: sunvmag
+#     You want:
+#           Definition: -26.74 = -26.74
+#     You have: vmag(sunvmag)
+#     You want: lx
+#           * 126134.45
+#           / 7.9280482e-06
+#
+# Moon surface brightness from visual magnitude and semidiameter at 100%
+# illumination (full moon):
+#     You have: moonvmag
+#     You want:
+#           Definition: -12.74 = -12.74
+#     You have: moonsd
+#     You want: arcsec
+#           * 932.59484
+#           / 0.001072277
+#     You have: moonvmag + 2.5 log(pi 932.59484^2)
+#     You want:
+#           Definition: 3.3513397
+#
+# Similar example with specific data obtained from another source (JPL
+# Horizons, https://ssd.jpl.nasa.gov/horizons.cgi); semidiameter is in
+# arcseconds
+#
+#     You have: -12.9 + 2.5 log(pi 2023.201|2^2)
+#     You want:
+#           Definition: 3.3679199
+#     You have: SB_second(-12.9 + 2.5 log(pi 2023.201|2^2))
+#     You want:
+#           Definition: 4858.6547 cd / m^2
+#
+# If surface brightness is provided by another source (e.g., Horizons),
+# it can simply be used directly:
+# You have: SB_second(3.3679199)
+# You want: cd/m^2
+#         * 4858.6546
+#         / 0.0002058183
+# The illuminance and luminance values are extraterrestrial (outside
+# Earth's atmosphere).  The values at Earth's surface are less than these
+# because of atmospheric extinction.  For example, in the last example
+# above, if the Moon were at an altitude of 55 degrees, the terrestrial
+# luminance could be calculated with
+#     You have: SB_second(3.3679199)
+#     You want: cd/m^2
+#           * 4858.6546
+#           / 0.0002058183
+#     You have: _ atm_transmission(55 deg)
+#     You want: cd/m^2
+#           * 3760.6356
+#           / 0.0002659125
+# If desired, photographic exposure can be determined with EV100(),
+# leading to acceptable combinations of aperture and exposure time.
+# For the example above, but with the Moon at 10 degrees,
+#     You have: SB_second(3.3679199) atm_transmission(10 deg)
+#     You want: EV100
+#           13.553962
+
+#
+# The Hartree system of atomic units, derived from fundamental units
+# of mass (of the electron), action (Planck's constant), charge, and
+# the Coulomb constant.  This system is used in the fields of physical
+# chemistry and condensed matter physics.  
+#
+
+# Fundamental units
+
+atomicmass              electronmass
+atomiccharge            e
+atomicaction            hbar
+atomicenergy            hartree
+
+# Derived units
+
+atomicvelocity          sqrt(atomicenergy / atomicmass)
+atomictime              atomicaction / atomicenergy
+atomiclength            atomicvelocity atomictime
+atomicforce             atomicenergy / atomiclength
+atomicmomentum          atomicenergy / atomicvelocity
+atomiccurrent           atomiccharge / atomictime
+atomicpotential         atomicenergy / atomiccharge   # electrical potential
+atomicvolt              atomicpotential
+atomicEfield            atomicpotential / atomiclength
+atomicBfield            atomicEfield / atomicvelocity
+atomictemperature       atomicenergy / boltzmann
+
+#
+# In Hartree units, m_e = hbar = e = coulombconst = bohrradius = alpha*c = 1
+#
+
+!var UNITS_SYSTEM hartree
+!message Hartree units selected
+!prompt (hartree)
++hartree      1
++kg           1/electronmass_SI
++K            k_SI / hbar_SI s 
++m            alpha c_SI electronmass_SI / hbar_SI
++s            alpha c_SI m
++A            1 / s e_SI
+!endvar
+
+#
+# These thermal units treat entropy as charge, from [5]
+#
+
+thermalcoulomb          J/K        # entropy
+thermalampere           W/K        # entropy flow
+thermalfarad            J/K^2
+thermalohm              K^2/W      # thermal resistance
+fourier                 thermalohm
+thermalhenry            J K^2/W^2  # thermal inductance
+thermalvolt             K          # thermal potential difference
+
+
+#
+# United States units
+#
+
+# linear measure
+
+# The US Metric Law of 1866 legalized the metric system in the USA and
+# defined the meter in terms of the British system with the exact
+# 1 meter = 39.37 inches.  On April 5, 1893 Thomas Corwin Mendenhall,
+# Superintendent of Weights and Measures, decided, in what has become
+# known as the "Mendenhall Order" that the meter and kilogram would be the
+# fundamental standards in the USA.  The definition from 1866 was turned
+# around to give an exact definition of the yard as 3600|3937 meters This
+# definition was used until July of 1959 when the definition was changed
+# to bring the US and other English-speaking countries into agreement; the
+# Canadian value of 1 yard = 0.9144 meter (exactly) was chosen because it
+# was approximately halfway between the British and US values; it had the
+# added advantage of making 1 inch = 25.4 mm (exactly).  Since 1959, the
+# "international" foot has been exactly 0.3048 meters.  At the same time,
+# it was decided that any data expressed in feet derived from geodetic
+# surveys within the US would continue to use the old definition and call
+# the old unit the "survey foot."
+#
+# Until 1 January 2023, the US continued to define the statute
+# mile, furlong, chain, rod, link, and fathom in terms of the US survey
+# foot.  Since then, use of the US survey foot has been officially
+# deprecated, with its use limited to historical and legacy applications.
+# These units are now defined in terms of the international foot.
+#
+# Sources:
+# NIST Special Publication 447, Sects. 5, 7, and 8.
+# NIST Handbook 44, 2024 ed., Appendix C.
+# Canadian Journal of Physics, 1959, 37:(1) 84, 10.1139/p59-014.
+
+inch                    2.54 cm          # Exact, international inch (1959)
+in                      inch
+foot                    12 inch
+feet                    foot
+ft                      foot
+yard                    3 ft
+yd                      yard
+mile                    5280 ft          # The mile was enlarged from 5000 ft
+                                         # to this number in order to make
+                                         # it an even number of furlongs.
+                                         # (The Roman mile is 5000 romanfeet.)
+line                    1|12 inch  # Also defined as '.1 in' or as '1e-8 Wb'
+rod                     16.5 ft
+pole                    rod
+perch                   rod
+furlong                 40 rod           # From "furrow long"
+statutemile             mile
+league                  3 mile           # Intended to be an hour's walk
+
+# surveyor's measure
+# The US survey foot is officially deprecated as of 1 January 2023
+US                      1200|3937 m/ft   # These four values will convert
+US-                     US               #   international measures to
+survey-                 US               #   US Survey measures
+geodetic-               US
+int                     3937|1200 ft/m   # Convert US Survey measures to
+int-                    int              #   international measures
+
+# values based on the US survey foot are deprecated as of 1 January 2023
+surveyorschain          66 surveyft
+surveychain             surveyorschain
+surveyorspole           1|4 surveyorschain
+surveyorslink           1|100 surveyorschain
+USacre                  10 surveychain^2
+USacrefoot              USacre surveyfoot
+
+chain                   66 ft
+link                    1|100 chain
+ch                      chain
+intacre                 10 chain^2       # Acre based on international ft
+intacrefoot             acre foot
+acrefoot                intacrefoot
+acre                    intacre
+ac                      acre
+section                 mile^2
+township                36 section
+homestead               160 acre # Area of land granted by the 1862 Homestead
+                                 # Act of the United States Congress
+gunterschain            surveyorschain
+
+engineerschain          100 ft
+engineerslink           1|100 engineerschain
+ramsdenschain           engineerschain
+ramsdenslink            engineerslink
+
+gurleychain             33 feet           # Andrew Ellicott chain is the
+gurleylink              1|50 gurleychain  # same length
+
+wingchain               66 feet           # Chain from 1664, introduced by
+winglink                1|80 wingchain    # Vincent Wing, also found in a
+                                          # 33 foot length with 40 links.
+# early US length standards
+
+# The US has had four standards for the yard: one by Troughton of London
+# (1815); bronze yard #11 (1856); the Mendhall yard (1893), consistent
+# with the definition of the meter in the metric joint resolution of
+# Congress in 1866, but defining the yard in terms of the meter; and the
+# international yard (1959), which standardized definitions for Australia,
+# Canada, New Zealand, South Africa, the UK, and the US.
+# Sources: Pat Naughtin (2009), Which Inch?:
+# https://metricationmatters.org/docs/WhichInch.pdf, 
+# Lewis E.  Barbrow and Lewis V.  Judson (1976).  NBS Special
+# Publication 447, Weights and Measures Standards of the United States: A
+# Brief History.
+
+troughtonyard           914.42190 mm
+bronzeyard11            914.39980 mm
+mendenhallyard          surveyyard
+internationalyard       yard
+
+# nautical measure
+
+fathom                  6 ft     # Originally defined as the distance from
+                                 #   fingertip to fingertip with arms fully
+                                 #   extended.
+nauticalmile            1852 m   # Supposed to be one minute of latitude at
+                                 # the equator.  That value is about 1855 m.
+                                 # Early estimates of Earth's circumference
+                                 # were a bit off.  The value of 1852 m was
+                                 # made the international standard in 1929.
+                                 # The US did not accept this value until
+                                 # 1954.  The UK switched in 1970.
+                                 
+# The cable is used for depth in water and has a wide range of definitions
+
+intcable                1|10 nauticalmile # international cable
+uscable                 120 fathom	  # value after 1 January 2023
+surveycable             120 USfathom	  # value before 1 January 2023
+UScable                 surveycable
+cableslength            cable
+cablelength             cable
+navycablelength         cable
+brcable                 1|10 brnauticalmile   
+admiraltycable          brcable
+
+marineleague            3 nauticalmile
+geographicalmile        brnauticalmile
+knot                    nauticalmile / hr
+click                   km       # US military slang
+klick                   click
+
+# Avoirdupois weight
+
+pound                   0.45359237 kg   # Exact, International Pound (1959)
+lb                      pound           # From the Latin libra
+grain                   1|7000 pound    # The grain is the same in all three
+                                        # weight systems.  It was originally
+                                        # defined as the weight of a barley
+                                        # corn taken from the middle of the
+                                        # ear.
+ounce                   1|16 pound
+oz                      ounce
+dram                    1|16 ounce
+dr                      dram
+ushundredweight         100 pounds
+cwt                     hundredweight
+shorthundredweight      ushundredweight
+uston                   shortton
+shortton                2000 lb
+quarterweight           1|4 uston
+shortquarterweight      1|4 shortton
+shortquarter            shortquarterweight
+
+# Troy Weight.  In 1828 the troy pound was made the first United States
+# standard weight.  It was to be used to regulate coinage.
+
+troypound               5760 grain
+troyounce               1|12 troypound
+ozt                     troyounce
+pennyweight             1|20 troyounce  # Abbreviated "d" in reference to a
+dwt                     pennyweight     #   Frankish coin called the "denier"
+                                        #   minted in the late 700's.  There
+                                        #   were 240 deniers to the pound.
+assayton                mg ton / troyounce  # mg / assayton = troyounce / ton
+usassayton              mg uston / troyounce
+brassayton              mg brton / troyounce
+fineounce               troyounce       # A troy ounce of 99.5% pure gold
+
+# Some other jewelers units
+
+metriccarat             0.2 gram        # Defined in 1907
+metricgrain             50 mg
+carat                   metriccarat
+ct                      carat
+jewelerspoint           1|100 carat
+silversmithpoint        1|4000 inch
+momme                   3.75 grams      # Traditional Japanese unit based
+                                        # on the chinese mace.  It is used for
+                                        # pearls in modern times and also for
+                                        # silk density.  The definition here
+                                        # was adopted in 1891.
+# Apothecaries' weight
+
+appound                 troypound
+apounce                 troyounce
+apdram                  1|8 apounce
+apscruple               1|3 apdram
+
+# Liquid measure
+
+usgallon                231 in^3        # US liquid measure is derived from
+gal                     gallon          # the British wine gallon of 1707.
+quart                   1|4 gallon      # See the "winegallon" entry below
+pint                    1|2 quart       # more historical information.
+gill                    1|4 pint
+usquart                 1|4 usgallon
+uspint                  1|2 usquart
+usgill                  1|4 uspint
+usfluidounce            1|16 uspint
+fluiddram               1|8 usfloz
+minimvolume             1|60 fluiddram
+qt                      quart
+pt                      pint
+floz                    fluidounce
+usfloz                  usfluidounce
+fldr                    fluiddram
+liquidbarrel            31.5 usgallon
+usbeerbarrel            2 beerkegs
+beerkeg                 15.5 usgallon   # Various among brewers
+ponykeg                 1|2 beerkeg
+winekeg                 12 usgallon
+petroleumbarrel         42 usgallon     # Originated in Pennsylvania oil
+barrel                  petroleumbarrel # fields, from the winetierce
+bbl                     barrel
+ushogshead              2 liquidbarrel
+usfirkin                9 usgallon
+
+# Dry measures: The Winchester Bushel was defined by William III in 1702 and
+# legally adopted in the US in 1836.
+
+usbushel                2150.42 in^3  # Volume of 8 inch cylinder with 18.5
+bu                      bushel        # inch diameter (rounded)
+peck                    1|4 bushel
+uspeck                  1|4 usbushel
+brpeck                  1|4 brbushel
+pk                      peck
+drygallon               1|2 uspeck
+dryquart                1|4 drygallon
+drypint                 1|2 dryquart
+drybarrel               7056 in^3     # Used in US for fruits, vegetables,
+                                      #   and other dry commodities except for
+                                      #   cranberries.
+cranberrybarrel         5826 in^3     # US cranberry barrel
+heapedbushel            1.278 usbushel# The following explanation for this
+                                      #   value was provided by Wendy Krieger
+                                      #   <os2fan2@yahoo.com> based on
+                                      #   guesswork.  The cylindrical vessel is
+                                      #   18.5 inches in diameter and 1|2 inch
+                                      #   thick.  A heaped bushel includes the
+                                      #   contents of this cylinder plus a heap
+                                      #   on top.  The heap is a cone 19.5
+                                      #   inches in diameter and 6 inches
+                                      #   high.  With these values, the volume
+                                      #   of the bushel is 684.5 pi in^3 and
+                                      #   the heap occupies 190.125 pi in^3.
+                                      #   Therefore, the heaped bushel is
+                                      #   874.625|684.5 bushels.  This value is
+                                      #   approximately 1.2777575 and it rounds
+                                      #   to the value listed for the size of
+                                      #   the heaped bushel.  Sometimes the
+                                      #   heaped bushel is reported as 1.25
+                                      #   bushels.  This same explanation gives
+                                      #   that value if the heap is taken to
+                                      #   have an 18.5 inch diameter.
+
+# Grain measures.  The bushel as it is used by farmers in the USA is actually
+# a measure of mass which varies for different commodities.  Canada uses the
+# same bushel masses for most commodities, but not for oats.
+
+wheatbushel             60 lb
+soybeanbushel           60 lb
+cornbushel              56 lb
+ryebushel               56 lb
+barleybushel            48 lb
+oatbushel               32 lb
+ricebushel              45 lb
+canada_oatbushel        34 lb
+
+# Wine and Spirits measure
+
+ponyvolume              1 usfloz
+jigger                  1.5 usfloz   # Can vary between 1 and 2 usfloz
+shot                    jigger     # Sometimes 1 usfloz
+eushot                  25 ml      # EU standard spirits measure
+fifth                   1|5 usgallon
+winebottle              750 ml     # US industry standard, 1979
+winesplit               1|4 winebottle
+magnum                  1.5 liter  # Standardized in 1979, but given
+                                   # as 2 qt in some references
+metrictenth             375 ml
+metricfifth             750 ml
+metricquart             1 liter
+
+# Old British bottle size
+
+reputedquart            1|6 brgallon
+reputedpint             1|2 reputedquart
+brwinebottle            reputedquart       # Very close to 1|5 winegallon
+
+# French champagne bottle sizes
+
+split                   200 ml
+jeroboam                2 magnum
+rehoboam                3 magnum
+methuselah              4 magnum
+imperialbottle          4 magnum
+salmanazar              6 magnum
+balthazar               8 magnum
+nebuchadnezzar          10 magnum
+solomon                 12 magnum
+melchior                12 magnum
+sovereign               17.5 magnum
+primat                  18 magnum
+goliath                 18 magnum
+melchizedek             20 magnum
+midas                   20 magnum
+
+# The wine glass doesn't seem to have an official standard, but the same value
+# is suggested by several sources in the US. 
+
+wineglass               150 mL 
+
+# In the UK, serving size offerings legally mandated by The Weights and
+# Measures (Specified Quantities) (Unwrapped Bread and Intoxicating
+# Liquor) Order 2011, effective 1st October 2011.  The quantities--not
+# the names--are mandated.  Lawful size offerings are these or multiples
+# thereof, but other sizes can be provided at the express request of a
+# buyer.
+
+smallwineglass          125 mL
+mediumwineglass         175 mL
+
+# Values vary considerably among countries and even more so in practice.  The
+# "standard" US value gives 5 glasses per standard 750 ml bottle.  Old practice
+# in the UK was 125 ml per glass, or 6 glasses per bottle. Some sources suggest
+# a more recent common value of 250 ml per glass, or 3 glasses per
+# bottle; as a multiple of 125 ml, this would be a lawful serving size offering.
+#
+# The value refers to the size of the serving, not the total volume of the
+# glass, which is typically not filled above the height of its greatest
+# diameter.
+#
+# A unit of alcohol is a specified amount of pure ethyl alcohol, expressed as a
+# mass or volumetric equivalent.  Many countries use the same concept but use
+# different terms. "Alcohol unit" is used officially in the UK; the US, Canada,
+# and Australia use "standard drink."  Values vary considerably among
+# countries.  The UK value of 8 g is nominally the amount of alcohol that a
+# typical adult can metabolize in one hour.
+
+alcoholunitus           14 g   / ethanoldensity
+alcoholunitca           13.6 g / ethanoldensity
+alcoholunituk            8 g   / ethanoldensity
+alcoholunitau           10 g   / ethanoldensity
+
+# Common serving sizes have roughly equivalent amounts of alcohol, as
+# illustrated by US examples for wine (12% Alcohol By Volume), beer (5% ABV),
+# and spirits (80 proof).
+#
+# alcoholunitus / 12% = 147.8 mL, close to the "standard" serving of 150 mL.
+# alcoholunitus / 5% = 11.995346 floz, close to a standard 12 floz bottle or can
+# alcoholunitus / 80 proof = 1.4994182 floz, close to a standard "shot" or jigger
+
+# https://www.rethinkingdrinking.niaaa.nih.gov/
+# https://www.cdc.gov/alcohol/faqs.htm
+# https://www.canada.ca/en/health-canada/services/substance-use/alcohol/low-risk-alcohol-drinking-guidelines
+# https://www.drinkaware.co.uk/
+# https://www.drinkaware.co.uk/facts/alcoholic-drinks-and-units
+# https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/545937/UK_CMOs__report.pdf
+# https://adf.org.au/reducing-risk/alcohol/alcohol-guidelines/
+# https://www.health.gov.au/topics/alcohol/about-alcohol/standard-drinks-guide
+# https://en.wikipedia.org/wiki/Unit_of_alcohol
+# https://en.wikipedia.org/wiki/Standard_drink
+
+# Coffee 
+#
+# The recommended ratio of coffee to water. Values vary considerably;
+# one is from the  Specialty Coffee Association of America: Brewing Best Practices
+# https://sca.coffee/research/protocols-best-practices
+
+coffeeratio             55 g/L  # +/- 10%
+
+# other recommendations are more loose, e.g.,
+# http://www.ncausa.org/About-Coffee/How-to-Brew-Coffee
+
+
+#
+# Water is "hard" if it contains various minerals, especially calcium
+# carbonate.
+#
+
+clarkdegree     grains/brgallon # Content by weigh of calcium carbonate
+gpg             grains/usgallon # Divide by water's density to convert to
+                                #   a dimensionless concentration measure
+#
+# Shoe measures
+#
+
+shoeiron                1|48 inch    # Used to measure leather in soles
+shoeounce               1|64 inch    # Used to measure non-sole shoe leather
+
+# USA shoe sizes.  These express the length of the shoe or the length
+# of the "last", the form that the shoe is made on.  But note that
+# this only captures the length.  It appears that widths change 1/4
+# inch for each letter within the same size, and if you change the
+# length by half a size then the width changes between 1/8 inch and
+# 1/4 inch.  But this may not be standard.  If you know better, please
+# contact me.
+
+shoesize_delta          1|3 inch     # USA shoe sizes differ by this amount
+shoe_men0               8.25 inch
+shoe_women0             (7+11|12) inch
+shoe_boys0              (3+11|12) inch
+shoe_girls0             (3+7|12) inch
+
+shoesize_men(n) units=[1;inch]   shoe_men0 + n shoesize_delta ; \
+                                (shoesize_men+(-shoe_men0))/shoesize_delta
+shoesize_women(n) units=[1;inch] shoe_women0 + n shoesize_delta ; \
+                                (shoesize_women+(-shoe_women0))/shoesize_delta
+shoesize_boys(n) units=[1;inch]  shoe_boys0 + n shoesize_delta ; \
+                                (shoesize_boys+(-shoe_boys0))/shoesize_delta
+shoesize_girls(n) units=[1;inch] shoe_girls0 + n shoesize_delta ; \
+                                (shoesize_girls+(-shoe_girls0))/shoesize_delta
+
+# European shoe size.  According to
+#      http://www.shoeline.com/footnotes/shoeterm.shtml
+# shoe sizes in Europe are measured with Paris points which simply measure
+# the length of the shoe.
+
+europeshoesize          2|3 cm
+
+#
+# USA slang units
+#
+
+buck                    US$
+fin                     5 US$
+sawbuck                 10 US$
+usgrand                 1000 US$
+greenback               US$
+key                     kg           # usually of marijuana, 60's
+lid                     1 oz         # Another 60's weed unit
+footballfield           usfootballfield
+usfootballfield         100 yards
+canadafootballfield     110 yards    # And 65 yards wide
+marathon                26 miles + 385 yards
+
+#
+# British
+#
+
+# The length measure in the UK was defined by a bronze bar manufactured in
+# 1844.  Various conversions were sanctioned for convenience at different
+# times, which makes conversions before 1963 a confusing matter.  Apparently
+# previous conversions were never explicitly revoked.  Four different
+# conversion factors appear below.  Multiply them times an imperial length
+# units as desired.  The Weights and Measures Act of 1963 switched the UK away
+# from their bronze standard and onto a definition of the yard in terms of the
+# meter.  This happened after an international agreement in 1959 to align the
+# world's measurement systems.
+
+UK                      UKlength_SJJ
+UK-                     UK
+british-                UK
+
+UKlength_B            0.9143992 meter / yard  # Benoit found the yard to be
+                                              #   0.9143992 m at a weights and
+                                              #   measures conference around
+                                              #   1896.   Legally sanctioned
+                                              #   in 1898.
+UKlength_SJJ          0.91439841 meter / yard # In 1922, Seers, Jolly and
+                                              #   Johnson found the yard to be
+                                              #   0.91439841 meters.
+                                              #   Used starting in the 1930's.
+UKlength_K              meter / 39.37079 inch # In 1816 Kater found this ratio
+                                              #   for the meter and inch.  This
+                                              #   value was used as the legal
+                                              #   conversion ratio when the
+                                              #   metric system was legalized
+                                              #   for contract in 1864.
+UKlength_C            meter / 1.09362311 yard # In 1866 Clarke found the meter
+                                              #   to be 1.09362311 yards.  This
+                                              #   conversion was legalized
+                                              #   around 1878.
+brnauticalmile          6080 ft               # Used until 1970 when the UK
+brknot                  brnauticalmile / hr   #   switched to the international
+admiraltymile           brnauticalmile        #   nautical mile.
+admiraltyknot           brknot
+seamile                 6000 ft
+shackle                 15 fathoms            # Adopted 1949 by British navy
+
+# British Imperial weight is mostly the same as US weight.  A few extra
+# units are added here.
+
+clove                   7 lb
+stone                   14 lb
+tod                     28 lb
+brquarterweight         1|4 brhundredweight
+brhundredweight         8 stone
+longhundredweight       brhundredweight
+longton                 20 brhundredweight
+brton                   longton
+
+# British Imperial volume measures
+
+brminim                 1|60 brdram
+brscruple               1|3 brdram
+fluidscruple            brscruple
+brdram                  1|8 brfloz
+brfluidounce            1|20 brpint
+brfloz                  brfluidounce
+brgill                  1|4 brpint
+brpint                  1|2 brquart
+brquart                 1|4 brgallon
+brgallon                4.54609 l      # The British Imperial gallon was
+                                       # defined in 1824 to be the volume of
+                                       # water which weighed 10 pounds at 62
+                                       # deg F with a pressure of 30 inHg.
+                                       # It was also defined as 277.274 in^3,
+                                       # Which is slightly in error.  In
+                                       # 1963 it was defined to be the volume
+                                       # occupied by 10 pounds of distilled
+                                       # water of density 0.998859 g/ml weighed
+                                       # in air of density 0.001217 g/ml
+                                       # against weights of density 8.136 g/ml.
+                                       # This gives a value of approximately
+                                       # 4.5459645 liters, but the old liter
+                                       # was in force at this time.  In 1976
+                                       # the definition was changed to exactly
+                                       # 4.54609 liters using the new
+                                       # definition of the liter (1 dm^3).
+brbarrel                36 brgallon    # Used for beer
+brbushel                8 brgallon
+brheapedbushel          1.278 brbushel
+brquarter               8 brbushel
+brchaldron              36 brbushel
+
+# Obscure British volume measures.  These units are generally traditional
+# measures whose definitions have fluctuated over the years.  Often they
+# depended on the quantity being measured.  They are given here in terms of
+# British Imperial measures.  For example, the puncheon may have historically
+# been defined relative to the wine gallon or beer gallon or ale gallon
+# rather than the British Imperial gallon.
+
+bag                     4 brbushel
+bucket                  4 brgallon
+kilderkin               2 brfirkin
+last                    40 brbushel
+noggin                  brgill
+pottle                  0.5 brgallon
+pin                     4.5 brgallon
+puncheon                72 brgallon
+seam                    8 brbushel
+coomb                   4 brbushel
+boll                    6 brbushel
+firlot                  1|4 boll
+brfirkin                9 brgallon     # Used for ale and beer
+cran                    37.5 brgallon  # measures herring, about 750 fish
+brwinehogshead          52.5 brgallon  # This value is approximately equal
+brhogshead              brwinehogshead #   to the old wine hogshead of 63
+                                       #   wine gallons.  This adjustment
+                                       #   is listed in the OED and in
+                                       #   "The Weights and Measures of
+                                       #   England" by R. D. Connor
+brbeerhogshead          54 brgallon
+brbeerbutt              2 brbeerhogshead
+registerton             100 ft^3  # Used for internal capacity of ships
+shippington             40 ft^3   # Used for ship's cargo freight or timber
+brshippington           42 ft^3   #
+freightton            shippington # Both register ton and shipping ton derive
+                                  # from the "tun cask" of wine.
+displacementton         35 ft^3   # Approximate volume of a longton weight of
+                                  # sea water.  Measures water displaced by
+                                  # ships.
+waterton                224 brgallon
+strike                  70.5 l    # 16th century unit, sometimes
+                                  #   defined as .5, 2, or 4 bushels
+                                  #   depending on the location.  It
+                                  #   probably doesn't make a lot of
+                                  #   sense to define in terms of imperial
+                                  #   bushels.  Zupko gives a value of
+                                  #   2 Winchester grain bushels or about
+                                  #   70.5 liters.
+amber                   4 brbushel# Used for dry and liquid capacity [18]
+
+# British volume measures with "imperial"
+
+imperialminim           brminim
+imperialscruple         brscruple
+imperialdram            brdram
+imperialfluidounce      brfluidounce
+imperialfloz            brfloz
+imperialgill            brgill
+imperialpint            brpint
+imperialquart           brquart
+imperialgallon          brgallon
+imperialbarrel          brbarrel
+imperialbushel          brbushel
+imperialheapedbushel    brheapedbushel
+imperialquarter         brquarter
+imperialchaldron        brchaldron
+imperialwinehogshead    brwinehogshead
+imperialhogshead        brhogshead
+imperialbeerhogshead    brbeerhogshead
+imperialbeerbutt        brbeerbutt
+imperialfirkin          brfirkin
+
+# obscure British lengths
+
+barleycorn              1|3 UKinch   # Given in Realm of Measure as the
+                                     # difference between successive shoe sizes
+nail                    1|16 UKyard  # Originally the width of the thumbnail,
+                                     #   or 1|16 ft.  This took on the general
+                                     #   meaning of 1|16 and settled on the
+                                     #   nail of a yard or 1|16 yards as its
+                                     #   final value.  [12]
+UKpole                  16.5 UKft    # This was 15 Saxon feet, the Saxon
+rope                    20 UKft      #   foot (aka northern foot) being longer
+englishell              45 UKinch
+flemishell              27 UKinch
+ell                     englishell   # supposed to be measure from elbow to
+                                     #   fingertips
+span                    9 UKinch     # supposed to be distance from thumb
+                                     #   to pinky with full hand extension
+goad                    4.5 UKft     # used for cloth, possibly named after the
+                                     #   stick used for prodding animals.
+
+# misc obscure British units
+
+hide                    120 acre  # English unit of land area dating to the 7th
+                                  #   century, originally the amount of land
+                                  #   that a single plowman could cultivate,
+                                  #   which varied from 60-180 acres regionally.
+                                  #   Standardized at Normon conquest.
+virgate                 1|4 hide
+nook                    1|2 virgate
+rood                    furlong rod  # Area of a strip a rod by a furlong
+englishcarat            troyounce/151.5 # Originally intended to be 4 grain
+                                        #   but this value ended up being
+                                        #   used in the London diamond market
+mancus                  2 oz
+mast                    2.5 lb
+nailkeg                 100 lbs
+basebox                 31360 in^2      # Used in metal plating
+
+# alternate spellings
+
+gramme                  gram
+litre                   liter
+dioptre                 diopter
+sulphur                 sulfur
+
+#
+# Units derived the human body (may not be very accurate)
+#
+
+geometricpace           5 ft   # distance between points where the same
+                               # foot hits the ground
+pace                    2.5 ft # distance between points where alternate
+                               # feet touch the ground
+USmilitarypace          30 in  # United States official military pace
+USdoubletimepace        36 in  # United States official doubletime pace
+fingerbreadth           7|8 in # The finger is defined as either the width
+fingerlength            4.5 in #   or length of the finger
+finger                  fingerbreadth
+palmwidth               hand   # The palm is a unit defined as either the width
+palmlength              8 in   #   or the length of the hand
+hand                    4 inch # width of hand
+shaftment               6 inch # Distance from tip of outstretched thumb to the
+                               #   opposite side of the palm of the hand.  The
+                               #   ending -ment is from the old English word
+                               #   for hand. [18]
+smoot              5 ft + 7 in # Created as part of an MIT fraternity prank.
+                               #   In 1958 Oliver Smoot was used to measure
+                               #   the length of the Harvard Bridge, which was
+                               #   marked off in Smoot lengths.  These
+                               #   markings have been maintained on the bridge
+                               #   since then and repainted by subsequent
+                               #   incoming fraternity members.  During a
+                               #   bridge renovation the new sidewalk was
+                               #   scored every Smoot rather than at the
+                               #   customary 6 ft spacing.
+tomcruise       5 ft + 7.75 in # Height of Tom Cruise
+
+#
+# Cooking measures
+#
+
+# Common abbreviations
+
+tbl                     tablespoon
+tbsp                    tablespoon
+tblsp                   tablespoon
+Tb                      tablespoon
+tsp                     teaspoon
+saltspoon               1|4 tsp
+
+# US measures
+
+uscup                   8 usfloz
+ustablespoon            1|16 uscup
+usteaspoon              1|3 ustablespoon
+ustbl                   ustablespoon
+ustbsp                  ustablespoon
+ustblsp                 ustablespoon
+ustsp                   usteaspoon
+metriccup               250 ml
+stickbutter             1|4 lb            # Butter in the USA is sold in one
+                                          # pound packages that contain four
+                                          # individually wrapped pieces.  The
+                                          # pieces are marked into tablespoons,
+                                          # making it possible to measure out
+                                          # butter by volume by slicing the
+                                          # butter.
+
+legalcup                240 ml            # The cup used on nutrition labeling
+legaltablespoon         1|16 legalcup
+legaltbsp               legaltablespoon
+
+# Scoop size.  Ice cream scoops in the US are marked with numbers
+# indicating the number of scoops required to fill a US quart.
+
+scoop(n)  units=[1;cup] domain=[4,100] range=[0.04,1] \
+           32 usfloz / n ; 32 usfloz / scoop
+
+
+# US can sizes.
+
+number1can              10 usfloz
+number2can              19 usfloz
+number2.5can            3.5 uscups
+number3can              4 uscups
+number5can              7 uscups
+number10can             105 usfloz
+
+# British measures
+
+brcup                   1|2 brpint
+brteacup                1|3 brpint
+brtablespoon            15 ml             # Also 5|8 brfloz, approx 17.7 ml
+brteaspoon              1|3 brtablespoon  # Also 1|4 brtablespoon
+brdessertspoon          2 brteaspoon
+dessertspoon            brdessertspoon
+dsp                     dessertspoon
+brtsp                   brteaspoon
+brtbl                   brtablespoon
+brtbsp                  brtablespoon
+brtblsp                 brtablespoon
+
+# Australian
+
+australiatablespoon     20 ml
+austbl                  australiatablespoon
+austbsp                 australiatablespoon
+austblsp                australiatablespoon
+australiateaspoon       1|4 australiatablespoon
+austsp                  australiateaspoon
+
+# Italian
+
+etto                    100 g          # Used for buying items like meat and
+etti                    etto           #   cheese.
+
+# Chinese
+
+catty                   0.5 kg
+oldcatty                4|3 lbs        # Before metric conversion.
+tael                    1|16 oldcatty  # Should the tael be defined both ways?
+mace                    0.1 tael
+oldpicul                100 oldcatty
+picul                   100 catty      # Chinese usage
+
+# Indian
+
+seer                    14400 grain    # British Colonial standard
+ser                     seer
+maund                   40 seer
+pakistanseer            1 kg
+pakistanmaund           40 pakistanseer
+chittak                 1|16 seer
+tola                    1|5 chittak
+ollock                  1|4 liter      # Is this right?
+
+# Japanese
+
+japancup                200 ml
+
+# densities of cooking ingredients from The Cake Bible by Rose Levy Beranbaum
+# so you can convert '2 cups sugar' to grams, for example, or in the other
+# direction grams could be converted to 'cup flour_scooped'.
+
+butter                  8 oz/uscup
+butter_clarified        6.8 oz/uscup
+cocoa_butter            9 oz/uscup
+shortening              6.75 oz/uscup   # vegetable shortening
+oil                     7.5 oz/uscup
+cakeflour_sifted        3.5 oz/uscup    # The density of flour depends on the
+cakeflour_spooned       4 oz/uscup      # measuring method.  "Scooped",  or
+cakeflour_scooped       4.5 oz/uscup    # "dip and sweep" refers to dipping a
+flour_sifted            4 oz/uscup      # measure into a bin, and then sweeping
+flour_spooned           4.25 oz/uscup   # the excess off the top.  "Spooned"
+flour_scooped           5 oz/uscup      # means to lightly spoon into a measure
+breadflour_sifted       4.25 oz/uscup   # and then sweep the top.  Sifted means
+breadflour_spooned      4.5 oz/uscup    # sifting the flour directly into a
+breadflour_scooped      5.5 oz/uscup    # measure and then sweeping the top.
+cornstarch              120 grams/uscup
+dutchcocoa_sifted       75 g/uscup      # These are for Dutch processed cocoa
+dutchcocoa_spooned      92 g/uscup
+dutchcocoa_scooped      95 g/uscup
+cocoa_sifted            75 g/uscup      # These are for nonalkalized cocoa
+cocoa_spooned           82 g/uscup
+cocoa_scooped           95 g/uscup
+heavycream              232 g/uscup
+milk                    242 g/uscup
+sourcream               242 g/uscup
+molasses                11.25 oz/uscup
+cornsyrup               11.5 oz/uscup
+honey                   11.75 oz/uscup
+sugar                   200 g/uscup
+powdered_sugar          4 oz/uscup
+brownsugar_light        217 g/uscup     # packed
+brownsugar_dark         239 g/uscup
+
+baking_powder           4.6 grams / ustsp
+salt                    6 g / ustsp
+koshersalt              2.8 g / ustsp   # Diamond Crystal kosher salt
+koshersalt_morton       4.8 g / ustsp   # Morton kosher salt
+                                        # Values are from the nutrition info
+                                        # on the packages
+
+
+# Egg weights and volumes for a USA large egg
+
+egg                     50 grams        # without shell
+eggwhite                30 grams
+eggyolk                 18.6 grams
+eggvolume               3 ustablespoons + 1|2 ustsp
+eggwhitevolume          2 ustablespoons
+eggyolkvolume           3.5 ustsp
+
+# Alcohol density
+
+ethanoldensity          0.7893 g/cm^3   # From CRC Handbook, 91st Edition
+alcoholdensity          ethanoldensity
+
+#
+# Density measures.  Density has traditionally been measured on a variety of
+# bizarre nonlinear scales.
+#
+
+# Density of a sugar syrup is frequently measured in candy making procedures.
+# In the USA the boiling point of the syrup is measured.  Some recipes instead
+# specify the density using degrees Baume.  Conversion between degrees Baume
+# and the boiling point measure has proved elusive.  This table appeared in one
+# text, and provides a fragmentary relationship to the concentration.
+#
+# temp(C)  conc (%)
+#   100      30
+#   101      40
+#   102      50
+#   103      60
+#   106      70
+#   112      80
+#   123      90
+#   140      95
+#   151      97
+#   160      98.2
+#   166      99.5
+#   171      99.6
+#
+# The best source identified to date came from "Boiling point elevation of
+# technical sugarcane solutions and its use in automatic pan boiling" by
+# Michael Saska.  International Sugar Journal, 2002, 104, 1247, pp 500-507.
+#
+# But I'm using equation (3) which is credited to Starzak and Peacock,
+# "Water activity coefficient in aqueous solutions of sucrose--A comprehensive
+# data analysis.  Zuckerindustrie, 122, 380-387.  (I couldn't find this
+# document.)
+#
+# Note that the range of validity is uncertain, but answers are in agreement
+# with the above table all the way to 99.6.
+#
+# The original equation has a parameter for the boiling point of water, which
+# of course varies with altitude.  It also includes various other model
+# parameters.  The input is the molar concentration of sucrose in the solution,
+# (moles sucrose) / (total moles).
+#
+# Bsp 3797.06 degC
+# Csp 226.28 degC
+# QQ -17638 J/mol
+# asp -1.0038
+# bsp -0.24653
+# tbw 100 degC     # boiling point of water
+# sugar_bpe_orig(x) ((1-QQ/R Bsp * x^2 (1+asp x + bsp x^2) (tbw + Csp) \
+#           /(tbw+stdtemp)) /  (1+(tbw + Csp)/Bsp *ln(1-x))-1) * (tbw + Csp)
+#
+# To convert mass concentration (brix) to molar concentration
+#
+# sc(x)  (x / 342.3) / (( x/342.3) + (100-x)/18.02); \
+#        100 sc 342.3|18.02 / (sc (342.3|18.02-1)+1)
+#
+# Here is a simplified version of this equation where the temperature of boiling
+# water has been fixed at 100 degrees Celsius and the argument is now the
+# concentration (brix).
+#
+# sugar_bpe(x) ((1+ 0.48851085 * sc(x)^2 (1+ -1.0038 sc(x) + -0.24653 sc(x)^2)) \
+#                   / (1+0.08592964 ln(1-sc(x)))-1) 326.28 K
+#
+#
+# The formula is not invertible, so to implement it in units we unfortunately
+# must turn it into a table.
+
+# This table gives the boiling point elevation as a function of the sugar syrup
+# concentration expressed as a percentage.
+
+sugar_conc_bpe[K] \
+ 0 0.0000   5 0.0788  10 0.1690  15 0.2729  20 0.3936  25 0.5351  \
+30 0.7027  35 0.9036  40 1.1475  42 1.2599  44 1.3825  46 1.5165  \
+48 1.6634  50 1.8249  52 2.0031  54 2.2005  56 2.4200  58 2.6651  \
+60 2.9400  61 3.0902  62 3.2499  63 3.4198  64 3.6010  65 3.7944  \
+66 4.0012  67 4.2227  68 4.4603  69 4.7156  70 4.9905  71 5.2870  \
+72 5.6075  73 5.9546  74 6.3316  75 6.7417  76 7.1892  77 7.6786  \
+78.0  8.2155  79.0  8.8061  80.0  9.4578  80.5  9.8092  81.0 10.1793  \
+81.5 10.5693  82.0 10.9807  82.5 11.4152  83.0 11.8743  83.5 12.3601  \
+84.0 12.8744  84.5 13.4197  85.0 13.9982  85.5 14.6128  86.0 15.2663  \
+86.5 15.9620  87.0 16.7033  87.5 17.4943  88.0 18.3391  88.5 19.2424  \
+89.0 20.2092  89.5 21.2452  90.0 22.3564  90.5 23.5493  91.0 24.8309  \
+91.5 26.2086  92.0 27.6903  92.5 29.2839  93.0 30.9972  93.5 32.8374  \
+94.0 34.8104  94.5 36.9195  95.0 39.1636  95.5 41.5348  96.0 44.0142  \
+96.5 46.5668  97.0 49.1350  97.5 51.6347  98.0 53.9681  98.1 54.4091  \
+98.2 54.8423  98.3 55.2692  98.4 55.6928  98.5 56.1174  98.6 56.5497  \
+98.7 56.9999  98.8 57.4828  98.9 58.0206  99.0 58.6455  99.1 59.4062  \
+99.2 60.3763  99.3 61.6706  99.4 63.4751  99.5 66.1062  99.6 70.1448  \
+99.7 76.7867
+
+# Using the brix table we can use this to produce a mapping from boiling point
+# to density which makes all of the units interconvertible.  Because the brix
+# table stops at 95 this approach works up to a boiling point elevation of 39 K
+# or a boiling point of 139 C / 282 F, which is the "soft crack" stage in candy
+# making.  The "hard crack" stage continues up to 310 F.
+
+# Boiling point elevation
+sugar_bpe(T) units=[K;g/cm^3] domain=[0,39.1636] range=[0.99717,1.5144619] \
+               brix(~sugar_conc_bpe(T)); sugar_conc_bpe(~brix(sugar_bpe))
+# Absolute boiling point (produces an absolute temperature)
+sugar_bp(T) units=[K;g/cm^3] domain=[373.15,412.3136] \
+                                         range=[0.99717,1.5144619] \
+                        brix(~sugar_conc_bpe(T-tempC(100))) ;\
+                        sugar_conc_bpe(~brix(sugar_bp))+tempC(100)
+
+# In practice dealing with the absolute temperature is annoying because it is
+# not possible to convert to a nested function, so you're stuck retyping the
+# absolute temperature in Kelvins to convert to celsius or Fahrenheit.  To
+# prevent this we supply definitions that build in the temperature conversion
+# and produce results in the Fahrenheit and Celsius scales.  So using these
+# measures, to convert 46 degrees Baume to a Fahrenheit boiling point:
+#
+#      You have: baume(45)
+#      You want: sugar_bpF
+#              239.05647
+#
+sugar_bpF(T) units=[1;g/cm^3] domain=[212,282.49448] range=[0.99717,1.5144619]\
+                        brix(~sugar_conc_bpe(tempF(T)+-tempC(100))) ;\
+                        ~tempF(sugar_conc_bpe(~brix(sugar_bpF))+tempC(100))
+sugar_bpC(T) units=[1;g/cm^3] domain=[100,139.1636] range=[0.99717,1.5144619]\
+                        brix(~sugar_conc_bpe(tempC(T)+-tempC(100))) ;\
+                        ~tempC(sugar_conc_bpe(~brix(sugar_bpC))+tempC(100))
+
+# Degrees Baume is used in European recipes to specify the density of a sugar
+# syrup.  An entirely different definition is used for densities below
+# 1 g/cm^3.  An arbitrary constant appears in the definition.  This value is
+# equal to 145 in the US, but was according to [], the old scale used in
+# Holland had a value of 144, and the new scale or Gerlach scale used 146.78.
+
+baumeconst 145      # US value
+baume(d) units=[1;g/cm^3] domain=[0,145) range=[1,) \
+                          (baumeconst/(baumeconst+-d)) g/cm^3 ; \
+                          (baume+((-g)/cm^3)) baumeconst / baume
+
+# It's not clear if this value was ever used with negative degrees.
+twaddell(x) units=[1;g/cm^3] domain=[-200,) range=[0,) \
+                             (1 + 0.005 x) g / cm^3 ; \
+                             200 (twaddell / (g/cm^3) +- 1)
+
+# The degree quevenne is a unit for measuring the density of milk.
+# Similarly it's unclear if negative values were allowed here.
+quevenne(x) units=[1;g/cm^3] domain=[-1000,) range=[0,) \
+                             (1 + 0.001 x) g / cm^3 ; \
+                             1000 (quevenne / (g/cm^3) +- 1)
+
+# Degrees brix measures sugar concentration by weigh as a percentage, so a
+# solution that is 3 degrees brix is 3% sugar by weight.  This unit was named
+# after Adolf Brix who invented a hydrometer that read this percentage
+# directly.  This data is from Table 114 of NIST Circular 440, "Polarimetry,
+# Saccharimetry and the Sugars".  It gives apparent specific gravity at 20
+# degrees Celsius of various sugar concentrations.  As rendered below this
+# data is converted to apparent density at 20 degrees Celsius using the
+# density figure for water given in the same NIST reference.  They use the
+# word "apparent" to refer to measurements being made in air with brass
+# weights rather than vacuum.
+
+brix[0.99717g/cm^3]\
+    0 1.00000  1 1.00390  2 1.00780  3 1.01173  4 1.01569  5 1.01968 \
+    6 1.02369  7 1.02773  8 1.03180  9 1.03590 10 1.04003 11 1.04418 \
+   12 1.04837 13 1.05259 14 1.05683 15 1.06111 16 1.06542 17 1.06976 \
+   18 1.07413 19 1.07853 20 1.08297 21 1.08744 22 1.09194 23 1.09647 \
+   24 1.10104 25 1.10564 26 1.11027 27 1.11493 28 1.11963 29 1.12436 \
+   30 1.12913 31 1.13394 32 1.13877 33 1.14364 34 1.14855 35 1.15350 \
+   36 1.15847 37 1.16349 38 1.16853 39 1.17362 40 1.17874 41 1.18390 \
+   42 1.18910 43 1.19434 44 1.19961 45 1.20491 46 1.21026 47 1.21564 \
+   48 1.22106 49 1.22652 50 1.23202 51 1.23756 52 1.24313 53 1.24874 \
+   54 1.25439 55 1.26007 56 1.26580 57 1.27156 58 1.27736 59 1.28320 \
+   60 1.28909 61 1.29498 62 1.30093 63 1.30694 64 1.31297 65 1.31905 \
+   66 1.32516 67 1.33129 68 1.33748 69 1.34371 70 1.34997 71 1.35627 \
+   72 1.36261 73 1.36900 74 1.37541 75 1.38187 76 1.38835 77 1.39489 \
+   78 1.40146 79 1.40806 80 1.41471 81 1.42138 82 1.42810 83 1.43486 \
+   84 1.44165 85 1.44848 86 1.45535 87 1.46225 88 1.46919 89 1.47616 \
+   90 1.48317 91 1.49022 92 1.49730 93 1.50442 94 1.51157 95 1.51876
+
+# Density measure invented by the American Petroleum Institute.  Lighter
+# petroleum products are more valuable, and they get a higher API degree.
+#
+# The intervals of range and domain should be open rather than closed.
+#
+apidegree(x) units=[1;g/cm^3] domain=[-131.5,) range=[0,) \
+                              141.5 g/cm^3 / (x+131.5) ; \
+                              141.5 (g/cm^3) / apidegree + (-131.5)
+#
+# Average densities of various woods (dried)
+# Data from The Wood Database https://www.wood-database.com
+#
+
+# North American Hardwoods
+
+wood_cherry             35 lb/ft^3
+wood_redoak             44 lb/ft^3
+wood_whiteoak           47 lb/ft^3
+wood_blackwalnut        38 lb/ft^3
+wood_walnut             wood_blackwalnut
+wood_birch              43 lb/ft^3
+wood_hardmaple          44 lb/ft^3
+
+wood_bigleafmaple       34 lb/ft^3
+wood_boxeldermaple      30 lb/ft^3
+wood_redmaple           38 lb/ft^3
+wood_silvermaple        33 lb/ft^3
+wood_stripedmaple       32 lb/ft^3
+wood_softmaple         (wood_bigleafmaple \
+                      + wood_boxeldermaple \
+                      + wood_redmaple \
+                      + wood_silvermaple \
+                      + wood_stripedmaple) / 5
+wood_poplar             29 lb/ft^3
+wood_beech              45 lb/ft^3
+
+# North American Softwoods
+
+wood_jeffreypine        28 lb/ft^3
+wood_ocotepine          44 lb/ft^3
+wood_ponderosapine      28 lb/ft^3
+
+wood_loblollypine       35 lb/ft^3
+wood_longleafpine       41 lb/ft^3
+wood_shortleafpine      35 lb/ft^3
+wood_slashpine          41 lb/ft^3
+wood_yellowpine        (wood_loblollypine \
+                      + wood_longleafpine \
+                      + wood_shortleafpine \
+                      + wood_slashpine) / 4
+wood_redpine            34 lb/ft^3
+
+wood_easternwhitepine   25 lb/ft^3
+wood_westernwhitepine   27 lb/ft^3
+wood_whitepine         (wood_easternwhitepine + wood_westernwhitepine) / 2
+
+wood_douglasfir         32 lb/ft^3
+
+wood_blackspruce        28 lb/ft^3
+wood_engelmannspruce    24 lb/ft^3
+wood_redspruce          27 lb/ft^3
+wood_sitkaspruce        27 lb/ft^3
+wood_whitespruce        27 lb/ft^3
+wood_spruce            (wood_blackspruce \
+                      + wood_engelmannspruce \
+                      + wood_redspruce \
+                      + wood_sitkaspruce \
+                      + wood_whitespruce) / 5
+
+# Other woods 
+
+wood_basswood           26 lb/ft^3
+wood_balsa               9 lb/ft^3
+wood_ebony_gaboon       60 lb/ft^3
+wood_ebony_macassar     70 lb/ft^3
+wood_mahogany           37 lb/ft^3   # True (Honduran) mahogany,
+                                     # Swietenia macrophylla
+wood_teak               41 lb/ft^3   
+wood_rosewood_brazilian 52 lb/ft^3
+wood_rosewood_honduran  64 lb/ft^3
+wood_rosewood_indian    52 lb/ft^3
+wood_cocobolo           69 lb/ft^3          
+wood_bubinga            56 lb/ft^3
+wood_zebrawood          50 lb/ft^3
+wood_koa                38 lb/ft^3
+wood_snakewood          75.7 lb/ft^3
+wood_lignumvitae        78.5 lb/ft^3
+wood_blackwood          79.3 lb/ft^3
+wood_blackironwood      84.5 lb/ft^3 # Krugiodendron ferreum, listed
+                                     #   in database as the heaviest wood
+
+#
+# Modulus of elasticity of selected woods.
+# Data from The Wood Database https://www.wood-database.com
+#
+
+# North American Hardwoods
+
+wood_mod_beech              1.720e6 lbf/in^2
+wood_mod_birchyellow        2.010e6 lbf/in^2
+wood_mod_birch              wood_mod_birchyellow
+wood_mod_cherry             1.490e6 lbf/in^2
+wood_mod_hardmaple          1.830e6 lbf/in^2
+
+wood_mod_bigleafmaple       1.450e6 lbf/in^2
+wood_mod_boxeldermaple      1.050e6 lbf/in^2
+wood_mod_redmaple           1.640e6 lbf/in^2
+wood_mod_silvermaple        1.140e6 lbf/in^2
+wood_mod_softmaple         (wood_mod_bigleafmaple \
+                          + wood_mod_boxeldermaple \
+                          + wood_mod_redmaple \
+                          + wood_mod_silvermaple) / 4
+
+wood_mod_redoak             1.761e6 lbf/in^2
+wood_mod_whiteoak           1.762e6 lbf/in^2
+wood_mod_poplar             1.580e6 lbf/in^2
+wood_mod_blackwalnut        1.680e6 lbf/in^2
+wood_mod_walnut             wood_mod_blackwalnut
+
+# North American Softwoods
+
+wood_mod_jeffreypine        1.240e6 lbf/in^2
+wood_mod_ocotepine          2.209e6 lbf/in^2
+wood_mod_ponderosapine      1.290e6 lbf/in^2
+
+wood_mod_loblollypine       1.790e6 lbf/in^2
+wood_mod_longleafpine       1.980e6 lbf/in^2
+wood_mod_shortleafpine      1.750e6 lbf/in^2
+wood_mod_slashpine          1.980e6 lbf/in^2
+wood_mod_yellowpine        (wood_mod_loblollypine \
+                          + wood_mod_longleafpine \
+                          + wood_mod_shortleafpine \
+                          + wood_mod_slashpine) / 4
+
+wood_mod_redpine            1.630e6 lbf/in^2
+
+wood_mod_easternwhitepine   1.240e6 lbf/in^2
+wood_mod_westernwhitepine   1.460e6 lbf/in^2
+wood_mod_whitepine         (wood_mod_easternwhitepine + \
+                            wood_mod_westernwhitepine) / 2
+
+wood_mod_douglasfir         1.765e6  lbf/in^2
+
+wood_mod_blackspruce        1.523e6 lbf/in^2
+wood_mod_englemannspruce    1.369e6 lbf/in^2
+wood_mod_redspruce          1.560e6 lbf/in^2
+wood_mod_sitkaspruce        1.600e6 lbf/in^2
+wood_mod_whitespruce        1.315e6 lbf/in^2
+wood_mod_spruce            (wood_mod_blackspruce \
+                          + wood_mod_englemannspruce \
+                          + wood_mod_redspruce + wood_mod_sitkaspruce \
+                          + wood_mod_whitespruce) / 5
+
+# Other woods 
+
+wood_mod_balsa              0.538e6 lbf/in^2
+wood_mod_basswood           1.460e6 lbf/in^2
+wood_mod_blackwood          2.603e6 lbf/in^2  # African, Dalbergia melanoxylon
+wood_mod_bubinga            2.670e6 lbf/in^2
+wood_mod_cocobolo           2.712e6 lbf/in^2
+wood_mod_ebony_gaboon       2.449e6 lbf/in^2
+wood_mod_ebony_macassar     2.515e6 lbf/in^2
+wood_mod_blackironwood      2.966e6 lbf/in^2  # Krugiodendron ferreum
+wood_mod_koa                1.503e6 lbf/in^2
+wood_mod_lignumvitae        2.043e6 lbf/in^2
+wood_mod_mahogany           1.458e6 lbf/in^2  # True (Honduran) mahogany,
+                                              # Swietenia macrophylla
+wood_mod_rosewood_brazilian 2.020e6 lbf/in^2
+wood_mod_rosewood_honduran  3.190e6 lbf/in^2
+wood_mod_rosewood_indian    1.668e6 lbf/in^2
+wood_mod_snakewood          3.364e6 lbf/in^2
+wood_mod_teak               1.781e6 lbf/in^2
+wood_mod_zebrawood          2.374e6 lbf/in^2
+
+#
+# Area of countries and other regions.  This is the "total area" which
+# includes land and water areas within international boundaries and
+# coastlines.  Data from January, 2019.  
+#
+# except as noted, sources are
+# https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_area
+# US Central Intelligence Agency: The World Factbook
+# https://www.cia.gov/the-world-factbook/
+
+area_russia              17098246 km^2
+area_antarctica          14000000 km^2
+# area_canada is covered below as sum of province and territory areas
+area_china                9596961 km^2
+# area_unitedstates is covered below as sum of state areas
+# includes only the 50 states and District of Columbia
+area_us                   area_unitedstates
+area_brazil               8515767 km^2
+area_australia            7692024 km^2
+# area_europeanunion is covered below as sum of member areas
+area_india                3287263 km^2
+area_argentina            2780400 km^2
+area_kazakhstan           2724900 km^2
+area_algeria              2381741 km^2
+area_drcongo              2344858 km^2
+area_greenland            2166086 km^2
+area_saudiarabia          2149690 km^2
+area_mexico               1964375 km^2
+area_indonesia            1910931 km^2
+area_sudan                1861484 km^2
+area_libya                1759540 km^2
+area_iran                 1648195 km^2
+area_mongolia             1564110 km^2
+area_peru                 1285216 km^2
+area_chad                 1284000 km^2
+area_niger                1267000 km^2
+area_angola               1246700 km^2
+area_mali                 1240192 km^2
+area_southafrica          1221037 km^2
+area_colombia             1141748 km^2
+area_ethiopia             1104300 km^2
+area_bolivia              1098581 km^2
+area_mauritania           1030700 km^2
+area_egypt                1002450 km^2
+area_tanzania              945087 km^2
+area_nigeria               923768 km^2
+area_venezuela             916445 km^2
+area_pakistan              881912 km^2
+area_namibia               825615 km^2
+area_mozambique            801590 km^2
+area_turkey                783562 km^2
+area_chile                 756102 km^2
+area_zambia                752612 km^2
+area_myanmar               676578 km^2
+area_burma                area_myanmar
+area_afghanistan           652230 km^2
+area_southsudan            644329 km^2
+area_france                640679 km^2
+area_somalia               637657 km^2
+area_centralafrica         622984 km^2
+area_ukraine               603500 km^2
+area_crimea                 27000 km^2  # occupied by Russia; included in
+                                        # (Encyclopedia Britannica)
+area_madagascar            587041 km^2
+area_botswana              581730 km^2
+area_kenya                 580367 km^2
+area_yemen                 527968 km^2
+area_thailand              513120 km^2
+area_spain                 505992 km^2
+area_turkmenistan          488100 km^2
+area_cameroon              475422 km^2
+area_papuanewguinea        462840 km^2
+area_sweden                450295 km^2
+area_uzbekistan            447400 km^2
+area_morocco               446550 km^2
+area_iraq                  438317 km^2
+area_paraguay              406752 km^2
+area_zimbabwe              390757 km^2
+area_japan                 377973 km^2
+area_germany               357114 km^2
+area_congorepublic         342000 km^2
+area_finland               338424 km^2
+area_vietnam               331212 km^2
+area_malaysia              330803 km^2
+area_norway                323802 km^2
+area_ivorycoast            322463 km^2
+area_poland                312696 km^2
+area_oman                  309500 km^2
+area_italy                 301339 km^2
+area_philippines           300000 km^2
+area_ecuador               276841 km^2
+area_burkinafaso           274222 km^2
+area_newzealand            270467 km^2
+area_gabon                 267668 km^2
+area_westernsahara         266000 km^2
+area_guinea                245857 km^2
+# area_unitedkingdom is covered below
+area_uganda                241550 km^2
+area_ghana                 238533 km^2
+area_romania               238397 km^2
+area_laos                  236800 km^2
+area_guyana                214969 km^2
+area_belarus               207600 km^2
+area_kyrgyzstan            199951 km^2
+area_senegal               196722 km^2
+area_syria                 185180 km^2
+area_golanheights            1150 km^2  # occupied by Israel; included in 
+                                        # Syria (Encyclopedia Britannica)
+area_cambodia              181035 km^2
+area_uruguay               176215 km^2
+area_somaliland            176120 km^2
+area_suriname              163820 km^2
+area_tunisia               163610 km^2
+area_bangladesh            147570 km^2
+area_nepal                 147181 km^2
+area_tajikistan            143100 km^2
+area_greece                131990 km^2
+area_nicaragua             130373 km^2
+area_northkorea            120540 km^2
+area_malawi                118484 km^2
+area_eritrea               117600 km^2
+area_benin                 114763 km^2
+area_honduras              112492 km^2
+area_liberia               111369 km^2
+area_bulgaria              110879 km^2
+area_cuba                  109884 km^2
+area_guatemala             108889 km^2
+area_iceland               103000 km^2
+area_southkorea            100210 km^2
+area_hungary                93028 km^2
+area_portugal               92090 km^2
+area_jordan                 89342 km^2
+area_serbia                 88361 km^2
+area_azerbaijan             86600 km^2
+area_austria                83871 km^2
+area_uae                    83600 km^2
+area_czechia                78865 km^2
+area_czechrepublic         area_czechia
+area_panama                 75417 km^2
+area_sierraleone            71740 km^2
+area_ireland                70273 km^2
+area_georgia                69700 km^2
+area_srilanka               65610 km^2
+area_lithuania              65300 km^2
+area_latvia                 64559 km^2
+area_togo                   56785 km^2
+area_croatia                56594 km^2
+area_bosnia                 51209 km^2
+area_costarica              51100 km^2
+area_slovakia               49037 km^2
+area_dominicanrepublic      48671 km^2
+area_estonia                45227 km^2
+area_denmark                43094 km^2
+area_netherlands            41850 km^2
+area_switzerland            41284 km^2
+area_bhutan                 38394 km^2
+area_taiwan                 36193 km^2
+area_guineabissau           36125 km^2
+area_moldova                33846 km^2
+area_belgium                30528 km^2
+area_lesotho                30355 km^2
+area_armenia                29743 km^2
+area_solomonislands         28896 km^2
+area_albania                28748 km^2
+area_equitorialguinea       28051 km^2
+area_burundi                27834 km^2
+area_haiti                  27750 km^2
+area_rwanda                 26338 km^2
+area_northmacedonia         25713 km^2
+area_djibouti               23200 km^2
+area_belize                 22966 km^2
+area_elsalvador             21041 km^2
+area_israel                 20770 km^2
+area_slovenia               20273 km^2
+area_fiji                   18272 km^2
+area_kuwait                 17818 km^2
+area_eswatini               17364 km^2
+area_easttimor              14919 km^2
+area_bahamas                13943 km^2
+area_montenegro             13812 km^2
+area_vanatu                 12189 km^2
+area_qatar                  11586 km^2
+area_gambia                 11295 km^2
+area_jamaica                10991 km^2
+area_kosovo                 10887 km^2
+area_lebanon                10452 km^2
+area_cyprus                  9251 km^2
+area_puertorico              9104 km^2  # United States territory; not included
+                                        #   in United States area
+area_westbank                5860 km^2  # (CIA World Factbook)
+area_hongkong                2755 km^2
+area_luxembourg              2586 km^2
+area_singapore                716 km^2
+area_gazastrip                360 km^2  # (CIA World Factbook)
+area_malta                    316 km^2  # smallest EU country
+area_liechtenstein            160 km^2
+area_monaco                     2.02 km^2
+area_vaticancity                0.44 km^2
+
+# Members as of 1 Feb 2020
+area_europeanunion        area_austria + area_belgium + area_bulgaria \
+                        + area_croatia + area_cyprus + area_czechia + area_denmark \
+                        + area_estonia + area_finland + area_france + area_germany \
+                        + area_greece + area_hungary + area_ireland + area_italy \
+                        + area_latvia + area_lithuania + area_luxembourg \
+                        + area_malta + area_netherlands + area_poland \
+                        + area_portugal + area_romania + area_slovakia \
+                        + area_slovenia + area_spain + area_sweden
+area_eu                   area_europeanunion
+
+#
+# Areas of the individual US states
+#
+# https://en.wikipedia.org/wiki/List_of_U.S._states_and_territories_by_area
+#
+# United States Summary: 2010, Population and Housing Unit Counts, Table 18, p. 41
+# Issued September 2012
+
+area_alaska               1723336.8 km^2
+area_texas                 695661.6 km^2
+area_california            423967.4 km^2
+area_montana               380831.1 km^2
+area_newmexico             314917.4 km^2
+area_arizona               295233.5 km^2
+area_nevada                286379.7 km^2
+area_colorado              269601.4 km^2
+area_oregon                254799.2 km^2
+area_wyoming               253334.5 km^2
+area_michigan              250486.8 km^2
+area_minnesota             225162.8 km^2
+area_utah                  219881.9 km^2
+area_idaho                 216442.6 km^2
+area_kansas                213100.0 km^2
+area_nebraska              200329.9 km^2
+area_southdakota           199728.7 km^2
+area_washington            184660.8 km^2
+area_northdakota           183107.8 km^2
+area_oklahoma              181037.2 km^2
+area_missouri              180540.3 km^2
+area_florida               170311.7 km^2
+area_wisconsin             169634.8 km^2
+area_georgia_us            153910.4 km^2
+area_illinois              149995.4 km^2
+area_iowa                  145745.9 km^2
+area_newyork               141296.7 km^2
+area_northcarolina         139391.0 km^2
+area_arkansas              137731.8 km^2
+area_alabama               135767.4 km^2
+area_louisiana             135658.7 km^2
+area_mississippi           125437.7 km^2
+area_pennsylvania          119280.2 km^2
+area_ohio                  116097.7 km^2
+area_virginia              110786.6 km^2
+area_tennessee             109153.1 km^2
+area_kentucky              104655.7 km^2
+area_indiana                94326.2 km^2
+area_maine                  91633.1 km^2
+area_southcarolina          82932.7 km^2
+area_westvirginia           62755.5 km^2
+area_maryland               32131.2 km^2
+area_hawaii                 28313.0 km^2
+area_massachusetts          27335.7 km^2
+area_vermont                24906.3 km^2
+area_newhampshire           24214.2 km^2
+area_newjersey              22591.4 km^2
+area_connecticut            14357.4 km^2
+area_delaware                6445.8 km^2
+area_rhodeisland             4001.2 km^2
+area_districtofcolumbia       177.0 km^2
+
+area_unitedstates          area_alabama + area_alaska + area_arizona \
+                         + area_arkansas + area_california + area_colorado \
+                         + area_connecticut + area_delaware \
+                         + area_districtofcolumbia + area_florida \
+                         + area_georgia_us + area_hawaii + area_idaho \
+                         + area_illinois + area_indiana + area_iowa \
+                         + area_kansas + area_kentucky + area_louisiana \
+                         + area_maine + area_maryland + area_massachusetts \
+                         + area_michigan + area_minnesota + area_mississippi \
+                         + area_missouri + area_montana + area_nebraska \
+                         + area_nevada + area_newhampshire + area_newjersey \
+                         + area_newmexico + area_newyork + area_northcarolina \
+                         + area_northdakota + area_ohio + area_oklahoma \
+                         + area_oregon + area_pennsylvania + area_rhodeisland \
+                         + area_southcarolina + area_southdakota \
+                         + area_tennessee + area_texas + area_utah \
+                         + area_vermont + area_virginia + area_washington \
+                         + area_westvirginia + area_wisconsin + area_wyoming
+
+# Total area of Canadian province and territories
+#
+# Statistics Canada, "Land and freshwater area, by province and territory",
+# 2016-10-07:
+#
+# https://www150.statcan.gc.ca/n1/pub/11-402-x/2012000/chap/geo/tbl/tbl06-eng.htm
+
+area_ontario                    1076395 km^2    # confederated 1867-Jul-01
+area_quebec                     1542056 km^2    # confederated 1867-Jul-01
+area_novascotia                 55284 km^2      # confederated 1867-Jul-01
+area_newbrunswick               72908 km^2      # confederated 1867-Jul-01
+area_canada_original            area_ontario + area_quebec + area_novascotia \
+                                             + area_newbrunswick
+area_manitoba                   647797 km^2     # confederated 1870-Jul-15
+area_britishcolumbia            944735 km^2     # confederated 1871-Jul-20
+area_princeedwardisland         5660 km^2       # confederated 1873-Jul-01
+area_canada_additional          area_manitoba + area_britishcolumbia \
+                                              + area_princeedwardisland
+area_alberta                    661848 km^2     # confederated 1905-Sep-01
+area_saskatchewan               651036 km^2     # confederated 1905-Sep-01
+area_newfoundlandandlabrador    405212 km^2     # confederated 1949-Mar-31
+area_canada_recent              area_alberta + area_saskatchewan \
+                                             + area_newfoundlandandlabrador
+area_canada_provinces           area_canada_original + area_canada_additional \
+                                                     + area_canada_recent
+area_northwestterritories       1346106 km^2    # NT confederated 1870-Jul-15
+area_yukon                      482443 km^2     # YT confederated 1898-Jun-13
+area_nunavut                    2093190 km^2    # NU confederated 1999-Apr-01
+area_canada_territories         area_northwestterritories + area_yukon \
+                                              + area_nunavut
+area_canada                     area_canada_provinces + area_canada_territories
+
+# area-uk-countries.units - UK country (/province) total areas
+# https://en.wikipedia.org/wiki/Countries_of_the_United_Kingdom#Statistics
+# GB is official UK country code for some purposes but internally is a Kingdom
+#
+# areas from A Beginners Guide to UK Geography 2019 v1.0, Office for National Statistics
+# England: country; 0927-Jul-12 united; 1603-Mar-24 union of crowns
+area_england            132947.76 km^2
+#
+# Wales: 1282 conquered; 1535 union; principality until 2011
+area_wales              21224.48 km^2
+#
+# England and Wales: nation; 1535 union
+area_englandwales       area_england + area_wales
+#
+# Scotland: country; ~900 united; 1603-Mar-24 union of crowns
+area_scotland           80226.36 km^2
+#
+# Great Britain: kingdom; excludes NI;
+# 1707 Treaty and Acts of Union: union of parliaments
+area_greatbritain       area_england + area_wales + area_scotland
+area_gb                 area_greatbritain
+#
+# Northern Ireland: province; Ireland: 1177 Henry II lordship;
+# 1542 Henry VIII kingdom; 1652 Cromwell commonwealth;
+# 1691 William III kingdom; 1800 Acts of Union: UK of GB & Ireland;
+# 1921 Irish Free State independent of UK
+area_northernireland    14133.38 km^2
+#
+# United Kingdom of GB & NI: 1800 Acts of Union: UK of GB & Ireland;
+# 1921 Irish Free State independent of UK
+area_unitedkingdom      area_greatbritain + area_northernireland
+area_uk                 area_unitedkingdom
+
+#
+# Units derived from imperial system
+#
+
+ouncedal                oz ft / s^2     # force which accelerates an ounce
+                                        #    at 1 ft/s^2
+poundal                 lb ft / s^2     # same thing for a pound
+tondal                  longton ft / s^2    # and for a ton
+pdl                     poundal
+osi                     ounce force / inch^2   # used in aviation
+psi                     pound force / inch^2
+psia                    psi             # absolute pressure
+                                        #   Note that gauge pressure can be given
+                                        #   using the gaugepressure() and
+                                        #   psig() nonlinear unit definitions
+tsi                     ton force / inch^2
+reyn                    psi sec
+slug                    lbf s^2 / ft
+slugf                   slug force
+slinch                  lbf s^2 / inch  # Mass unit derived from inch second
+slinchf                 slinch force    #   pound-force system.  Used in space
+                                        #   applications where in/sec^2 was a
+                                        #   natural acceleration measure.
+geepound                slug
+lbf                     lb force
+tonf                    ton force
+lbm                     lb
+kip                     1000 lbf     # from kilopound
+ksi                     kip / in^2
+mil                     0.001 inch
+thou                    0.001 inch
+tenth                   0.0001 inch  # one tenth of one thousandth of an inch
+millionth               1e-6 inch    # one millionth of an inch
+circularinch            1|4 pi in^2  # area of a one-inch diameter circle
+circleinch              circularinch #    A circle with diameter d inches has
+                                     #    an area of d^2 circularinches
+cylinderinch         circleinch inch # Cylinder h inch tall, d inches diameter
+                                     #    has volume d^2 h cylinder inches
+circularmil             1|4 pi mil^2 # area of one-mil diameter circle
+cmil                    circularmil
+cental                  100 pound
+centner                 cental
+
+# Shotgun gauge measures the inside diameter of the barrel by counting
+# the number of spherical lead balls you can make to fit that barrel
+# using a pound of lead.  Equivalently, this means that an n gauge gun
+# has a bore diameter that fits a ball of lead that weighs 1|n pounds
+ 
+shotgungauge(ga)        units=[1;m] domain=(0,] range=(0,] \
+                        2 ~spherevol(1 pound / ga leaddensity) ; \
+                        1 pound / leaddensity spherevol(shotgungauge/2)
+shotgunga()             shotgungauge
+caliber                 0.01 inch    # for measuring bullets
+
+duty                    ft lbf
+celo                    ft / s^2
+jerk                    ft / s^3
+australiapoint          0.01 inch    # The "point" is used to measure rainfall
+                                     #   in Australia
+sabin                   ft^2         # Measure of sound absorption equal to the
+                                     #   absorbing power of one square foot of
+                                     #   a perfectly absorbing material.  The
+                                     #   sound absorptivity of an object is the
+                                     #   area times a dimensionless
+                                     #   absorptivity coefficient.
+standardgauge          4 ft + 8.5 in # Standard width between railroad track
+flag                   5 ft^2        # Construction term referring to sidewalk.
+rollwallpaper          30 ft^2       # Area of roll of wall paper
+fillpower              in^3 / ounce  # Density of down at standard pressure.
+                                     #   The best down has 750-800 fillpower.
+pinlength              1|16 inch     # A #17 pin is 17/16 in long in the USA.
+buttonline             1|40 inch     # The line was used in 19th century USA
+                                     #   to measure width of buttons.
+beespace               1|4 inch      # Bees will fill any space that is smaller
+                                     #   than the bee space and leave open
+                                     #   spaces that are larger.  The size of
+                                     #   the space varies with species.
+diamond                8|5 ft        # Marking on US tape measures that is
+                                     #   useful to carpenters who wish to place
+                                     #   five studs in an 8 ft distance.  Note
+                                     #   that the numbers appear in red every
+                                     #   16 inches as well, giving six
+                                     #   divisions in 8 feet.
+retmaunit              1.75 in       # Height of rack mountable equipment.
+U                      retmaunit     #   Equipment should be 1|32 inch narrower
+RU                     U             #   than its U measurement indicates to
+                                     #   allow for clearance, so 4U=(6+31|32)in
+                                     #   RETMA stands for the former name of
+                                     #   the standardizing organization, Radio
+                                     #   Electronics Television Manufacturers
+                                     #   Association.  This organization is now
+                                     #   called the Electronic Industries
+                                     #   Alliance (EIA) and the rack standard
+                                     #   is specified in EIA RS-310-D.
+count                  per pound     # For measuring the size of shrimp
+flightlevel            100 ft        # Flight levels are used to ensure safe
+FL                     flightlevel   #   vertical separation between aircraft
+                                     #   despite variations in local air
+                                     #   pressure.  Flight levels define
+                                     #   altitudes based on a standard air
+                                     #   pressure so that altimeter calibration
+                                     #   is not needed.  This means that
+                                     #   aircraft at separated flight levels
+                                     #   are guaranteed to be separated.
+                                     #   Hence the definition of 100 feet is
+                                     #   a nominal, not true, measure.
+                                     #   Customarily written with no space in
+                                     #   the form FL290, which will not work in
+                                     #   units.  But note "FL 290" will work.  
+
+#
+# Other units of work, energy, power, etc
+#
+
+# Calorie: approximate energy to raise a gram of water one degree celsius
+
+calorie                 cal_th       # Default is the thermochemical calorie
+cal                     calorie
+calorie_th              4.184 J      # Thermochemical calorie, defined in 1930
+thermcalorie            calorie_th   #   by Frederick Rossini as 4.1833 J to 
+cal_th                  calorie_th   #   avoid difficulties associated with the 
+                                     #   uncertainty in the heat capacity of 
+                                     #   water.  In 1948 the value of the joule 
+                                     #   was changed, so the thermochemical
+                                     #   calorie was redefined to 4.184 J.
+                                     #   This kept the energy measured by this
+                                     #   unit the same. 
+calorie_IT              4.1868 J     # International (Steam) Table calorie,
+cal_IT                  calorie_IT   #   defined in 1929 as watt-hour/860 or
+                                     #   equivalently 180|43 joules.  At this
+                                     #   time the international joule had a
+                                     #   different value than the modern joule,
+                                     #   and the values were different in the
+                                     #   USA and in Europe.  In 1956 at the
+                                     #   Fifth International Conference on
+                                     #   Properties of Steam the exact
+                                     #   definition given here was adopted. 
+calorie_15              4.18580 J    # Energy to go from 14.5 to 15.5 degC
+cal_15                  calorie_15
+calorie_fifteen         cal_15
+calorie_20              4.18190 J    # Energy to go from 19.5 to 20.5 degC
+cal_20                  calorie_20
+calorie_twenty          calorie_20
+calorie_4               4.204 J      # Energy to go from 3.5 to 4.5 degC
+cal_4                   calorie_4
+calorie_four            calorie_4
+cal_mean                4.19002 J    # 1|100 energy to go from 0 to 100 degC
+Calorie                 kilocalorie  # the food Calorie
+thermie              1e6 cal_15      # Heat required to raise the
+                                     # temperature of a tonne of
+                                     # water from 14.5 to 15.5 degC.
+
+# btu definitions: energy to raise a pound of water 1 degF
+
+btu                     btu_IT       # International Table BTU is the default
+britishthermalunit      btu
+btu_IT                  cal_IT lb degF / gram K
+btu_th                  cal_th lb degF / gram K
+btu_mean                cal_mean lb degF / gram K
+btu_15                  cal_15 lb degF / gram K
+btu_ISO                 1055.06 J    # Exact, rounded ISO definition based
+                                     #    on the IT calorie
+quad                    quadrillion btu
+
+ECtherm                 1e5 btu_ISO    # Exact definition
+UStherm                 1.054804e8 J   # Exact definition
+therm                   UStherm
+
+# Water latent heat from [23]
+
+water_fusion_heat       6.01 kJ/mol / (18.015 g/mol) # At 0 deg C
+water_vaporization_heat 2256.4 J/g  # At saturation, 100 deg C, 101.42 kPa
+
+# Specific heat capacities of various substances
+#
+# SPECFIC_HEAT          ENERGY / MASS / TEMPERATURE_DIFFERENCE
+# SPECFIC_HEAT_CAPACITY ENERGY / MASS / TEMPERATURE_DIFFERENCE
+
+specificheat_water      calorie / g K
+water_specificheat      specificheat_water
+     # Values from www.engineeringtoolbox.com/specific-heat-metals-d_152.html
+specificheat_aluminum   0.91 J/g K
+specificheat_antimony   0.21 J/g K
+specificheat_barium     0.20 J/g K
+specificheat_beryllium  1.83 J/g K
+specificheat_bismuth    0.13 J/g K
+specificheat_cadmium    0.23 J/g K
+specificheat_cesium     0.24 J/g K
+specificheat_chromium   0.46 J/g K
+specificheat_cobalt     0.42 J/g K
+specificheat_copper     0.39 J/g K
+specificheat_gallium    0.37 J/g K
+specificheat_germanium  0.32 J/g K
+specificheat_gold       0.13 J/g K
+specificheat_hafnium    0.14 J/g K
+specificheat_indium     0.24 J/g K
+specificheat_iridium    0.13 J/g K
+specificheat_iron       0.45 J/g K
+specificheat_lanthanum  0.195 J/g K
+specificheat_lead       0.13 J/g K
+specificheat_lithium    3.57 J/g K
+specificheat_lutetium   0.15 J/g K
+specificheat_magnesium  1.05 J/g K
+specificheat_manganese  0.48 J/g K
+specificheat_mercury    0.14 J/g K
+specificheat_molybdenum 0.25 J/g K
+specificheat_nickel     0.44 J/g K
+specificheat_osmium     0.13 J/g K
+specificheat_palladium  0.24 J/g K
+specificheat_platinum   0.13 J/g K
+specificheat_plutonum   0.13 J/g K
+specificheat_potassium  0.75 J/g K
+specificheat_rhenium    0.14 J/g K
+specificheat_rhodium    0.24 J/g K
+specificheat_rubidium   0.36 J/g K
+specificheat_ruthenium  0.24 J/g K
+specificheat_scandium   0.57  J/g K
+specificheat_selenium   0.32 J/g K
+specificheat_silicon    0.71 J/g K
+specificheat_silver     0.23 J/g K
+specificheat_sodium     1.21 J/g K
+specificheat_strontium  0.30 J/g K
+specificheat_tantalum   0.14 J/g K
+specificheat_thallium   0.13 J/g K
+specificheat_thorium    0.13 J/g K
+specificheat_tin        0.21 J/g K
+specificheat_titanium   0.54 J/g K
+specificheat_tungsten   0.13 J/g K
+specificheat_uranium    0.12 J/g K
+specificheat_vanadium   0.39 J/g K
+specificheat_yttrium    0.30 J/g K
+specificheat_zinc       0.39 J/g K
+specificheat_zirconium  0.27 J/g K
+specificheat_ethanol    2.3  J/g K
+specificheat_ammonia    4.6 J/g K
+specificheat_freon      0.91 J/g K   # R-12 at 0 degrees Fahrenheit
+specificheat_gasoline   2.22 J/g K
+specificheat_iodine     2.15 J/g K
+specificheat_oliveoil   1.97 J/g K
+
+#  en.wikipedia.org/wiki/Heat_capacity#Table_of_specific_heat_capacities
+specificheat_hydrogen   14.3 J/g K
+specificheat_helium     5.1932 J/g K
+specificheat_argon      0.5203 J/g K
+specificheat_tissue     3.5 J/g K
+specificheat_diamond    0.5091 J/g K
+specificheat_granite    0.79 J/g K
+specificheat_graphite   0.71 J/g K
+specificheat_ice        2.11 J/g K
+specificheat_asphalt    0.92 J/g K
+specificheat_brick      0.84 J/g K
+specificheat_concrete   0.88 J/g K
+specificheat_glass_silica 0.84 J/g K
+specificheat_glass_flint  0.503 J/g K
+specificheat_glass_pyrex  0.753 J/g K
+specificheat_gypsum     1.09 J/g K
+specificheat_marble     0.88 J/g K
+specificheat_sand       0.835 J/g K
+specificheat_soil       0.835 J/g K
+specificheat_wood       1.7 J/g K
+
+specificheat_sucrose    1.244 J/g K #www.sugartech.co.za/heatcapacity/index.php
+
+
+# Energy densities of various fuels
+#
+# Most of these fuels have varying compositions or qualities and hence their
+# actual energy densities vary.  These numbers are hence only approximate.
+#
+# E1. http://www.aps.org/policy/reports/popa-reports/energy/units.cfm
+# E2. https://web.archive.org/web/20100825042309/http://www.ior.com.au/ecflist.html
+
+tonoil                  1e10 cal_IT    # Ton oil equivalent.  A conventional
+                                       # value for the energy released by
+toe                     tonoil         # burning one metric ton of oil. [18,E1]
+                                       # Note that energy per mass of petroleum
+                                       # products is fairly constant.
+                                       # Variations in volumetric energy
+                                       # density result from variations in the
+                                       # density (kg/m^3) of different fuels.
+                                       # This definition is given by the
+                                       # IEA/OECD.
+toncoal                 7e9 cal_IT     # Energy in metric ton coal from [18].
+                                       # This is a nominal value which
+                                       # is close to the heat content
+                                       # of coal used in the 1950's
+barreloil               5.8 Mbtu       # Conventional value for barrel of crude
+                                       # oil [E1].  Actual range is 5.6 - 6.3.
+naturalgas_HHV          1027 btu/ft3   # Energy content of natural gas.  HHV
+naturalgas_LHV          930 btu/ft3    # is for Higher Heating Value and
+naturalgas              naturalgas_HHV # includes energy from condensation
+                                       # combustion products.  LHV is for Lower
+                                       # Heating Value and excludes these.
+                                       # American publications typically report
+                                       # HHV whereas European ones report LHV.
+charcoal                30 GJ/tonne
+woodenergy_dry          20 GJ/tonne    # HHV, a cord weights about a tonne
+woodenergy_airdry       15 GJ/tonne    # 20% moisture content
+coal_bituminous         27 GJ / tonne
+coal_lignite            15 GJ / tonne
+coal_US                 22 GJ / uston  # Average for US coal (short ton), 1995
+ethanol_HHV         84000 btu/usgallon
+ethanol_LHV         75700 btu/usgallon
+diesel             130500 btu/usgallon
+gasoline_LHV       115000 btu/usgallon
+gasoline_HHV       125000 btu/usgallon
+gasoline                gasoline_HHV
+heating                 37.3 MJ/liter
+fueloil                 39.7 MJ/liter  # low sulphur
+propane                 93.3 MJ/m^3
+butane                  124 MJ/m^3
+
+# The US EPA defines a "miles per gallon equivalent" for alternative
+# energy vehicles:
+
+mpg_e          miles / gallon gasoline_LHV
+MPGe           mpg_e
+
+# These values give total energy from uranium fission.  Actual efficiency
+# of nuclear power plants is around 30%-40%.  Note also that some reactors
+# use enriched uranium around 3% U-235.  Uranium during processing or use
+# may be in a compound of uranium oxide or uranium hexafluoride, in which
+# case the energy density would be lower depending on how much uranium is
+# in the compound.
+
+uranium_pure     200 MeV avogadro / (235.0439299 g/mol)  # Pure U-235
+uranium_natural         0.7% uranium_pure        # Natural uranium: 0.7% U-235
+
+# Celsius heat unit: energy to raise a pound of water 1 degC
+
+celsiusheatunit         cal lb degC / gram K
+chu                     celsiusheatunit
+
+# "Apparent" average power in an AC circuit, the product of rms voltage
+# and rms current, equal to the true power in watts when voltage and
+# current are in phase.  In a DC circuit, always equal to the true power.
+
+VA                      volt ampere
+
+kWh                     kilowatt hour
+
+# The horsepower is supposedly the power of one horse pulling.   Obviously
+# different people had different horses.
+
+horsepower              550 foot pound force / sec    # Invented by James Watt
+mechanicalhorsepower    horsepower
+hp                      horsepower
+metrichorsepower        75 kilogram force meter / sec # PS=Pferdestaerke in
+electrichorsepower      746 W                         # Germany
+boilerhorsepower        9809.50 W
+waterhorsepower         746.043 W
+brhorsepower            horsepower   # Value corrected Dec, 2019.  Was 745.7 W.
+donkeypower             250 W
+chevalvapeur            metrichorsepower
+
+#
+# Heat Transfer
+#
+# Thermal conductivity, K, measures the rate of heat transfer across
+# a material.  The heat transferred is
+#     Q = K dT A t / L
+# where dT is the temperature difference across the material, A is the
+# cross sectional area, t is the time, and L is the length (thickness).
+# Thermal conductivity is a material property.
+
+THERMAL_CONDUCTIVITY    POWER / AREA (TEMPERATURE_DIFFERENCE/LENGTH)
+THERMAL_RESISTIVITY     1/THERMAL_CONDUCTIVITY
+
+# Thermal conductance is the rate at which heat flows across a given
+# object, so the area and thickness have been fixed.  It depends on
+# the size of the object and is hence not a material property.
+
+THERMAL_CONDUCTANCE     POWER / TEMPERATURE_DIFFERENCE
+THERMAL_RESISTANCE      1/THERMAL_CONDUCTANCE
+
+# Thermal admittance is the rate of heat flow per area across an
+# object whose thickness has been fixed.  Its reciprocal, thermal
+# insulation, is used to for measuring the heat transfer per area
+# of sheets of insulation or cloth that are of specified thickness.
+
+THERMAL_ADMITTANCE      THERMAL_CONDUCTIVITY / LENGTH
+THERMAL_INSULANCE       THERMAL_RESISTIVITY LENGTH
+THERMAL_INSULATION      THERMAL_RESISTIVITY LENGTH
+
+Rvalue                  degF ft^2 hr / btu
+Uvalue                  1/Rvalue
+europeanUvalue          watt / m^2 K
+RSI                     degC m^2 / W
+clo                     0.155 degC m^2 / W # Supposed to be the insulance
+                                           # required to keep a resting person
+                                           # comfortable indoors.  The value
+                                           # given is from NIST and the CRC,
+                                           # but [5] gives a slightly different
+                                           # value of 0.875 ft^2 degF hr / btu.
+tog                     0.1 degC m^2 / W   # Also used for clothing.
+
+
+# Thermal Conductivity of a few materials
+
+diamond_natural_thermal_conductivity    2200 W / m K
+diamond_synthetic_thermal_conductivity  3320 W / m K  # 99% pure C12
+silver_thermal_conductivity             406 W / m K
+aluminum_thermal_conductivity           205 W / m K
+copper_thermal_conductivity             385 W / m K
+gold_thermal_conductivity               314 W / m K
+iron_thermal_conductivity               79.5 W / m K
+stainless_304_thermal_conductivity      15.5 W / m K  # average value
+
+# The bel was defined by engineers of Bell Laboratories to describe the
+# reduction in audio level over a length of one mile. It was originally
+# called the transmission unit (TU) but was renamed around 1923 to honor
+# Alexander Graham Bell. The bel proved inconveniently large so the decibel
+# has become more common.  The decibel is dimensionless since it reports a
+# ratio, but it is used in various contexts to report a signal's power
+# relative to some reference level.
+
+bel(x)     units=[1;1] range=(0,) 10^(x);    log(bel)    # Basic bel definition
+decibel(x) units=[1;1] range=(0,) 10^(x/10); 10 log(decibel) # Basic decibel
+dB()       decibel                                           # Abbreviation
+dBW(x)     units=[1;W] range=(0,) dB(x) W ;  ~dB(dBW/W)      # Reference = 1 W
+dBk(x)     units=[1;W] range=(0,) dB(x) kW ; ~dB(dBk/kW)     # Reference = 1 kW
+dBf(x)     units=[1;W] range=(0,) dB(x) fW ; ~dB(dBf/fW)     # Reference = 1 fW
+dBm(x)     units=[1;W] range=(0,) dB(x) mW ; ~dB(dBm/mW)     # Reference = 1 mW
+dBmW(x)    units=[1;W] range=(0,) dBm(x) ;   ~dBm(dBmW)      # Reference = 1 mW
+dBJ(x)     units=[1;J] range=(0,) dB(x) J; ~dB(dBJ/J)        # Energy relative
+                                     # to 1 joule.  Used for power spectral
+                                     # density since W/Hz = J
+
+
+# When used to measure amplitude, voltage, or current the signal is squared
+# because power is proportional to the square of these measures.  The root
+# mean square (RMS) voltage is typically used with these units.
+
+dB_amplitude(x) units=[1;1]  dB(0.5 x) ; ~dB(dB_amplitude^2) 
+dBV(x)  units=[1;V] range=(0,) dB(0.5 x) V;~dB(dBV^2 / V^2) # Reference = 1 V
+dBmV(x) units=[1;V] range=(0,) dB(0.5 x) mV;~dB(dBmV^2/mV^2)# Reference = 1 mV
+dBuV(x) units=[1;V] range=(0,) dB(0.5 x) microV ; ~dB(dBuV^2 / microV^2)
+                                   # Reference = 1 microvolt
+
+# Here are dB measurements for current.  Be aware that dbA is also 
+# a unit for frequency weighted sound pressure.  
+dBA(x)  units=[1;A] range=(0,) dB(0.5 x) A;~dB(dBA^2 / A^2) # Reference = 1 A
+dBmA(x) units=[1;A] range=(0,) dB(0.5 x) mA;~dB(dBmA^2/mA^2)# Reference = 1 mA
+dBuA(x) units=[1;A] range=(0,) dB(0.5 x) microA ; ~dB(dBuA^2 / microA^2)
+                                                    # Reference = 1 microamp
+
+# Referenced to the voltage that causes 1 mW dissipation in a 600 ohm load.
+# Originally defined as dBv but changed to prevent confusion with dBV.
+# The "u" is for unloaded.
+dBu(x) units=[1;V] range=(0,) dB(0.5 x) sqrt(mW 600 ohm) ; \
+                              ~dB(dBu^2 / mW 600 ohm)
+dBv(x) units=[1;V] range=(0,) dBu(x) ; ~dBu(dBv)  # Synonym for dBu
+
+# Measurements for sound in air, referenced to the threshold of human hearing
+# Note that sound in other media typically uses 1 micropascal as a reference
+# for sound pressure.  Units dBA, dBB, dBC, refer to different frequency
+# weightings meant to approximate the human ear's response.
+
+# sound pressure level
+dBSPL(x) units=[1;Pa] range=(0,) dB(0.5 x) 20 microPa ;  \
+                                 ~dB(dBSPL^2 / (20 microPa)^2)
+# sound intensity level
+dBSIL(x) units=[1;W/m^2] range=(0,) dB(x) 1e-12 W/m^2; \
+                                    ~dB(dBSIL / (1e-12 W/m^2))
+# sound power level (The W in SWL is for the reference power, 1 W.)
+dBSWL(x) units=[1;W] range=(0,) dB(x) 1e-12 W; ~dB(dBSWL/1e-12 W)
+
+# The neper is another similar logarithmic unit.  Note that the neper
+# is defined based on the ratio of amplitudes rather than the power
+# ratio like the decibel.  This means that if the data is power, and
+# you convert to nepers you should take the square root of the data
+# to convert to amplitude.  If you want to convert nepers to a power
+# measurement you need to square the resulting output.  
+
+neper(x)       units=[1;1]  range=(0,)  exp(x);      ln(neper)
+centineper(x)  units=[1;1]  range=(0,)  exp(x/100);  100 ln(centineper)
+Np()           neper
+cNp()          centineper
+Np_power(x)    units=[1;1]  Np(2 x) ; ~Np(Np_power)/2
+
+# Misc other measures
+
+ENTROPY                 ENERGY / TEMPERATURE
+clausius                1e3 cal/K       # A unit of physical entropy
+langley                 thermcalorie/cm^2    # Used in radiation theory
+poncelet                100 kg force m / s
+tonrefrigeration        uston 144 btu / lb day # One ton refrigeration is
+                                        # the rate of heat extraction required
+                                        # turn one ton of water to ice in
+                                        # a day.  Ice is defined to have a
+                                        # latent heat of 144 btu/lb.
+tonref                  tonrefrigeration
+refrigeration           tonref / ton
+frigorie                1000 cal_15     # Used in refrigeration engineering.
+airwatt                 8.5 (ft^3/min) inH2O # Measure of vacuum power as
+                                             # pressure times air flow.
+
+# The unit "tnt" is defined so that you can write "tons tnt".  The
+# question of which ton, exactly, is intended.  The answer is that
+# nobody knows: 
+#
+# Quoting the footnote from page 13 of
+# The Effects of Nuclear Weapons, 3rd ed.
+# https://www.fourmilab.ch/etexts/www/effects/eonw_1.pdf
+# 
+#     The majority of the experimental and theoretical values of the
+#     explosive energy released by TNT range from 900 to 1,100 calories per
+#     gram. At one time, there was some uncertainty as to whether the term
+#     "kiloton" of TNT referred to a short kiloton (2*10^6 pounds), a metric
+#     kiloton (2.205*10^6 pounds), or a long kiloton (2.24*10^6 pounds). In
+#     order to avoid ambiguity, it was agreed that the term "kiloton" would
+#     refer to the release of 10^12 calories of explosive energy. This is
+#     equivalent to 1 short kiloton of TNT if the energy release is 1,102
+#     calories per gram or to 1 long kiloton if the energy is 984 calories
+#     per gram of TNT.
+#
+# It is therefore not well-defined how much energy a "gram of tnt" is,
+# though this term does appear in some references.
+
+tnt                     1e9 cal_th / ton   # Defined exact value
+
+# Nuclear weapon yields 
+
+davycrocket             10 ton tnt         # lightest US tactical nuclear weapon
+hiroshima               15.5 kiloton tnt   # Uranium-235 fission bomb
+nagasaki                21 kiloton tnt     # Plutonium-239 fission bomb
+fatman                  nagasaki
+littleboy               hiroshima
+ivyking                 500 kiloton tnt    # most powerful fission bomb
+castlebravo             15 megaton tnt     # most powerful US test
+tsarbomba               50 megaton tnt     # most powerful test ever: USSR,
+                                           # 30 October 1961
+b53bomb                 9 megaton tnt
+                 # http://rarehistoricalphotos.com/gadget-first-atomic-bomb/
+trinity                 18 kiloton tnt     # July 16, 1945
+gadget                  trinity
+
+#
+# Permeability: The permeability or permeance, n, of a substance determines
+# how fast vapor flows through the substance.  The formula W = n A dP
+# holds where W is the rate of flow (in mass/time), n is the permeability,
+# A is the area of the flow path, and dP is the vapor pressure difference.
+#
+
+perm_0C                 grain / hr ft^2 inHg
+perm_zero               perm_0C
+perm_0                  perm_0C
+perm                    perm_0C
+perm_23C                grain / hr ft^2 in Hg23C
+perm_twentythree        perm_23C
+
+#
+# Counting measures
+#
+
+pair                    2
+brace                   2
+nest                    3     # often used for items like bowls that
+                              #   nest together
+hattrick                3     # Used in sports, especially cricket and ice
+                              #   hockey to report the number of goals.
+dicker                  10
+dozen                   12
+bakersdozen             13
+score                   20
+flock                   40
+timer                   40
+shock                   60
+toncount                100   # Used in sports in the UK
+longhundred             120   # From a germanic counting system
+gross                   144
+greatgross              12 gross
+tithe                   1|10  # From Anglo-Saxon word for tenth
+
+# Paper counting measure
+
+shortquire              24
+quire                   25
+shortream               480
+ream                    500
+perfectream             516
+bundle                  2 reams
+bale                    5 bundles
+
+#
+# Paper measures
+#
+
+# USA paper sizes
+
+lettersize              8.5 inch 11 inch
+legalsize               8.5 inch 14 inch
+ledgersize              11 inch 17 inch
+executivesize           7.25 inch 10.5 inch
+Apaper                  8.5 inch 11 inch
+Bpaper                  11 inch 17 inch
+Cpaper                  17 inch 22 inch
+Dpaper                  22 inch 34 inch
+Epaper                  34 inch 44 inch
+
+# Correspondence envelope sizes.  #10 is the standard business
+# envelope in the USA. 
+
+envelope6_25size        3.5 inch 6 inch
+envelope6_75size        3.625 inch 6.5 inch
+envelope7size           3.75 inch 6.75 inch
+envelope7_75size        3.875 inch 7.5 inch
+envelope8_625size       3.625 inch 8.625 inch
+envelope9size           3.875 inch 8.875 inch
+envelope10size          4.125 inch 9.5 inch
+envelope11size          4.5 inch 10.375 inch
+envelope12size          4.75 inch 11 inch
+envelope14size          5 inch 11.5 inch
+envelope16size          6 inch 12 inch
+
+# Announcement envelope sizes (no relation to metric paper sizes like A4)
+
+envelopeA1size          3.625 inch 5.125 inch  # same as 4bar
+envelopeA2size          4.375 inch 5.75 inch
+envelopeA6size          4.75 inch 6.5 inch
+envelopeA7size          5.25 inch 7.25 inch
+envelopeA8size          5.5 inch 8.125 inch   
+envelopeA9size          5.75 inch 8.75 inch
+envelopeA10size         6 inch 9.5 inch
+
+# Baronial envelopes
+
+envelope4bar            3.625 inch 5.125 inch  # same as A1
+envelope5_5bar          4.375 inch 5.75 inch
+envelope6bar            4.75 inch 6.5 inch
+
+# Coin envelopes
+
+envelope1baby           2.25 inch 3.5 inch     # same as #1 coin
+envelope00coin          1.6875 inch 2.75 inch
+envelope1coin           2.25 inch 3.5 inch
+envelope3coin           2.5 inch 4.25 inch
+envelope4coin           3 inch 4.5 inch
+envelope4_5coin         3 inch 4.875 inch
+envelope5coin           2.875 inch 5.25 inch
+envelope5_5coin         3.125 inch 5.5 inch
+envelope6coin           3.375 inch 6 inch
+envelope7coin           3.5 inch 6.5 inch
+
+# The metric paper sizes are defined so that if a sheet is cut in half
+# along the short direction, the result is two sheets which are
+# similar to the original sheet.  This means that for any metric size,
+# the long side is close to sqrt(2) times the length of the short
+# side.  Each series of sizes is generated by repeated cuts in half,
+# with the values rounded down to the nearest millimeter.
+
+A0paper                 841 mm 1189 mm   # The basic size in the A series
+A1paper                 594 mm  841 mm   # is defined to have an area of
+A2paper                 420 mm  594 mm   # one square meter.
+A3paper                 297 mm  420 mm
+A4paper                 210 mm  297 mm
+A5paper                 148 mm  210 mm
+A6paper                 105 mm  148 mm
+A7paper                  74 mm  105 mm
+A8paper                  52 mm   74 mm
+A9paper                  37 mm   52 mm
+A10paper                 26 mm   37 mm
+
+B0paper                1000 mm 1414 mm   # The basic B size has an area
+B1paper                 707 mm 1000 mm   # of sqrt(2) square meters.
+B2paper                 500 mm  707 mm
+B3paper                 353 mm  500 mm
+B4paper                 250 mm  353 mm
+B5paper                 176 mm  250 mm
+B6paper                 125 mm  176 mm
+B7paper                  88 mm  125 mm
+B8paper                  62 mm   88 mm
+B9paper                  44 mm   62 mm
+B10paper                 31 mm   44 mm
+
+C0paper                 917 mm 1297 mm   # The basic C size has an area
+C1paper                 648 mm  917 mm   # of sqrt(sqrt(2)) square meters.
+C2paper                 458 mm  648 mm
+C3paper                 324 mm  458 mm   # Intended for envelope sizes
+C4paper                 229 mm  324 mm
+C5paper                 162 mm  229 mm
+C6paper                 114 mm  162 mm
+C7paper                  81 mm  114 mm
+C8paper                  57 mm   81 mm
+C9paper                  40 mm   57 mm
+C10paper                 28 mm   40 mm
+
+# gsm (Grams per Square Meter), a sane, metric paper weight measure
+
+gsm                     grams / meter^2
+
+# In the USA, a collection of crazy historical paper measures are used.  Paper
+# is measured as a weight of a ream of that particular type of paper.  This is
+# sometimes called the "substance" or "basis" (as in "substance 20" paper).
+# The standard sheet size or "basis size" varies depending on the type of
+# paper.  As a result, 20 pound bond paper and 50 pound text paper are actually
+# about the same weight.  The different sheet sizes were historically the most
+# convenient for printing or folding in the different applications.  These
+# different basis weights are standards maintained by American Society for
+# Testing Materials (ASTM) and the American Forest and Paper Association
+# (AF&PA).
+
+poundbookpaper          lb / 25 inch 38 inch ream
+lbbook                  poundbookpaper
+poundtextpaper          poundbookpaper
+lbtext                  poundtextpaper
+poundoffsetpaper        poundbookpaper    # For offset printing
+lboffset                poundoffsetpaper
+poundbiblepaper         poundbookpaper    # Designed to be lightweight, thin,
+lbbible                 poundbiblepaper   # strong and opaque.
+poundtagpaper           lb / 24 inch 36 inch ream
+lbtag                   poundtagpaper
+poundbagpaper           poundtagpaper
+lbbag                   poundbagpaper
+poundnewsprintpaper     poundtagpaper
+lbnewsprint             poundnewsprintpaper
+poundposterpaper        poundtagpaper
+lbposter                poundposterpaper
+poundtissuepaper        poundtagpaper
+lbtissue                poundtissuepaper
+poundwrappingpaper      poundtagpaper
+lbwrapping              poundwrappingpaper
+poundwaxingpaper        poundtagpaper
+lbwaxing                poundwaxingpaper
+poundglassinepaper      poundtagpaper
+lbglassine              poundglassinepaper
+poundcoverpaper         lb / 20 inch 26 inch ream
+lbcover                 poundcoverpaper
+poundindexpaper         lb / 25.5 inch 30.5 inch ream
+lbindex                 poundindexpaper
+poundindexbristolpaper  poundindexpaper
+lbindexbristol          poundindexpaper
+poundbondpaper          lb / 17 inch 22 inch ream  # Bond paper is stiff and
+lbbond                  poundbondpaper             # durable for repeated
+poundwritingpaper       poundbondpaper             # filing, and it resists
+lbwriting               poundwritingpaper          # ink penetration.
+poundledgerpaper        poundbondpaper
+lbledger                poundledgerpaper
+poundcopypaper          poundbondpaper
+lbcopy                  poundcopypaper
+poundblottingpaper      lb / 19 inch 24 inch ream
+lbblotting              poundblottingpaper
+poundblankspaper        lb / 22 inch 28 inch ream
+lbblanks                poundblankspaper
+poundpostcardpaper      lb / 22.5 inch 28.5 inch ream
+lbpostcard              poundpostcardpaper
+poundweddingbristol     poundpostcardpaper
+lbweddingbristol        poundweddingbristol
+poundbristolpaper       poundweddingbristol
+lbbristol               poundbristolpaper
+poundboxboard           lb / 1000 ft^2
+lbboxboard              poundboxboard
+poundpaperboard         poundboxboard
+lbpaperboard            poundpaperboard
+
+# When paper is marked in units of M, it means the weight of 1000 sheets of the
+# given size of paper.  To convert this to paper weight, divide by the size of
+# the paper in question.
+
+paperM                  lb / 1000
+
+# In addition paper weight is reported in "caliper" which is simply the
+# thickness of one sheet, typically in inches.  Thickness is also reported in
+# "points" where a point is 1|1000 inch.  These conversions are supplied to
+# convert these units roughly (using an approximate density) into the standard
+# paper weight values.
+
+pointthickness          0.001 in
+paperdensity            0.8 g/cm^3        # approximate--paper densities vary!
+papercaliper            in paperdensity
+paperpoint              pointthickness paperdensity
+
+#
+# Printing
+#
+
+fournierpoint           0.1648 inch / 12  # First definition of the printers
+                                          # point made by Pierre Fournier who
+                                          # defined it in 1737 as 1|12 of a
+                                          # cicero which was 0.1648 inches.
+olddidotpoint           1|72 frenchinch   # Francois Ambroise Didot, one of
+                                          # a family of printers, changed
+                                          # Fournier's definition around 1770
+                                          # to fit to the French units then in
+                                          # use.
+bertholdpoint           1|2660 m          # H. Berthold tried to create a
+                                          # metric version of the didot point
+                                          # in 1878.
+INpoint                 0.4 mm            # This point was created by a
+                                          # group directed by Fermin Didot in
+                                          # 1881 and is associated with the
+                                          # imprimerie nationale.  It doesn't
+                                          # seem to have been used much.
+germandidotpoint        0.376065 mm       # Exact definition appears in DIN
+                                          # 16507, a German standards document
+                                          # of 1954.  Adopted more broadly  in
+                                          # 1966 by ???
+metricpoint             3|8 mm            # Proposed in 1977 by Eurograf
+oldpoint                1|72.27 inch      # The American point was invented
+printerspoint           oldpoint          # by Nelson Hawks in 1879 and
+texpoint                oldpoint          # dominates USA publishing.
+                                          # It was standardized by the American
+                                          # Typefounders Association at the
+                                          # value of 0.013837 inches exactly.
+                                          # Knuth uses the approximation given
+                                          # here (which is very close).  The
+                                          # comp.fonts FAQ claims that this
+                                          # value is supposed to be 1|12 of a
+                                          # pica where 83 picas is equal to 35
+                                          # cm.  But this value differs from
+                                          # the standard.
+texscaledpoint          1|65536 texpoint  # The TeX typesetting system uses
+texsp                   texscaledpoint    # this for all computations.
+computerpoint           1|72 inch         # The American point was rounded
+point                   computerpoint
+computerpica            12 computerpoint  # to an even 1|72 inch by computer
+postscriptpoint         computerpoint     # people at some point.
+pspoint                 postscriptpoint
+twip                    1|20 point        # TWentieth of an Imperial Point
+Q                       1|4 mm            # Used in Japanese phototypesetting
+                                          # Q is for quarter
+frenchprinterspoint     olddidotpoint
+didotpoint              germandidotpoint  # This seems to be the dominant value
+europeanpoint           didotpoint        # for the point used in Europe
+cicero                  12 didotpoint
+
+stick                   2 inches
+
+# Type sizes
+
+excelsior               3 oldpoint
+brilliant               3.5 oldpoint
+diamondtype             4 oldpoint
+pearl                   5 oldpoint
+agate                   5.5 oldpoint  # Originally agate type was 14 lines per
+                                      #   inch, giving a value of 1|14 in.
+ruby                    agate         # British
+nonpareil               6 oldpoint
+mignonette              6.5 oldpoint
+emerald                 mignonette    # British
+minion                  7 oldpoint
+brevier                 8 oldpoint
+bourgeois               9 oldpoint
+longprimer              10 oldpoint
+smallpica               11 oldpoint
+pica                    12 oldpoint
+english                 14 oldpoint
+columbian               16 oldpoint
+greatprimer             18 oldpoint
+paragon                 20 oldpoint
+meridian                44 oldpoint
+canon                   48 oldpoint
+
+# German type sizes
+
+nonplusultra            2 didotpoint
+brillant                3 didotpoint
+diamant                 4 didotpoint
+perl                    5 didotpoint
+nonpareille             6 didotpoint
+kolonel                 7 didotpoint
+petit                   8 didotpoint
+borgis                  9 didotpoint
+korpus                  10 didotpoint
+corpus                  korpus
+garamond                korpus
+mittel                  14 didotpoint
+tertia                  16 didotpoint
+text                    18 didotpoint
+kleine_kanon            32 didotpoint
+kanon                   36 didotpoint
+grobe_kanon             42 didotpoint
+missal                  48 didotpoint
+kleine_sabon            72 didotpoint
+grobe_sabon             84 didotpoint
+
+#
+# Information theory units.  Note that the name "entropy" is used both
+# to measure information and as a physical quantity.
+#
+
+INFORMATION             bit
+
+nat                     (1/ln(2)) bits       # Entropy measured base e
+hartley                 log2(10) bits        # Entropy of a uniformly
+ban                     hartley              #   distributed random variable
+                                             #   over 10 symbols.
+dit                     hartley              # from Decimal digIT
+
+#
+# Computer
+#
+
+bps                     bit/sec              # Sometimes the term "baud" is
+                                             #   incorrectly used to refer to
+                                             #   bits per second.  Baud refers
+                                             #   to symbols per second.  Modern
+                                             #   modems transmit several bits
+                                             #   per symbol.
+byte                    8 bit                # Not all machines had 8 bit
+B                       byte                 #   bytes, but these days most of
+                                             #   them do.  But beware: for
+                                             #   transmission over modems, a
+                                             #   few extra bits are used so
+                                             #   there are actually 10 bits per
+                                             #   byte.
+octet                   8 bits               # The octet is always 8 bits
+nybble                  4 bits               # Half of a byte. Sometimes
+                                             #   equal to different lengths
+                                             #   such as 3 bits.
+nibble                  nybble
+nyp                     2 bits               # Donald Knuth asks in an exercise
+                                             #   for a name for a 2 bit
+                                             #   quantity and gives the "nyp"
+                                             #   as a solution due to Gregor
+                                             #   Purdy.  Not in common use.
+meg                     megabyte             # Some people consider these
+                                             # units along with the kilobyte
+gig                     gigabyte             # to be defined according to
+                                             # powers of 2 with the kilobyte
+                                             # equal to 2^10 bytes, the
+                                             # megabyte equal to 2^20 bytes and
+                                             # the gigabyte equal to 2^30 bytes
+                                             # but these usages are forbidden
+                                             # by SI.  Binary prefixes have
+                                             # been defined by IEC to replace
+                                             # the SI prefixes.  Use them to
+                                             # get the binary units KiB, MiB,
+                                             # GiB, etc.
+jiffy                   0.01 sec     # This is defined in the Jargon File
+jiffies                 jiffy        # (http://www.jargon.org) as being the
+                                     # duration of a clock tick for measuring
+                                     # wall-clock time.  Supposedly the value
+                                     # used to be 1|60 sec or 1|50 sec
+                                     # depending on the frequency of AC power,
+                                     # but then 1|100 sec became more common.
+                                     # On linux systems, this term is used and
+                                     # for the Intel based chips, it does have
+                                     # the value of .01 sec.  The Jargon File
+                                     # also lists two other definitions:
+                                     # millisecond, and the time taken for
+                                     # light to travel one foot.
+cdaudiospeed      44.1 kHz 2*16 bits # CD audio data rate at 44.1 kHz with 2
+                                     # samples of sixteen bits each.
+cdromspeed       75 2048 bytes / sec # For data CDs (mode1) 75 sectors are read
+                                     # each second with 2048 bytes per sector.
+                                     # Audio CDs do not have sectors, but
+                                     # people sometimes divide the bit rate by
+                                     # 75 and claim a sector length of 2352.
+                                     # Data CDs have a lower rate due to
+                                     # increased error correction overhead.
+                                     # There is a rarely used mode (mode2) with
+                                     # 2336 bytes per sector that has fewer
+                                     # error correction bits than mode1.
+dvdspeed                 1385 kB/s   # This is the "1x" speed of a DVD using
+                                     # constant linear velocity (CLV) mode.
+                                     # Modern DVDs may vary the linear velocity
+                                     # as they go from the inside to the
+                                     # outside of the disc.
+                       # See http://www.osta.org/technology/dvdqa/dvdqa4.htm
+
+FIT         / 1e9 hour # Failures In Time, number of failures per billion hours
+
+#
+# The IP address space is divided into subnets.  The number of hosts
+# in a subnet depends on the length of the subnet prefix.  This is
+# often written as /N where N is the number of bits in the prefix.
+#
+# https://en.wikipedia.org/wiki/Subnetwork
+#
+# These definitions gives the number of hosts for a subnet whose
+# prefix has the specified length in bits.
+#
+
+ipv4subnetsize(prefix_len) units=[1;1]  domain=[0,32] range=[1,4294967296] \
+                         2^(32-prefix_len) ; 32-log2(ipv4subnetsize)
+ipv4classA               ipv4subnetsize(8)
+ipv4classB               ipv4subnetsize(16)
+ipv4classC               ipv4subnetsize(24)
+
+ipv6subnetsize(prefix_len) units=[1;1] domain=[0,128] \
+                         range=[1,340282366920938463463374607431768211456] \
+                         2^(128-prefix_len) ; 128-log2(ipv6subnetsize)
+
+#
+# Musical measures.  Musical intervals expressed as ratios.  Multiply
+# two intervals together to get the sum of the interval.  The function
+# musicalcent can be used to convert ratios to cents.
+#
+
+# Perfect intervals
+
+octave                  2
+majorsecond             musicalfifth^2 / octave
+majorthird              5|4
+minorthird              6|5
+musicalfourth           4|3
+musicalfifth            3|2
+majorsixth              musicalfourth majorthird
+minorsixth              musicalfourth minorthird
+majorseventh            musicalfifth majorthird
+minorseventh            musicalfifth minorthird
+
+pythagoreanthird        majorsecond musicalfifth^2 / octave
+syntoniccomma           pythagoreanthird / majorthird
+pythagoreancomma        musicalfifth^12 / octave^7
+
+# Equal tempered definitions
+
+semitone                octave^(1|12)
+musicalcent(x) units=[1;1] range=(0,) semitone^(x/100) ; \
+                                      100 log(musicalcent)/log(semitone)
+
+#
+# Musical note lengths.
+#
+
+wholenote               !
+MUSICAL_NOTE_LENGTH     wholenote
+halfnote                1|2 wholenote
+quarternote             1|4 wholenote
+eighthnote              1|8 wholenote
+sixteenthnote           1|16 wholenote
+thirtysecondnote        1|32 wholenote
+sixtyfourthnote         1|64 wholenote
+dotted                  3|2
+doubledotted            7|4
+breve                   doublewholenote
+semibreve               wholenote
+minimnote               halfnote
+crotchet                quarternote
+quaver                  eighthnote
+semiquaver              sixteenthnote
+demisemiquaver          thirtysecondnote
+hemidemisemiquaver      sixtyfourthnote
+semidemisemiquaver      hemidemisemiquaver
+
+#
+# yarn and cloth measures
+#
+
+# yarn linear density
+
+woolyarnrun             1600 yard/pound # 1600 yds of "number 1 yarn" weighs
+                                        # a pound.
+yarncut                 300 yard/pound  # Less common system used in
+                                        # Pennsylvania for wool yarn
+cottonyarncount         840 yard/pound
+linenyarncount          300 yard/pound  # Also used for hemp and ramie
+worstedyarncount        1680 ft/pound
+metricyarncount         meter/gram
+denier                  1|9 tex            # used for silk and rayon
+manchesteryarnnumber    drams/1000 yards   # old system used for silk
+pli                     lb/in
+typp                    1000 yd/lb   # abbreviation for Thousand Yard Per Pound
+asbestoscut             100 yd/lb    # used for glass and asbestos yarn
+
+tex                     gram / km    # rational metric yarn measure, meant
+drex                    0.1 tex      # to be used for any kind of yarn
+poumar                  lb / 1e6 yard
+
+# yarn and cloth length
+
+skeincotton             80*54 inch   # 80 turns of thread on a reel with a
+                                     #  54 in circumference (varies for other
+                                     #  kinds of thread)
+cottonbolt              120 ft       # cloth measurement
+woolbolt                210 ft
+bolt                    cottonbolt
+heer                    600 yards
+cut                     300 yards    # used for wet-spun linen yarn
+lea                     300 yards
+
+sailmakersyard          28.5 in
+sailmakersounce         oz / sailmakersyard 36 inch
+
+silkmomme               momme / 25 yards 1.49 inch  # Traditional silk weight
+silkmm                  silkmomme        # But it is also defined as
+                                         # lb/100 yd 45 inch.  The two
+                                         # definitions are slightly different
+                                         # and neither one seems likely to be
+                                         # the true source definition.
+
+#
+# drug dosage
+#
+
+mcg                     microgram        # Frequently used for vitamins
+iudiptheria             62.8 microgram   # IU is for international unit
+iupenicillin            0.6 microgram
+iuinsulin               41.67 microgram
+drop                    1|20 ml          # The drop was an old "unit" that was
+                                         # replaced by the minim.  But I was
+                                         # told by a pharmacist that in his
+                                         # profession, the conversion of 20
+                                         # drops per ml is actually used.
+bloodunit               450 ml           # For whole blood.  For blood
+                                         # components, a blood unit is the
+                                         # quantity of the component found in a
+                                         # blood unit of whole blood.  The
+                                         # human body contains about 12 blood
+                                         # units of whole blood.
+
+#
+# misc medical measure
+#
+
+frenchcathetersize      1|3 mm           # measure used for the outer diameter
+                                         # of a catheter
+charriere               frenchcathetersize
+
+
+#
+# fixup units for times when prefix handling doesn't do the job
+#
+
+hectare                 hectoare
+megohm                  megaohm
+kilohm                  kiloohm
+microhm                 microohm
+megalerg                megaerg    # 'L' added to make it pronounceable [18].
+
+#
+# Money
+#
+# Note that US$ is the primitive unit so other currencies are
+# generally given in US$.
+#
+
+unitedstatesdollar      US$
+usdollar                US$
+$                       dollar
+mark                    germanymark
+#bolivar                 venezuelabolivar       # Not all databases are 
+#venezuelabolivarfuerte  1e-5 bolivar           #    supplying these
+#bolivarfuerte           1e-5 bolivar           # The currency was revalued 
+#oldbolivar              1|1000 bolivarfuerte   # twice
+peseta                  spainpeseta
+rand                    southafricarand
+escudo                  portugalescudo
+guilder                 netherlandsguilder
+hollandguilder          netherlandsguilder
+peso                    mexicopeso
+yen                     japanyen
+lira                    turkeylira
+rupee                   indiarupee
+drachma                 greecedrachma
+franc                   francefranc
+markka                  finlandmarkka
+britainpound            unitedkingdompound
+greatbritainpound       unitedkingdompound
+unitedkingdompound      ukpound
+poundsterling           britainpound
+yuan                    chinayuan
+
+# Unicode Currency Names
+
+!utf8
+icelandkróna            icelandkrona
+polandzłoty             polandzloty
+tongapa’anga            tongapa'anga
+#venezuelabolívar        venezuelabolivar
+vietnamđồng             vietnamdong
+mongoliatögrög          mongoliatugrik
+sãotomé&príncipedobra   saotome&principedobra
+!endutf8
+
+UKP                     GBP        # Not an ISO code, but looks like one, and
+                                   # sometimes used on usenet.
+
+!include currency.units
+
+# Money on the gold standard, used in the late 19th century and early
+# 20th century.
+
+olddollargold           23.22 grains goldprice  # Used until 1934
+newdollargold           96|7 grains goldprice   # After Jan 31, 1934
+dollargold              newdollargold
+poundgold               113 grains goldprice    # British pound
+
+# Precious metals
+
+goldounce               goldprice troyounce
+silverounce             silverprice troyounce
+platinumounce           platinumprice troyounce
+XAU                     goldounce
+XPT                     platinumounce
+XAG                     silverounce
+
+# Nominal masses of US coins.  Note that dimes, quarters and half dollars
+# have weight proportional to value.  Before 1965 it was $40 / kg.
+
+USpennyweight           2.5 grams         # Since 1982, 48 grains before
+USnickelweight          5 grams
+USdimeweight            US$ 0.10 / (20 US$ / lb)   # Since 1965
+USquarterweight         US$ 0.25 / (20 US$ / lb)   # Since 1965
+UShalfdollarweight      US$ 0.50 / (20 US$ / lb)   # Since 1971
+USdollarweight          8.1 grams         # Weight of Susan B. Anthony and
+                                          #   Sacagawea dollar coins
+
+# British currency
+
+quid                    britainpound        # Slang names
+fiver                   5 quid
+tenner                  10 quid
+monkey                  500 quid
+brgrand                 1000 quid
+bob                     shilling
+
+shilling                1|20 britainpound   # Before decimalisation, there
+oldpence                1|12 shilling       # were 20 shillings to a pound,
+farthing                1|4 oldpence        # each of twelve old pence
+guinea                  21 shilling         # Still used in horse racing
+crown                   5 shilling
+florin                  2 shilling
+groat                   4 oldpence
+tanner                  6 oldpence
+brpenny                 0.01 britainpound
+pence                   brpenny
+tuppence                2 pence
+tuppenny                tuppence
+ha'penny                halfbrpenny
+hapenny                 ha'penny
+oldpenny                oldpence
+oldtuppence             2 oldpence
+oldtuppenny             oldtuppence
+threepence              3 oldpence    # threepence never refers to new money
+threepenny              threepence
+oldthreepence           threepence
+oldthreepenny           threepence
+oldhalfpenny            halfoldpenny
+oldha'penny             oldhalfpenny
+oldhapenny              oldha'penny
+brpony                  25 britainpound
+
+# Canadian currency
+
+loony                   1 canadadollar    # This coin depicts a loon
+toony                   2 canadadollar
+
+# Cryptocurrency
+
+satoshi                 1e-8 bitcoin
+XBT                     bitcoin           # nonstandard code
+
+# Inflation.
+#
+# Currently US inflation as reported by the BLS CPI index is available.
+# The UScpi() table reports the USA consumer price index.  Note that 
+# if you specify a year like 2015, that refers to the CPI reported
+# for December of 2014 (which is released in mid January 2015),
+# so it refers to the point right at the start of the given year. 
+# Months are increments of 1|12 on the year, so the January 2015
+# release will be 2015+1|12 = 2015.08333.  
+
+!include cpi.units
+
+USCPI()            UScpi
+USCPI_now          UScpi_now
+USCPI_lastdate     UScpi_lastdate
+cpi()              UScpi
+CPI()              UScpi
+cpi_now            UScpi_now
+CPI_now            UScpi_now
+cpi_lastdate       UScpi_lastdate
+CPI_lastdate       UScpi_lastdate
+
+# These definitions hide the CPI index and directly convert US dollars
+# from a specified date to current dollars.  You can use this to convert
+# historical dollars to present value or to convert money in the past
+# between two dates.
+
+dollars_in()       USdollars_in
+US$in()            USdollars_in
+$in()              USdollars_in
+
+# This definition gives the dimensionless US inflation factor since the
+# specified date.
+
+inflation_since()  USinflation_since
+
+
+#
+# Units used for measuring volume of wood
+#
+
+cord                    4*4*8 ft^3   # 4 ft by 4 ft by 8 ft bundle of wood
+facecord                1|2 cord
+cordfoot                1|8 cord     # One foot long section of a cord
+cordfeet                cordfoot
+housecord               1|3 cord     # Used to sell firewood for residences,
+                                     #   often confusingly called a "cord"
+boardfoot               ft^2 inch    # Usually 1 inch thick wood
+boardfeet               boardfoot
+fbm                     boardfoot    # feet board measure
+stack                   4 yard^3     # British, used for firewood and coal [18]
+rick                    4 ft 8 ft 16 inches # Stack of firewood, supposedly
+                                     #   sometimes called a face cord, but this
+                                     #   value is equal to 1|3 cord.  Name
+                                     #   comes from an old Norse word for a
+                                     #   stack of wood.
+stere                   m^3
+timberfoot              ft^3         # Used for measuring solid blocks of wood
+standard                120 12 ft 11 in 1.5 in  # This is the St Petersburg or
+                                     #   Pittsburg standard.  Apparently the
+                                     #   term is short for "standard hundred"
+                                     #   which was meant to refer to 100 pieces
+                                     #   of wood (deals).  However, this
+                                     #   particular standard is equal to 120
+                                     #   deals which are 12 ft by 11 in by 1.5
+                                     #   inches (not the standard deal).
+hoppusfoot               (4/pi) ft^3 # Volume calculation suggested in 1736
+hoppusboardfoot      1|12 hoppusfoot #   forestry manual by Edward Hoppus, for
+hoppuston              50 hoppusfoot #   estimating the usable volume of a log.
+                                     #   It results from computing the volume
+                                     #   of a cylindrical log of length, L, and
+                                     #   girth (circumference), G, by V=L(G/4)^2.
+                                     #   The hoppus ton is apparently still in
+                                     #   use for shipments from Southeast Asia.
+
+# In Britain, the deal is apparently any piece of wood over 6 feet long, over
+# 7 wide and 2.5 inches thick.  The OED doesn't give a standard size.  A piece
+# of wood less than 7 inches wide is called a "batten".  This unit is now used
+# exclusively for fir and pine.
+
+deal              12 ft 11 in 2.5 in # The standard North American deal [OED]
+wholedeal        12 ft 11 in 1.25 in # If it's half as thick as the standard
+                                     #   deal it's called a "whole deal"!
+splitdeal         12 ft 11 in 5|8 in # And half again as thick is a split deal.
+
+
+# Used for shellac mixing rate
+
+poundcut            pound / gallon
+lbcut               poundcut
+
+#
+# Gas and Liquid flow units
+#
+
+FLUID_FLOW              VOLUME / TIME
+
+# Some obvious volumetric gas flow units (cu is short for cubic)
+
+cumec                   m^3/s
+cusec                   ft^3/s
+
+# Conventional abbreviations for fluid flow units
+
+gph                     gal/hr
+gpm                     gal/min
+mgd                     megagal/day
+brgph                   brgallon/hr
+brgpm                   brgallon/min
+brmgd                   mega brgallon/day
+usgph                   usgallon/hr
+usgpm                   usgallon/min
+usmgd                   mega usgallon/day
+cfs                     ft^3/s
+cfh                     ft^3/hour
+cfm                     ft^3/min
+lpm                     liter/min
+lfm                     ft/min     # Used to report air flow produced by fans.
+                                   # Multiply by cross sectional area to get a
+                                   # flow in cfm.
+
+pru                     mmHg / (ml/min)  # peripheral resistance unit, used in
+                                         # medicine to assess blood flow in
+                                         # the capillaries.
+
+# Miner's inch:  This is an old historic unit used in the Western  United
+# States.  It is generally defined as the rate of flow through a one square
+# inch hole at a specified depth such as 4 inches.  In the late 19th century,
+# volume of water was sometimes measured in the "24 hour inch".  Values for the
+# miner's inch were fixed by state statues.  (This information is from a web
+# site operated by the Nevada Division of Water Planning:  The Water Words
+# Dictionary at http://water.nv.gov/WaterPlanDictionary.aspx, specifically
+# http://water.nv.gov/programs/planning/dictionary/wwords-M.pdf.  All
+# but minersinchNV are s.v.  Miner's Inch [Western United States])
+
+minersinchAZ            1.5 ft^3/min
+minersinchCA            1.5 ft^3/min
+minersinchMT            1.5 ft^3/min
+minersinchNV            1.5 ft^3/min
+minersinchOR            1.5 ft^3/min
+minersinchID            1.2 ft^3/min
+minersinchKS            1.2 ft^3/min
+minersinchNE            1.2 ft^3/min
+minersinchNM            1.2 ft^3/min
+minersinchND            1.2 ft^3/min
+minersinchSD            1.2 ft^3/min
+minersinchUT            1.2 ft^3/min
+minersinchCO            1 ft^3/sec / 38.4  # 38.4 miner's inches = 1 ft^3/sec
+minersinchBC            1.68 ft^3/min      # British Columbia
+
+# Oceanographic flow
+
+sverdrup                1e6 m^3 / sec   # Used to express flow of ocean
+                                        # currents.  Named after Norwegian
+                                        # oceanographer H. Sverdrup.
+
+# In vacuum science and some other applications, gas flow is measured
+# as the product of volumetric flow and pressure.  This is useful
+# because it makes it easy to compare with the flow at standard
+# pressure (one atmosphere).  It also directly relates to the number
+# of gas molecules per unit time, and hence to the mass flow if the
+# molecular mass is known.
+
+GAS_FLOW                PRESSURE FLUID_FLOW
+
+sccm                    atm cc/min     # 's' is for "standard" to indicate
+sccs                    atm cc/sec     # flow at standard pressure
+scfh                    atm ft^3/hour  #
+scfm                    atm ft^3/min
+slpm                    atm liter/min
+slph                    atm liter/hour
+lusec                   liter micron Hg / s  # Used in vacuum science
+
+# US Standard Atmosphere (1976)
+# Atmospheric temperature and pressure vs. geometric height above sea level
+# This definition covers only the troposphere (the lowest atmospheric
+# layer, up to 11 km), and assumes the layer is polytropic.
+# A polytropic process is one for which PV^k = const, where P is the
+# pressure, V is the volume, and k is the polytropic exponent.  The
+# polytropic index is n = 1 / (k - 1).  As noted in the Wikipedia article
+# https://en.wikipedia.org/wiki/Polytropic_process, some authors reverse
+# the definitions of "exponent" and "index."  The functions below assume
+# the following parameters:
+
+# temperature lapse rate, -dT/dz, in troposphere
+
+lapserate       6.5 K/km        # US Std Atm (1976)
+
+# air molecular weight, including constituent mol wt, given
+# in Table 3, p. 3; CH4 (16.04303) and N2O (44.0128) from
+# Table 15, p. 33. Values for molecular weights are slightly
+# different from current values, so the original numerical
+# values are retained.
+
+air_1976        78.084   %    28.0134 \
+              + 20.9476  %    31.9988 \
+              + 9340     ppm  39.948 \
+              +  314     ppm  44.00995 \
+              +   18.18  ppm  20.183 \
+              +    5.24  ppm   4.0026 \
+              +    1.5   ppm  16.04303 \
+              +    1.14  ppm  83.80 \
+              +    0.5   ppm   2.01594 \
+              +    0.27  ppm  44.0128 \
+              +    0.087 ppm 131.30
+
+# from US Standard Atmosphere, 1962, Table I.2.7, p. 9
+
+air_1962        78.084     %  28.0134 \
+              + 20.9476    %  31.9988 \
+              + 9340     ppm  39.948 \
+              +  314     ppm  44.00995 \
+              +  18.18   ppm  20.183 \
+              +   5.24   ppm   4.0026 \
+              +   2      ppm  16.04303 \
+              +   1.14   ppm  83.80 \
+              +   0.5    ppm   2.01594 \
+              +   0.5    ppm  44.0128 \
+              +   0.087  ppm 131.30
+
+# Average molecular weight of air
+#
+# Concentration of greenhouse gases CO2, CH4, and N20 are from
+# https://gml.noaa.gov/ccgg/trends/global.html (accessed 2023-04-10);
+# others are from NASA Earth Fact Sheet
+# https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html (accessed 2023-04-10)
+# Numbers do not add up to exactly 100% due to roundoff and uncertainty.  Water
+# is highly variable, typically makes up about 1%
+
+air_2023        78.08% nitrogen 2 \
+              + 20.95% oxygen 2 \
+              + 9340 ppm argon \
+              +  419 ppm (carbon + oxygen 2) \
+              +   18.18 ppm neon \
+              +    5.24 ppm helium \
+              +    1.92 ppm (carbon + 4 hydrogen) \
+              +    1.14 ppm krypton \
+              +    0.55 ppm hydrogen 2 \
+              +    0.34 ppm (nitrogen 2 + oxygen)
+
+# from NASA Earth Fact Sheet (accessed 28 August 2015)
+# http://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html
+
+air_2015        78.08% nitrogen 2 \
+              + 20.95% oxygen 2 \
+              + 9340 ppm argon \
+              +  400 ppm (carbon + oxygen 2) \
+              +   18.18 ppm neon \
+              +    5.24 ppm helium \
+              +    1.7  ppm (carbon + 4 hydrogen) \
+              +    1.14 ppm krypton \
+              +    0.55 ppm hydrogen 2
+
+air             air_2023
+
+# universal gas constant
+R_1976          8.31432e3 N m/(kmol K)
+
+# polytropic index n
+polyndx_1976    air_1976 (kg/kmol) gravity/(R_1976 lapserate) - 1
+
+# If desired, redefine using current values for air mol wt and R
+
+polyndx         polyndx_1976
+# polyndx       air (kg/kmol) gravity/(R lapserate) - 1
+
+# for comparison with various references
+
+polyexpnt       (polyndx + 1) / polyndx
+
+# The model assumes the following reference values:
+# sea-level temperature and pressure
+
+stdatmT0        288.15 K
+stdatmP0        atm
+
+# "effective radius" for relation of geometric to geopotential height,
+# at a latitude at which g = 9.80665 m/s (approximately 45.543 deg); no
+# relation to actual radius
+
+earthradUSAtm   6356766 m
+
+# Temperature vs. geopotential height h
+# Assumes 15 degC at sea level
+# Based on approx 45 deg latitude
+# Lower limits of domain and upper limits of range are those of the
+# tables in US Standard Atmosphere (NASA 1976)
+
+stdatmTH(h) units=[m;K] domain=[-5000,11e3] range=[217,321] \
+    stdatmT0+(-lapserate h) ; (stdatmT0+(-stdatmTH))/lapserate
+
+# Temperature vs. geometric height z; based on approx 45 deg latitude
+stdatmT(z) units=[m;K] domain=[-5000,11e3] range=[217,321] \
+    stdatmTH(geop_ht(z)) ; ~geop_ht(~stdatmTH(stdatmT))
+
+# Pressure vs. geopotential height h
+# Assumes 15 degC and 101325 Pa at sea level
+# Based on approx 45 deg latitude
+# Lower limits of domain and upper limits of range are those of the
+# tables in US Standard Atmosphere (NASA 1976)
+
+stdatmPH(h) units=[m;Pa] domain=[-5000,11e3] range=[22877,177764] \
+    atm (1 - (lapserate/stdatmT0) h)^(polyndx + 1) ; \
+    (stdatmT0/lapserate) (1+(-(stdatmPH/stdatmP0)^(1/(polyndx + 1))))
+
+# Pressure vs. geometric height z; based on approx 45 deg latitude
+stdatmP(z) units=[m;Pa] domain=[-5000,11e3] range=[22877,177764] \
+   stdatmPH(geop_ht(z)); ~geop_ht(~stdatmPH(stdatmP))
+
+# Geopotential height from geometric height
+# Based on approx 45 deg latitude
+# Lower limits of domain and range are somewhat arbitrary; they
+# correspond to the limits in the US Std Atm tables
+
+geop_ht(z) units=[m;m] domain=[-5000,) range=[-5004,) \
+    (earthradUSAtm z) / (earthradUSAtm + z) ; \
+    (earthradUSAtm geop_ht) / (earthradUSAtm + (-geop_ht))
+
+# The standard value for the sea-level acceleration due to gravity is
+# 9.80665 m/s^2, but the actual value varies with latitude (Harrison 1949)
+# R_eff = 2 g_phi / denom
+# g_phi = 978.0356e-2 (1+0.0052885 sin(lat)^2+(-0.0000059) sin(2 lat)^2)
+#   or
+# g_phi = 980.6160e-2 (1+(-0.0026373) cos(2 lat)+0.0000059 cos(2 lat)^2)
+# denom = 3.085462e-6+2.27e-9 cos(2 lat)+(-2e-12) cos(4 lat) (minutes?)
+# There is no inverse function; the standard value applies at a latitude
+# of about 45.543 deg
+
+g_phi(lat) units=[deg;m/s2] domain=[0,90] noerror  \
+    980.6160e-2 (1+(-0.0026373) cos(2 lat)+0.0000059 cos(2 lat)^2) m/s2
+
+# effective Earth radius for relation of geometric height to
+# geopotential height, as function of latitude (Harrison 1949)
+
+earthradius_eff(lat) units=[deg;m] domain=[0,90] noerror \
+    m 2 9.780356 (1+0.0052885 sin(lat)^2+(-0.0000059) sin(2 lat)^2) / \
+    (3.085462e-6 + 2.27e-9 cos(2 lat) + (-2e-12) cos(4 lat))
+
+# References
+# Harrison, L.P. 1949.  Relation Between Geopotential and Geometric
+#   Height.  In Smithsonian Meteorological Tables. List, Robert J., ed.
+#   6th ed., 4th reprint, 1968.  Washington, DC: Smithsonian Institution.
+# NASA.  US National Aeronautics and Space Administration. 1976.
+#   US Standard Atmosphere 1976.  Washington, DC: US Government Printing Office.
+
+# Gauge pressure functions
+#
+# Gauge pressure is measured relative to atmospheric pressure.  In the English
+# system, where pressure is often given in pounds per square inch, gauge
+# pressure is often indicated by 'psig' to distinguish it from absolute
+# pressure, often indicated by 'psia'.  At the standard atmospheric pressure
+# of 14.696 psia, a gauge pressure of 0 psig is an absolute pressure of 14.696
+# psia; an automobile tire inflated to 31 psig has an absolute pressure of
+# 45.696 psia.
+#
+# With gaugepressure(), the units must be specified (e.g., gaugepressure(1.5
+# bar)); with psig(), the units are taken as psi, so the example above of tire
+# pressure could be given as psig(31).
+#
+# If the normal elevation is significantly different from sea level, change
+# Patm appropriately, and adjust the lower domain limit on the gaugepressure
+# definition.
+
+Patm    atm
+
+gaugepressure(x) units=[Pa;Pa] domain=[-101325,) range=[0,) \
+                x + Patm ; gaugepressure+(-Patm)
+
+psig(x) units=[1;Pa] domain=[-14.6959487755135,) range=[0,) \
+    gaugepressure(x psi) ; ~gaugepressure(psig) / psi
+
+
+# Pressure for underwater diving
+
+seawater             0.1 bar / meter
+msw                  meter seawater
+fsw                  foot seawater
+
+#
+# Wire Gauge
+#
+# This area is a nightmare with huge charts of wire gauge diameters
+# that usually have no clear origin.  There are at least 5 competing wire gauge
+# systems to add to the confusion.  The use of wire gauge is related to the
+# manufacturing method: a metal rod is heated and drawn through a hole.  The
+# size change can't be too big.  To get smaller wires, the process is repeated
+# with a series of smaller holes.  Generally larger gauges mean smaller wires.
+# The gauges often have values such as "00" and "000" which are larger sizes
+# than simply "0" gauge.  In the tables that appear below, these gauges must be
+# specified as negative numbers (e.g. "00" is -1, "000" is -2, etc).
+# Alternatively, you can use the following units:
+#
+
+g00                      (-1)
+g000                     (-2)
+g0000                    (-3)
+g00000                   (-4)
+g000000                  (-5)
+g0000000                 (-6)
+
+# American Wire Gauge (AWG) or Brown & Sharpe Gauge appears to be the most
+# important gauge. ASTM B-258 specifies that this gauge is based on geometric
+# interpolation between gauge 0000, which is 0.46 inches exactly, and gauge 36
+# which is 0.005 inches exactly.  Therefore, the diameter in inches of a wire
+# is given by the formula 1|200 92^((36-g)/39).  Note that 92^(1/39) is close
+# to 2^(1/6), so diameter is approximately halved for every 6 gauges.  For the
+# repeated zero values, use negative numbers in the formula.  The same document
+# also specifies rounding rules which seem to be ignored by makers of tables.
+# Gauges up to 44 are to be specified with up to 4 significant figures, but no
+# closer than 0.0001 inch.  Gauges from 44 to 56 are to be rounded to the
+# nearest 0.00001 inch.
+#
+# In addition to being used to measure wire thickness, this gauge is used to
+# measure the thickness of sheets of aluminum, copper, and most metals other
+# than steel, iron and zinc.
+
+wiregauge(g) units=[1;m] range=(0,) \
+             1|200 92^((36+(-g))/39) in; 36+(-39)ln(200 wiregauge/in)/ln(92)
+awg()        wiregauge
+
+# Next we have the SWG, the Imperial or British Standard Wire Gauge.  This one
+# is piecewise linear.  It was used for aluminum sheets but also shows up for
+# wire used in jewelry.  
+
+brwiregauge[in]  \
+       -6 0.5    \
+       -5 0.464  \
+       -3 0.4    \
+       -2 0.372  \
+        3 0.252  \
+        6 0.192  \
+       10 0.128  \
+       14 0.08   \
+       19 0.04   \
+       23 0.024  \
+       26 0.018  \
+       28 0.0148 \
+       30 0.0124 \
+       39 0.0052 \
+       49 0.0012 \
+       50 0.001
+
+swg()        brwiregauge  
+
+# The following is from the Appendix to ASTM B 258
+#
+#    For example, in U.S. gage, the standard for sheet metal is based on the
+#    weight of the metal, not on the thickness. 16-gage is listed as
+#    approximately .0625 inch thick and 40 ounces per square foot (the original
+#    standard was based on wrought iron at .2778 pounds per cubic inch; steel
+#    has almost entirely superseded wrought iron for sheet use, at .2833 pounds
+#    per cubic inch). Smaller numbers refer to greater thickness. There is no
+#    formula for converting gage to thickness or weight.
+#
+# It's rather unclear from the passage above whether the plate gauge values are
+# therefore wrong if steel is being used.  Reference [15] states that steel is
+# in fact measured using this gauge (under the name Manufacturers' Standard
+# Gauge) with a density of 501.84 lb/ft3 = 0.2904 lb/in3 used for steel.
+# But this doesn't seem to be the correct density of steel (.2833 lb/in3 is
+# closer).
+#
+# This gauge was established in 1893 for purposes of taxation.
+
+# Old plate gauge for iron
+
+plategauge[(oz/ft^2)/(480*lb/ft^3)] \
+      -5 300   \
+       1 180   \
+      14  50   \
+      16  40   \
+      17  36   \
+      20  24   \
+      26  12   \
+      31   7   \
+      36   4.5 \
+      38   4
+
+# Manufacturers Standard Gage
+
+stdgauge[(oz/ft^2)/(501.84*lb/ft^3)] \
+      -5 300   \
+       1 180   \
+      14  50   \
+      16  40   \
+      17  36   \
+      20  24   \
+      26  12   \
+      31   7   \
+      36   4.5 \
+      38   4
+
+# A special gauge is used for zinc sheet metal.  Notice that larger gauges
+# indicate thicker sheets.
+
+zincgauge[in]    \
+        1 0.002  \
+       10 0.02   \
+       15 0.04   \
+       19 0.06   \
+       23 0.1    \
+       24 0.125  \
+       27 0.5    \
+       28 1
+
+#
+# Imperial drill bit sizes are reported in inches or in a numerical or
+# letter gauge.
+#
+
+drillgauge[in] \
+       1  0.2280 \
+       2  0.2210 \
+       3  0.2130 \
+       4  0.2090 \
+       5  0.2055 \
+       6  0.2040 \
+       7  0.2010 \
+       8  0.1990 \
+       9  0.1960 \
+      10  0.1935 \
+      11  0.1910 \
+      12  0.1890 \
+      13  0.1850 \
+      14  0.1820 \
+      15  0.1800 \
+      16  0.1770 \
+      17  0.1730 \
+      18  0.1695 \
+      19  0.1660 \
+      20  0.1610 \
+      22  0.1570 \
+      23  0.1540 \
+      24  0.1520 \
+      25  0.1495 \
+      26  0.1470 \
+      27  0.1440 \
+      28  0.1405 \
+      29  0.1360 \
+      30  0.1285 \
+      31  0.1200 \
+      32  0.1160 \
+      33  0.1130 \
+      34  0.1110 \
+      35  0.1100 \
+      36  0.1065 \
+      38  0.1015 \
+      39  0.0995 \
+      40  0.0980 \
+      41  0.0960 \
+      42  0.0935 \
+      43  0.0890 \
+      44  0.0860 \
+      45  0.0820 \
+      46  0.0810 \
+      48  0.0760 \
+      51  0.0670 \
+      52  0.0635 \
+      53  0.0595 \
+      54  0.0550 \
+      55  0.0520 \
+      56  0.0465 \
+      57  0.0430 \
+      65  0.0350 \
+      66  0.0330 \
+      68  0.0310 \
+      69  0.0292 \
+      70  0.0280 \
+      71  0.0260 \
+      73  0.0240 \
+      74  0.0225 \
+      75  0.0210 \
+      76  0.0200 \
+      78  0.0160 \
+      79  0.0145 \
+      80  0.0135 \
+      88  0.0095 \
+      104 0.0031 
+
+drillA    0.234 in
+drillB    0.238 in
+drillC    0.242 in
+drillD    0.246 in
+drillE    0.250 in
+drillF    0.257 in
+drillG    0.261 in
+drillH    0.266 in
+drillI    0.272 in
+drillJ    0.277 in
+drillK    0.281 in
+drillL    0.290 in
+drillM    0.295 in
+drillN    0.302 in
+drillO    0.316 in
+drillP    0.323 in
+drillQ    0.332 in
+drillR    0.339 in
+drillS    0.348 in
+drillT    0.358 in
+drillU    0.368 in
+drillV    0.377 in
+drillW    0.386 in
+drillX    0.397 in
+drillY    0.404 in
+drillZ    0.413 in
+
+#
+# Screw sizes
+#
+# In the USA, screw diameters for both wood screws and machine screws
+# are reported using a gauge number.  Metric machine screws are
+# reported as Mxx where xx is the diameter in mm.
+#
+
+screwgauge(g) units=[1;m] range=[0,) \
+              (.06 + .013 g) in ; (screwgauge/in + (-.06)) / .013
+
+#
+# Abrasive grit size
+#
+# Standards governing abrasive grit sizes are complicated, specifying
+# fractions of particles that are passed or retained by different mesh
+# sizes.  As a result, it is not possible to make precise comparisons
+# of different grit standards.  The tables below allow the
+# determination of rough equivlants by using median particle size.
+#
+# Standards in the USA are determined by the Unified Abrasives
+# Manufacturers' Association (UAMA), which resulted from the merger of
+# several previous organizations.  One of the old organizations was
+# CAMI (Coated Abrasives Manufacturers' Institute).
+#
+# UAMA has a web page with plots showing abrasive particle ranges for
+# various different grits and comparisons between standards.
+#
+# https://uama.org/abrasives-101/
+#
+# Abrasives are grouped into "bonded" abrasives for use with grinding
+# wheels and "coated" abrasives for sandpapers and abrasive films.
+# The industry uses different grit standards for these two
+# categories.
+#
+# Another division is between "macrogrits", grits below 240 and
+# "microgrits", which are above 240.  Standards differ, as do methods
+# for determining particle size.  In the USA, ANSI B74.12 is the
+# standard governing macrogrits.  ANSI B74.10 covers bonded microgrit
+# abrasives, and ANSI B74.18 covers coated microgrit abrasives.  It
+# appears that the coated standard is identical to the bonded standard
+# for grits up through 600 but then diverges significantly.
+#
+# European grit sizes are determined by the Federation of European
+# Producers of Abrasives.  http://www.fepa-abrasives.org
+#
+# They give two standards, the "F" grit for bonded abrasives and the
+# "P" grit for coated abrasives.  This data is taken directly from
+# their web page.
+
+# FEPA P grit for coated abrasives is commonly seen on sandpaper in
+# the USA where the paper will be marked P600, for example.  FEPA P
+# grits are said to be more tightly constrained than comparable ANSI
+# grits so that the particles are more uniform in size and hence give
+# a better finish.
+
+grit_P[micron] \
+        12 1815 \
+        16 1324 \
+        20 1000 \
+        24 764 \
+        30 642 \
+        36 538 \
+        40 425 \
+        50 336 \
+        60 269 \
+        80 201 \
+        100 162 \
+        120 125 \
+        150 100 \
+        180 82 \
+        220 68 \
+        240 58.5 \
+        280 52.2 \
+        320 46.2 \
+        360 40.5 \
+        400 35 \
+        500 30.2 \
+        600 25.8 \
+        800 21.8 \
+        1000 18.3 \
+        1200 15.3 \
+        1500 12.6 \
+        2000 10.3 \
+        2500 8.4
+
+# The F grit is the European standard for bonded abrasives such as
+# grinding wheels
+
+grit_F[micron] \
+        4 4890 \
+        5 4125 \
+        6 3460 \
+        7 2900 \
+        8 2460 \
+        10 2085 \
+        12 1765 \
+        14 1470 \
+        16 1230 \
+        20 1040 \
+        22 885 \
+        24 745 \
+        30 625 \
+        36 525 \
+        40 438 \
+        46 370 \
+        54 310 \
+        60 260 \
+        70 218 \
+        80 185 \
+        90 154 \
+        100 129 \
+        120 109 \
+        150 82 \
+        180 69 \
+        220 58 \
+        230 53 \
+        240 44.5 \
+        280 36.5 \
+        320 29.2 \
+        360 22.8 \
+        400 17.3 \
+        500 12.8 \
+        600 9.3 \
+        800 6.5 \
+        1000 4.5 \
+        1200 3 \
+        1500 2.0 \
+        2000 1.2
+
+# According to the UAMA web page, the ANSI bonded and ANSI coated standards
+# are identical to FEPA F in the macrogrit range (under 240 grit), so these
+# values are taken from the FEPA F table.  The values for 240 and above are
+# from the UAMA web site and represent the average of the "d50" range
+# endpoints listed there.
+
+ansibonded[micron] \
+    4 4890 \
+    5 4125 \
+    6 3460 \
+    7 2900 \
+    8 2460 \
+    10 2085 \
+    12 1765 \
+    14 1470 \
+    16 1230 \
+    20 1040 \
+    22 885 \
+    24 745 \
+    30 625 \
+    36 525 \
+    40 438 \
+    46 370 \
+    54 310 \
+    60 260 \
+    70 218 \
+    80 185 \
+    90 154 \
+    100 129 \
+    120 109 \
+    150 82 \
+    180 69 \
+    220 58 \
+    240 50 \
+    280 39.5 \
+    320 29.5 \
+    360 23 \
+    400 18.25 \
+    500 13.9 \
+    600 10.55 \
+    800 7.65 \
+    1000 5.8 \
+    1200 3.8
+
+grit_ansibonded() ansibonded
+
+# Like the bonded grit, the coated macrogrits below 240 are taken from the
+# FEPA F table.  Data above this is from the UAMA site.  Note that the coated
+# and bonded standards are evidently the same from 240 up to 600 grit, but
+# starting at 800 grit, the coated standard diverges.  The data from UAMA show
+# that 800 grit coated has an average size slightly larger than the average
+# size of 600 grit coated/bonded.  However, the 800 grit has a significantly
+# smaller particle size variation.
+#
+# Because of this non-monotonicity from 600 grit to 800 grit this definition
+# produces a warning about the lack of a unique inverse.
+
+ansicoated[micron] noerror \
+    4 4890 \
+    5 4125 \
+    6 3460 \
+    7 2900 \
+    8 2460 \
+    10 2085 \
+    12 1765 \
+    14 1470 \
+    16 1230 \
+    20 1040 \
+    22 885 \
+    24 745 \
+    30 625 \
+    36 525 \
+    40 438 \
+    46 370 \
+    54 310 \
+    60 260 \
+    70 218 \
+    80 185 \
+    90 154 \
+    100 129 \
+    120 109 \
+    150 82 \
+    180 69 \
+    220 58 \
+    240 50 \
+    280 39.5 \
+    320 29.5 \
+    360 23 \
+    400 18.25 \
+    500 13.9 \
+    600 10.55 \
+    800 11.5 \
+    1000 9.5 \
+    2000 7.2 \
+    2500 5.5 \
+    3000 4 \
+    4000 3 \
+    6000 2 \
+    8000 1.2
+
+grit_ansicoated()  ansicoated
+
+
+#
+# Is this correct?  This is the JIS Japanese standard used on waterstones
+#
+jisgrit[micron] \
+     150 75 \
+     180 63 \
+     220 53 \
+     280 48 \
+     320 40 \
+     360 35 \
+     400 30 \
+     600 20 \
+     700 17 \
+     800 14 \
+     1000 11.5 \
+     1200 9.5 \
+     1500 8 \
+     2000 6.7 \
+     2500 5.5 \
+     3000 4 \
+     4000 3 \
+     6000 2 \
+     8000 1.2
+
+# The "Finishing Scale" marked with an A (e.g. A75).  This information
+# is from the web page of the sand paper manufacturer Klingspor
+# https://www.klingspor.com/ctemplate1.aspx?page=default/html/gritGradingSystems_en-US.html
+#
+# I have no information about what this scale is used for.
+
+grit_A[micron]\
+     16 15.3 \
+     25 21.8 \
+     30 23.6 \
+     35 25.75 \
+     45 35 \
+     60 46.2 \
+     65 53.5 \
+     75 58.5 \
+     90 65 \
+     110 78 \
+     130 93 \
+     160 127 \
+     200 156
+#
+# Grits for DMT brand diamond sharpening stones from
+# https://www.dmtsharp.com/resources/dmt-catalog-product-information.html
+# "DMT Diamond Grits" PDF download
+
+dmtxxcoarse  120 micron    # 120 mesh
+dmtsilver    dmtxxcoarse
+dmtxx        dmtxxcoarse
+dmtxcoarse   60 micron     # 220 mesh
+dmtx         dmtxcoarse
+dmtblack     dmtxcoarse
+dmtcoarse    45 micron     # 325 mesh
+dmtc         dmtcoarse
+dmtblue      dmtcoarse
+dmtfine      25 micron     # 600 mesh
+dmtred       dmtfine
+dmtf         dmtfine
+dmtefine     9 micron      # 1200 mesh
+dmte         dmtefine
+dmtgreen     dmtefine
+dmtceramic   7 micron      # 2200 mesh
+dmtcer       dmtceramic
+dmtwhite     dmtceramic
+dmteefine    3 micron      # 8000 mesh
+dmttan       dmteefine
+dmtee        dmteefine
+
+#
+# The following values come from a page in the Norton Stones catalog,
+# available at their web page, http://www.nortonstones.com.
+#
+
+hardtranslucentarkansas  6 micron     # Natural novaculite (silicon quartz)
+softarkansas             22 micron    #   stones
+
+extrafineindia           22 micron    # India stones are Norton's manufactured
+fineindia                35 micron    #   aluminum oxide product
+mediumindia              53.5 micron
+coarseindia              97 micron
+
+finecrystolon            45 micron    # Crystolon stones are Norton's
+mediumcrystalon          78 micron    #   manufactured silicon carbide product
+coarsecrystalon          127 micron
+
+# The following are not from the Norton catalog
+hardblackarkansas        6 micron
+hardwhitearkansas        11 micron
+washita                  35 micron
+
+#
+# Mesh systems for measuring particle sizes by sifting through a wire
+# mesh or sieve
+#
+
+# The Tyler system and US Sieve system are based on four steps for
+# each factor of 2 change in the size, so each size is 2^1|4 different
+# from the adjacent sizes.  Unfortunately, the mesh numbers are
+# arbitrary, so the sizes cannot be expressed with a functional form.
+# Various references round the values differently.  The mesh numbers
+# are supposed to correspond to the number of holes per inch, but this
+# correspondence is only approximate because it doesn't include the
+# wire size of the mesh.
+
+# The Tyler Mesh system was apparently introduced by the WS Tyler
+# company, but it appears that they no longer use it.  They follow the
+# ASTM E11 standard.
+
+meshtyler[micron] \
+          2.5 8000 \
+          3   6727 \
+          3.5 5657 \
+          4   4757 \
+          5   4000 \
+          6   3364 \
+          7   2828 \
+          8   2378 \
+          9   2000 \
+         10   1682 \
+         12   1414 \
+         14   1189 \
+         16   1000 \
+         20    841 \
+         24    707 \
+         28    595 \
+         32    500 \
+         35    420 \
+         42    354 \
+         48    297 \
+         60    250 \
+         65    210 \
+         80    177 \
+        100    149 \
+        115    125 \
+        150    105 \
+        170     88 \
+        200     74 \
+        250     63 \
+        270     53 \
+        325     44 \
+        400     37 
+
+# US Sieve size, ASTM E11
+#
+# The WS Tyler company prints the list from ASTM E11 in
+# A Calculator for ASTM E11 Standard Sieve Designations
+# https://blog.wstyler.com/particle-analysis/astm-e11-standard-designations
+
+sieve[micron] \
+          3.5   5600 \
+          4     4750 \
+          5     4000 \
+          6     3350 \
+          7     2800 \
+          8     2360 \
+         10     2000 \
+         12     1700 \
+         14     1400 \
+         16     1180 \
+         18     1000 \
+         20      850 \
+         25      710 \
+         30      600 \
+         35      500 \
+         40      425 \
+         45      355 \
+         50      300 \
+         60      250 \
+         70      212 \
+         80      180 \
+        100      150 \
+        120      125 \
+        140      106 \
+        170       90 \
+        200       75 \
+        230       63 \
+        270       53 \
+        325       45 \
+        400       38 \
+        450       32 \
+        500       25 \
+        625       20   # These last two values are not in the standard series
+                       # but were included in the ASTM standard because they
+meshUS()  sieve        # were in common usage.                               
+
+# British Mesh size, BS 410: 1986
+# This system appears to correspond to the Tyler and US system, but
+# with different mesh numbers.
+#
+# http://www.panadyne.com/technical/panadyne_international_sieve_chart.pdf
+# 
+
+meshbritish[micron] \
+          3    5657 \
+          3.5  4757 \
+          4    4000 \
+          5    3364 \
+          6    2828 \
+          7    2378 \
+          8    2000 \
+         10    1682 \
+         12    1414 \
+         14    1189 \
+         16    1000 \
+         18     841 \
+         22     707 \
+         25     595 \
+         30     500 \
+         36     420 \
+         44     354 \
+         52     297 \
+         60     250 \
+         72     210 \
+         85     177 \
+        100     149 \
+        120     125 \
+        150     105 \
+        170      88 \
+        200      74 \
+        240      63 \
+        300      53 \
+        350      44 \
+        400      37  
+
+# French system, AFNOR NFX11-501: 1970
+# The system appears to be based on size doubling every 3 mesh
+# numbers, though the values have been aggressively rounded.
+# It's not clear if the unrounded values would be considered
+# incorrect, so this is given as a table rather than a function.
+# Functional form:
+#    meshtamis(mesh) units=[1;m] 5000 2^(1|3 (mesh-38)) micron
+#
+# http://www.panadyne.com/technical/panadyne_international_sieve_chart.pdf
+
+meshtamis[micron] \
+        17   40 \
+        18   50 \
+        19   63 \
+        20   80 \
+        21  100 \
+        22  125 \
+        23  160 \
+        24  200 \
+        25  250 \
+        26  315 \
+        27  400 \
+        28  500 \
+        29  630 \
+        30  800 \
+        31 1000 \
+        32 1250 \
+        33 1600 \
+        34 2000 \
+        35 2500 \
+        36 3150 \
+        37 4000 \
+        38 5000 
+
+#
+# Ring size. All ring sizes are given as the circumference of the ring.
+#
+
+# USA ring sizes.  Several slightly different definitions seem to be in
+# circulation.  According to [15], the interior diameter of size n ring in
+# inches is 0.32 n + 0.458 for n ranging from 3 to 13.5 by steps of 0.5.  The
+# size 2 ring is inconsistently 0.538in and no 2.5 size is listed.
+#
+# However, other sources list 0.455 + 0.0326 n and 0.4525 + 0.0324 n as the
+# diameter and list no special case for size 2.  (Or alternatively they are
+# 1.43 + .102 n and 1.4216+.1018 n for measuring circumference in inches.)  One
+# reference claimed that the original system was that each size was 1|10 inch
+# circumference, but that source doesn't have an explanation for the modern
+# system which is somewhat different.
+
+ringsize(n) units=[1;in] domain=[2,) range=[1.6252,) \
+            (1.4216+.1018 n) in ; (ringsize/in + (-1.4216))/.1018
+
+# Old practice in the UK measured rings using the "Wheatsheaf gauge" with sizes
+# specified alphabetically and based on the ring inside diameter in steps of
+# 1|64 inch.  This system was replaced in 1987 by British Standard 6820 which
+# specifies sizes based on circumference.  Each size is 1.25 mm different from
+# the preceding size.  The baseline is size C which is 40 mm circumference.
+# The new sizes are close to the old ones.  Sometimes it's necessary to go
+# beyond size Z to Z+1, Z+2, etc.
+
+sizeAring               37.50 mm
+sizeBring               38.75 mm
+sizeCring               40.00 mm
+sizeDring               41.25 mm
+sizeEring               42.50 mm
+sizeFring               43.75 mm
+sizeGring               45.00 mm
+sizeHring               46.25 mm
+sizeIring               47.50 mm
+sizeJring               48.75 mm
+sizeKring               50.00 mm
+sizeLring               51.25 mm
+sizeMring               52.50 mm
+sizeNring               53.75 mm
+sizeOring               55.00 mm
+sizePring               56.25 mm
+sizeQring               57.50 mm
+sizeRring               58.75 mm
+sizeSring               60.00 mm
+sizeTring               61.25 mm
+sizeUring               62.50 mm
+sizeVring               63.75 mm
+sizeWring               65.00 mm
+sizeXring               66.25 mm
+sizeYring               67.50 mm
+sizeZring               68.75 mm
+
+# Japanese sizes start with size 1 at a 13mm inside diameter and each size is
+# 1|3 mm larger in diameter than the previous one.  They are multiplied by pi
+# to give circumference.
+
+jpringsize(n)  units=[1;mm] domain=[1,) range=[0.040840704,) \
+               (38|3 + n/3) pi mm ; 3 jpringsize/ pi mm + (-38)
+
+# The European ring sizes are the length of the circumference in mm minus 40.
+
+euringsize(n)  units=[1;mm] (n+40) mm ; euringsize/mm + (-40)
+
+#
+# Abbreviations
+#
+
+mph                     mile/hr
+brmpg                   mile/brgallon
+usmpg                   mile/usgallon
+mpg                     mile/gal
+kph                     km/hr
+fL                      footlambert
+fpm                     ft/min
+fps                     ft/s
+rpm                     rev/min
+rps                     rev/sec
+mi                      mile
+smi                     mile
+nmi                     nauticalmile
+mbh                     1e3 btu/hour
+mcm                     1e3 circularmil
+ipy                     inch/year    # used for corrosion rates
+ccf                     100 ft^3     # used for selling water [18]
+Mcf                     1000 ft^3    # not million cubic feet [18]
+kp                      kilopond
+kpm                     kp meter
+Wh                      W hour
+hph                     hp hour
+plf                     lb / foot    # pounds per linear foot
+
+#
+# Compatibility units with Unix version
+#
+
+pa                      Pa
+ev                      eV
+hg                      Hg
+oe                      Oe
+mh                      mH
+rd                      rod
+pf                      pF
+gr                      grain
+nt                      N
+hz                      Hz
+hd                      hogshead
+dry                     drygallon/gallon
+nmile                   nauticalmile
+beV                     GeV
+bev                     beV
+coul                    C
+
+#
+# Radioactivity units
+#
+event !dimensionless
+becquerel    event           /s           # Activity of radioactive source
+Bq                      becquerel    #
+curie                   3.7e10 Bq    # Defined in 1910 as the radioactivity
+Ci                      curie        # emitted by the amount of radon that is
+                                     # in equilibrium with 1 gram of radium.
+rutherford              1e6 Bq       #
+
+RADIATION_DOSE          gray
+gray                    J/kg         # Absorbed dose of radiation
+Gy                      gray         #
+rad                     1e-2 Gy      # From Radiation Absorbed Dose
+rep                     8.38 mGy     # Roentgen Equivalent Physical, the amount
+                                     #   of radiation which , absorbed in the
+                                     #   body, would liberate the same amount
+                                     #   of energy as 1 roentgen of X rays
+                                     #   would, or 97 ergs.
+
+sievert                 J/kg         # Dose equivalent:  dosage that has the
+Sv                      sievert      #   same effect on human tissues as 200
+rem                     1e-2 Sv      #   keV X-rays.  Different types of
+                                     #   radiation are weighted by the
+                                     #   Relative Biological Effectiveness
+                                     #   (RBE).
+                                     #
+                                     #      Radiation type       RBE
+                                     #       X-ray, gamma ray     1
+                                     #       beta rays, > 1 MeV   1
+                                     #       beta rays, < 1 MeV  1.08
+                                     #       neutrons, < 1 MeV   4-5
+                                     #       neutrons, 1-10 MeV   10
+                                     #       protons, 1 MeV      8.5
+                                     #       protons, .1 MeV      10
+                                     #       alpha, 5 MeV         15
+                                     #       alpha, 1 MeV         20
+                                     #
+                                     #   The energies are the kinetic energy
+                                     #   of the particles.  Slower particles
+                                     #   interact more, so they are more
+                                     #   effective ionizers, and hence have
+                                     #   higher RBE values.
+                                     #
+                                     # rem stands for Roentgen Equivalent
+                                     # Mammal
+banana_dose           0.1e-6 sievert # Informal measure of the dose due to 
+                                     #   eating one average sized banana
+roentgen              2.58e-4 C / kg # Ionizing radiation that produces
+                                     #   1 statcoulomb of charge in 1 cc of
+                                     #   dry air at stp.
+rontgen                 roentgen     # Sometimes it appears spelled this way
+sievertunit             8.38 rontgen # Unit of gamma ray dose delivered in one
+                                     #   hour at a distance of 1 cm from a
+                                     #   point source of 1 mg of radium
+                                     #   enclosed in platinum .5 mm thick.
+
+eman                    1e-7 Ci/m^3  # radioactive concentration
+mache                   3.7e-7 Ci/m^3
+
+#
+# Atomic weights.  The atomic weight of an element is the ratio of the mass of
+# a mole of the element to 1|12 of a mole of Carbon 12.  For each element, we
+# list the atomic weights of all of the isotopes.  The Standard Atomic Weights
+# apply to the elements in the isotopic composition that occurs naturally on
+# Earth.  These are computed values based on the isotopic distribution, and
+# may vary for specific samples.  Elements which do not occur naturally do
+# not have Standard Atomic Weights.  For these elements, if data on the most
+# stable isotope is available, is given.  Otherwise, the user must specify the
+# desired isotope.  
+
+!include elements.units
+
+# Density of the elements
+#
+# Note some elements occur in multiple forms (allotropes) with different
+# densities, and they are accordingly listed multiple times.  
+
+# Density of gas phase elements at STP
+
+hydrogendensity            0.08988 g/l    
+heliumdensity              0.1786 g/l     
+neondensity                0.9002 g/l     
+nitrogendensity            1.2506 g/l     
+oxygendensity              1.429 g/l      
+fluorinedensity            1.696 g/l      
+argondensity               1.784 g/l      
+chlorinedensity            3.2 g/l        
+kryptondensity             3.749 g/l      
+xenondensity               5.894 g/l      
+radondensity               9.73 g/l       
+
+# Density of liquid phase elements near room temperature
+
+brominedensity             3.1028 g/cm^3  
+mercurydensity            13.534 g/cm^3  
+
+# Density of solid elements near room temperature
+
+lithiumdensity             0.534 g/cm^3  
+potassiumdensity           0.862 g/cm^3  
+sodiumdensity              0.968 g/cm^3  
+rubidiumdensity            1.532 g/cm^3  
+calciumdensity             1.55 g/cm^3   
+magnesiumdensity           1.738 g/cm^3  
+phosphorus_white_density   1.823 g/cm^3  
+berylliumdensity           1.85 g/cm^3   
+sulfur_gamma_density       1.92 g/cm^3   
+cesiumdensity              1.93 g/cm^3
+carbon_amorphous_density   1.95 g/cm^3   # average value
+sulfur_betadensity         1.96 g/cm^3   
+sulfur_alpha_density       2.07 g/cm^3   
+carbon_graphite_density    2.267 g/cm^3  
+phosphorus_red_density     2.27 g/cm^3   # average value
+silicondensity             2.3290 g/cm^3 
+phosphorus_violet_density  2.36 g/cm^3   
+borondensity               2.37 g/cm^3   
+strontiumdensity           2.64 g/cm^3   
+phosphorus_black_density   2.69 g/cm^3   
+aluminumdensity            2.7 g/cm^3    
+bariumdensity              3.51 g/cm^3   
+carbon_diamond_density     3.515 g/cm^3  
+scandiumdensity            3.985 g/cm^3  
+selenium_vitreous_density  4.28 g/cm^3   
+selenium_alpha_density     4.39 g/cm^3   
+titaniumdensity            4.406 g/cm^3  
+yttriumdensity             4.472 g/cm^3  
+selenium_gray_density      4.81 g/cm^3   
+iodinedensity              4.933 g/cm^3  
+europiumdensity            5.264 g/cm^3  
+germaniumdensity           5.323 g/cm^3  
+radiumdensity              5.5 g/cm^3    
+arsenicdensity             5.727 g/cm^3  
+tin_alpha_density          5.769 g/cm^3  
+galliumdensity             5.91 g/cm^3   
+vanadiumdensity            6.11 g/cm^3   
+lanthanumdensity           6.162 g/cm^3  
+telluriumdensity           6.24 g/cm^3   
+zirconiumdensity           6.52 g/cm^3   
+antimonydensity            6.697 g/cm^3  
+ceriumdensity              6.77 g/cm^3   
+praseodymiumdensity        6.77 g/cm^3   
+ytterbiumdensity           6.9 g/cm^3    
+neodymiumdensity           7.01 g/cm^3   
+zincdensity                7.14 g/cm^3   
+chromiumdensity            7.19 g/cm^3   
+manganesedensity           7.21 g/cm^3   
+promethiumdensity          7.26 g/cm^3   
+tin_beta_density           7.265 g/cm^3  
+indiumdensity              7.31 g/cm^3   
+samariumdensity            7.52 g/cm^3   
+irondensity                7.874 g/cm^3  
+gadoliniumdensity          7.9 g/cm^3    
+terbiumdensity             8.23 g/cm^3   
+dysprosiumdensity          8.54 g/cm^3   
+niobiumdensity             8.57 g/cm^3   
+cadmiumdensity             8.65 g/cm^3   
+holmiumdensity             8.79 g/cm^3   
+cobaltdensity              8.9 g/cm^3    
+nickeldensity              8.908 g/cm^3  
+erbiumdensity              9.066 g/cm^3  
+polonium_alpha_density     9.196 g/cm^3  
+thuliumdensity             9.32 g/cm^3   
+polonium_beta_density      9.398 g/cm^3  
+bismuthdensity             9.78 g/cm^3   
+lutetiumdensity            9.841 g/cm^3  
+actiniumdensity           10 g/cm^3      
+molybdenumdensity         10.28 g/cm^3   
+silverdensity             10.49 g/cm^3   
+technetiumdensity         11 g/cm^3      
+leaddensity               11.34 g/cm^3   
+thoriumdensity            11.7 g/cm^3    
+thalliumdensity           11.85 g/cm^3   
+americiumdensity          12 g/cm^3      
+palladiumdensity          12.023 g/cm^3  
+rhodiumdensity            12.41 g/cm^3   
+rutheniumdensity          12.45 g/cm^3   
+berkelium_beta_density    13.25 g/cm^3   
+hafniumdensity            13.31 g/cm^3   
+curiumdensity             13.51 g/cm^3   
+berkelium_alphadensity    14.78 g/cm^3   
+californiumdensity        15.1 g/cm^3    
+protactiniumdensity       15.37 g/cm^3   
+tantalumdensity           16.69 g/cm^3   
+uraniumdensity            19.1 g/cm^3    
+tungstendensity           19.3 g/cm^3    
+golddensity               19.30 g/cm^3   
+plutoniumdensity          19.816 g/cm^3  
+neptuniumdensity          20.45 g/cm^3 # alpha form, only one at room temp
+rheniumdensity            21.02 g/cm^3   
+platinumdensity           21.45 g/cm^3   
+iridiumdensity            22.56 g/cm^3   
+osmiumdensity             22.59 g/cm^3
+
+# A few alternate names
+
+tin_gray tin_alpha_density
+tin_white tin_beta_density
+graphitedensity carbon_graphite_density
+diamonddensity carbon_diamond_density
+
+# Predicted density of elements that have not been made in sufficient
+# quantities for measurement.  
+
+franciumdensity            2.48 g/cm^3 # liquid, predicted melting point 8 degC
+astatinedensity            6.35 g/cm^3    
+einsteiniumdensity         8.84 g/cm^3    
+fermiumdensity             9.7 g/cm^3     
+nobeliumdensity            9.9 g/cm^3     
+mendeleviumdensity        10.3 g/cm^3    
+lawrenciumdensity         16 g/cm^3      
+rutherfordiumdensity      23.2 g/cm^3
+roentgeniumdensity        28.7 g/cm^3    
+dubniumdensity            29.3 g/cm^3    
+darmstadtiumdensity       34.8 g/cm^3    
+seaborgiumdensity         35 g/cm^3      
+bohriumdensity            37.1 g/cm^3    
+meitneriumdensity         37.4 g/cm^3    
+hassiumdensity            41 g/cm^3      
+
+#
+# population units
+#
+
+people                  1
+person                  people
+death                   people
+capita                  people
+percapita               per capita
+
+# TGM dozen based unit system listed on the "dozenal" forum
+# http://www.dozenalsociety.org.uk/apps/tgm.htm.  These units are
+# proposed as an allegedly more rational alternative to the SI system.
+
+Tim                     12^-4 hour         # Time 
+Grafut                  gravity Tim^2      # Length based on gravity
+Surf                    Grafut^2           # area
+Volm                    Grafut^3           # volume
+Vlos                    Grafut/Tim         # speed
+Denz                    Maz/Volm           # density
+Mag                     Maz gravity        # force
+Maz                     Volm kg / oldliter # mass based on water
+
+# Abbreviations
+
+# Tm                      Tim              # Conflicts with Tm = Terameter
+Gf                      Grafut
+Sf                      Surf
+Vm                      Volm
+Vl                      Vlos
+Mz                      Maz
+Dz                      Denz
+
+# Dozen based unit prefixes
+
+Zena-                   12
+Duna-                   12^2
+Trina-                  12^3
+Quedra-                 12^4
+Quena-                  12^5
+Hesa-                   12^6
+Seva-                   12^7
+Aka-                    12^8
+Neena-                  12^9
+Dexa-                   12^10
+Lefa-                   12^11
+Zennila-                12^12
+
+Zeni-                   12^-1
+Duni-                   12^-2
+Trini-                  12^-3
+Quedri-                 12^-4
+Queni-                  12^-5
+Hesi-                   12^-6
+Sevi-                   12^-7
+Aki-                    12^-8
+Neeni-                  12^-9
+Dexi-                   12^-10
+Lefi-                   12^-11
+Zennili-                12^-12
+
+#
+# Traditional Japanese units (shakkanhou)
+#
+# The traditional system of weights and measures is called shakkanhou from the
+# shaku and the ken.  Japan accepted SI units in 1891 and legalized conversions
+# to the traditional system.  In 1909 the inch-pound system was also legalized,
+# so Japan had three legally approved systems.  A change to the metric system
+# started in 1921 but there was a lot of resistance.  The Measurement Law of
+# October 1999 prohibits sales in anything but SI units.  However, the old
+# units still live on in construction and as the basis for paper sizes of books
+# and tools used for handicrafts.
+#
+# Note that units below use the Hepburn romanization system.  Some other
+# systems would render "mou", "jou", and "chou" as "mo", "jo" and "cho".
+#
+#
+# http://hiramatu-hifuka.com/onyak/onyindx.html
+
+# Japanese Proportions.  These are still in everyday use.  They also
+# get used as units to represent the proportion of the standard unit.
+
+wari_proportion      1|10
+wari                 wari_proportion
+bu_proportion        1|100    # The character bu can also be read fun or bun
+                              # but usually "bu" is used for units.
+rin_proportion       1|1000
+mou_proportion       1|10000
+
+
+# Japanese Length Measures
+#
+# The length system is called kanejaku or
+# square and originated in China.  It was
+# adopted as Japan's official measure in 701
+# by the Taiho Code.  This system is still in
+# common use in architecture and clothing.
+
+shaku              1|3.3 m
+mou                1|10000 shaku
+rin                1|1000 shaku
+bu_distance        1|100 shaku
+sun                1|10 shaku
+jou_distance       10 shaku
+jou                jou_distance
+
+kanejakusun        sun      # Alias to emphasize architectural name
+kanejaku           shaku
+kanejakujou        jou
+
+# http://en.wikipedia.org/wiki/Taiwanese_units_of_measurement
+taichi             shaku   # http://zh.wikipedia.org/wiki/台尺
+taicun             sun     # http://zh.wikipedia.org/wiki/台制
+!utf8
+台尺               taichi  # via Hanyu Pinyin romanizations
+台寸               taicun
+!endutf8
+
+# In context of clothing, shaku is different from architecture
+
+kujirajaku         10|8 shaku
+kujirajakusun      1|10 kujirajaku
+kujirajakubu       1|100 kujirajaku
+kujirajakujou      10 kujirajaku
+tan_distance       3 kujirajakujou
+
+ken                6 shaku  # Also sometimes 6.3, 6.5, or 6.6
+                            # http://www.homarewood.co.jp/syakusun.htm
+
+# mostly unused
+chou_distance      60 ken
+chou               chou_distance
+ri                 36 chou
+
+# Japanese Area Measures
+
+# Tsubo is still used for land size, though the others are more
+# recognized by their homonyms in the other measurements.
+
+gou_area             1|10 tsubo
+tsubo                36 shaku^2    # Size of two tatami = ken^2 ??
+se                   30 tsubo
+tan_area             10 se
+chou_area            10 tan_area
+
+# http://en.wikipedia.org/wiki/Taiwanese_units_of_measurement
+ping                 tsubo     # http://zh.wikipedia.org/wiki/坪
+jia                  2934 ping # http://zh.wikipedia.org/wiki/甲_(单位)
+fen                  1|10 jia  # http://zh.wikipedia.org/wiki/分
+fen_area             1|10 jia  # Protection against future collisions
+!utf8
+坪                   ping      # via Hanyu Pinyin romanizations
+甲                   jia
+分                   fen
+分地                 fen_area  # Protection against future collisions
+!endutf8
+
+# Japanese architecture is based on a "standard" size of tatami mat.
+# Room sizes today are given in number of tatami, and this number
+# determines the spacing between colums and hence sizes of sliding
+# doors and paper screens.  However, every region has its own slightly
+# different tatami size.  Edoma, used in and around Tokyo and
+# Hokkaido, is becoming a nationwide standard.  Kyouma is used around
+# Kyoto, Osaka and Kyuushu, and Chuukyouma is used around Nagoya.
+# Note that the tatami all have the aspect ratio 2:1 so that the mats
+# can tile the room with some of them turned 90 degrees.
+#
+# http://www.moon2.net/tatami/infotatami/structure.html
+
+edoma                (5.8*2.9) shaku^2
+kyouma               (6.3*3.15) shaku^2
+chuukyouma           (6*3) shaku^2
+jou_area             edoma
+tatami               jou_area
+
+# Japanese Volume Measures
+
+# The "shou" is still used for such things as alcohol and seasonings.
+# Large quantities of paint are still purchased in terms of "to".
+
+shaku_volume         1|10 gou_volume
+gou_volume           1|10 shou
+gou                  gou_volume
+shou                 (4.9*4.9*2.7) sun^3   # The character shou which is
+                                           # the same as masu refers to a
+                                           # rectangular wooden cup used to
+                                           # measure liquids and cereal.
+                                           # Sake is sometimes served in a masu
+                                           # Note that it happens to be
+                                           # EXACTLY 7^4/11^3 liters.
+to                   10 shou
+koku                 10 to  # No longer used; historically a measure of rice
+
+# Japanese Weight Measures
+#
+# https://web.archive.org/web/20040927115452/http://wyoming.hp.infoseek.co.jp/zatugaku/zamoney.html
+# https://en.wikipedia.org/wiki/Japanese_units_of_measurement
+
+# Not really used anymore.
+
+rin_weight           1|10 bu_weight
+bu_weight            1|10 monme
+fun                  1|10 monme
+monme                momme
+kin                  160 monme
+kan                  1000 monme
+kwan                 kan         # This was the old pronunciation of the unit.
+                                 # The old spelling persisted a few centuries
+                                 # longer and was not changed until around
+                                 # 1950.
+
+# http://en.wikipedia.org/wiki/Taiwanese_units_of_measurement
+# says: "Volume measure in Taiwan is largely metric".
+taijin               kin      # http://zh.wikipedia.org/wiki/台斤
+tailiang             10 monme # http://zh.wikipedia.org/wiki/台斤
+taiqian              monme    # http://zh.wikipedia.org/wiki/台制
+!utf8
+台斤                 taijin # via Hanyu Pinyin romanizations
+台兩                 tailiang
+台錢                 taiqian
+!endutf8
+
+#
+# Australian unit
+#
+
+australiasquare         (10 ft)^2   # Used for house area
+
+
+#
+# A few German units as currently in use.
+#
+
+zentner                 50 kg
+doppelzentner           2 zentner
+pfund                   500 g
+
+# The klafter, which was used in central Europe, was derived from the span of
+# outstretched arms.
+#
+# https://en.wikipedia.org/wiki/Obsolete_Austrian_units_of_measurement
+# https://www.llv.li/files/abi/klafter-m2-en.pdf
+
+austriaklafter          1.89648384 m    # Exact definition, 23 July 1871 
+austriafoot             1|6 austriaklafter
+prussiaklafter          1.88 m
+prussiafoot             1|6 prussiaklafter
+bavariaklafter          1.751155 m
+bavariafoot             1|6 bavariaklafter
+hesseklafter            2.5 m
+hessefoot               1|6 hesseklafter
+switzerlandklafter      metricklafter
+switzerlandfoot         1|6 switzerlandklafter
+swissklafter            switzerlandklafter
+swissfoot               1|6 swissklafter
+metricklafter           1.8 m
+
+austriayoke             8 austriaklafter * 200 austriaklafter
+
+liechtensteinsquareklafter 3.596652 m^2 # Used until 2017 to measure land area 
+liechtensteinklafter  sqrt(liechtensteinsquareklafter)
+
+# The klafter was also used to measure volume of wood, generally being a stack
+# of wood one klafter wide, one klafter long, with logs 3 feet (half a klafter)
+# in length
+
+prussiawoodklafter      0.5 prussiaklafter^3
+austriawoodklafter      0.5 austriaklafter^3
+festmeter               m^3             # modern measure of wood, solid cube
+raummeter               0.7 festmeter   # Air space between the logs, stacked
+schuettraummeter        0.65 raummeter  # A cubic meter volume of split and cut
+                                        #   firewood in a loose, unordered
+                                        #   pile, not stacked.  This is called
+                                        #   "tipped".
+!utf8
+schüttraummeter         schuettraummeter
+!endutf8
+
+
+#
+# Swedish (Sweden) pre-metric units of 1739.
+# The metric system was adopted in 1878.
+# https://sv.wikipedia.org/wiki/Verkm%C3%A5tt
+#
+
+verklinje               2.0618125 mm
+verktum                 12 verklinje
+kvarter                 6 verktum
+fot                     2 kvarter
+aln                     2 fot
+famn                    3 aln
+
+#
+# Some traditional Russian measures
+#
+# If you would like to help expand this section and understand
+# cyrillic transliteration, let me know.  These measures are meant to
+# reflect common usage, e.g. in translated literature.  
+#
+
+dessiatine              2400 sazhen^2    # Land measure
+dessjatine              dessiatine
+
+funt                    409.51718 grams  # similar to pound
+zolotnik                1|96 funt        # used for precious metal measure
+pood                    40 funt          # common in agricultural measure
+
+arshin                  (2 + 1|3) feet
+sazhen                  3 arshin         # analogous to fathom
+verst                   500 sazhen       # of similar use to mile
+versta                  verst
+borderverst             1000 sazhen
+russianmile             7 verst
+
+
+
+
+#
+# Old French distance measures, from French Weights and Measures
+# Before the Revolution by Zupko
+#
+
+frenchfoot              144|443.296 m     # pied de roi, the standard of Paris.
+pied                    frenchfoot        #   Half of the hashimicubit,
+frenchfeet              frenchfoot        #   instituted by Charlemagne.
+frenchinch              1|12 frenchfoot   #   This exact definition comes from
+frenchthumb             frenchinch        #   a law passed on 10 Dec 1799 which
+pouce                   frenchthumb       #   fixed the meter at
+                                          #   3 frenchfeet + 11.296 lignes.
+frenchline              1|12 frenchinch   # This is supposed to be the size
+ligne                   frenchline        #   of the average barleycorn
+frenchpoint             1|12 frenchline
+toise                   6 frenchfeet
+arpent                  180^2 pied^2      # The arpent is 100 square perches,
+                                          # but the perche seems to vary a lot
+                                          # and can be 18 feet, 20 feet, or 22
+                                          # feet.  This measure was described
+                                          # as being in common use in Canada in
+                                          # 1934 (Websters 2nd).  The value
+                                          # given here is the Paris standard
+                                          # arpent.
+frenchgrain             1|18827.15 kg     # Weight of a wheat grain, hence
+                                          # smaller than the British grain.
+frenchpound             9216 frenchgrain
+
+#
+# Before the Imperial Weights and Measures Act of 1824, various different
+# weights and measures were in use in different places.
+#
+
+# Scots linear measure
+
+scotsinch        1.00540054 UKinch
+scotslink        1|100 scotschain
+scotsfoot        12 scotsinch
+scotsfeet        scotsfoot
+scotsell         37 scotsinch
+scotsfall        6 scotsell
+scotschain       4 scotsfall
+scotsfurlong     10 scotschain
+scotsmile        8 scotsfurlong
+
+# Scots area measure
+
+scotsrood        40 scotsfall^2
+scotsacre        4 scotsrood
+
+# Irish linear measure
+
+irishinch       UKinch
+irishpalm       3 irishinch
+irishspan       3 irishpalm
+irishfoot       12 irishinch
+irishfeet       irishfoot
+irishcubit      18 irishinch
+irishyard       3 irishfeet
+irishpace       5 irishfeet
+irishfathom     6 irishfeet
+irishpole       7 irishyard      # Only these values
+irishperch      irishpole        # are different from
+irishchain      4 irishperch     # the British Imperial
+irishlink       1|100 irishchain # or English values for
+irishfurlong    10 irishchain    # these lengths.
+irishmile       8 irishfurlong   #
+
+#  Irish area measure
+
+irishrood       40 irishpole^2
+irishacre       4 irishrood
+
+# English wine capacity measures (Winchester measures)
+
+winepint       1|2 winequart
+winequart      1|4 winegallon
+winegallon     231 UKinch^3   # Sometimes called the Winchester Wine Gallon,
+                              # it was legalized in 1707 by Queen Anne, and
+                              # given the definition of 231 cubic inches.  It
+                              # had been in use for a while as 8 pounds of wine
+                              # using a merchant's pound, but the definition of
+                              # the merchant's pound had become uncertain.  A
+                              # pound of 15 tower ounces (6750 grains) had been
+                              # common, but then a pound of 15 troy ounces
+                              # (7200 grains) gained popularity.  Because of
+                              # the switch in the value of the merchants pound,
+                              # the size of the wine gallon was uncertain in
+                              # the market, hence the official act in 1707.
+                              # The act allowed that a six inch tall cylinder
+                              # with a 7 inch diameter was a lawful wine
+                              # gallon.  (This comes out to 230.9 in^3.)
+                              # Note also that in Britain a legal conversion
+                              # was established to the 1824 Imperial gallon
+                              # then taken as 277.274 in^3 so that the wine
+                              # gallon was 0.8331 imperial gallons.  This is
+                              # 231.1 cubic inches (using the international
+                              # inch).
+winerundlet    18 winegallon
+winebarrel     31.5 winegallon
+winetierce     42 winegallon
+winehogshead   2 winebarrel
+winepuncheon   2 winetierce
+winebutt       2 winehogshead
+winepipe       winebutt
+winetun        2 winebutt
+
+# English beer and ale measures used 1803-1824 and used for beer before 1688
+
+beerpint       1|2 beerquart
+beerquart      1|4 beergallon
+beergallon     282 UKinch^3
+beerbarrel     36 beergallon
+beerhogshead   1.5 beerbarrel
+
+# English ale measures used from 1688-1803 for both ale and beer
+
+alepint        1|2 alequart
+alequart       1|4 alegallon
+alegallon      beergallon
+alebarrel      34 alegallon
+alehogshead    1.5 alebarrel
+
+# Scots capacity measure
+
+scotsgill      1|4 mutchkin
+mutchkin       1|2 choppin
+choppin        1|2 scotspint
+scotspint      1|2 scotsquart
+scotsquart     1|4 scotsgallon
+scotsgallon    827.232 UKinch^3
+scotsbarrel    8 scotsgallon
+jug            scotspint
+
+# Scots dry capacity measure
+
+scotswheatlippy   137.333 UKinch^3    # Also used for peas, beans, rye, salt
+scotswheatlippies scotswheatlippy
+scotswheatpeck    4 scotswheatlippy
+scotswheatfirlot  4 scotswheatpeck
+scotswheatboll    4 scotswheatfirlot
+scotswheatchalder 16 scotswheatboll
+
+scotsoatlippy     200.345 UKinch^3    # Also used for barley and malt
+scotsoatlippies   scotsoatlippy
+scotsoatpeck      4 scotsoatlippy
+scotsoatfirlot    4 scotsoatpeck
+scotsoatboll      4 scotsoatfirlot
+scotsoatchalder   16 scotsoatboll
+
+# Scots Tron weight
+
+trondrop       1|16 tronounce
+tronounce      1|20 tronpound
+tronpound      9520 grain
+tronstone      16 tronpound
+
+# Irish liquid capacity measure
+
+irishnoggin    1|4 irishpint
+irishpint      1|2 irishquart
+irishquart     1|2 irishpottle
+irishpottle    1|2 irishgallon
+irishgallon    217.6 UKinch^3
+irishrundlet   18 irishgallon
+irishbarrel    31.5 irishgallon
+irishtierce    42 irishgallon
+irishhogshead  2 irishbarrel
+irishpuncheon  2 irishtierce
+irishpipe      2 irishhogshead
+irishtun       2 irishpipe
+
+# Irish dry capacity measure
+
+irishpeck      2 irishgallon
+irishbushel    4 irishpeck
+irishstrike    2 irishbushel
+irishdrybarrel 2 irishstrike
+irishquarter   2 irishbarrel
+
+# English Tower weights, abolished in 1528
+
+towerpound       5400 grain
+towerounce       1|12 towerpound
+towerpennyweight 1|20 towerounce
+towergrain       1|32 towerpennyweight
+
+# English Mercantile weights, used since the late 12th century
+
+mercpound      6750 grain
+mercounce      1|15 mercpound
+mercpennyweight 1|20 mercounce
+
+# English weights for lead
+
+leadstone     12.5 lb
+fotmal        70 lb
+leadwey       14 leadstone
+fothers       12 leadwey
+
+# English Hay measure
+
+newhaytruss 60 lb             # New and old here seem to refer to "new"
+newhayload  36 newhaytruss    # hay and "old" hay rather than a new unit
+oldhaytruss 56 lb             # and an old unit.
+oldhayload  36 oldhaytruss
+
+# English wool measure
+
+woolclove   7 lb
+woolstone   2 woolclove
+wooltod     2 woolstone
+woolwey     13 woolstone
+woolsack    2 woolwey
+woolsarpler 2 woolsack
+woollast    6 woolsarpler
+
+#
+# Ancient history units:  There tends to be uncertainty in the definitions
+#                         of the units in this section
+# These units are from [11]
+
+# Roman measure.  The Romans had a well defined distance measure, but their
+# measures of weight were poor.  They adopted local weights in different
+# regions without distinguishing among them so that there are half a dozen
+# different Roman "standard" weight systems.
+
+romanfoot    296 mm          # There is some uncertainty in this definition
+romanfeet    romanfoot       # from which all the other units are derived.
+pes          romanfoot       # This value appears in numerous sources. In "The
+pedes        romanfoot       # Roman Land Surveyors", Dilke gives 295.7 mm.
+romaninch    1|12 romanfoot  # The subdivisions of the Roman foot have the
+romandigit   1|16 romanfoot  #   same names as the subdivisions of the pound,
+romanpalm    1|4 romanfoot   #   but we can't have the names for different
+romancubit   18 romaninch    #   units.
+romanpace    5 romanfeet     # Roman double pace (basic military unit)
+passus       romanpace
+romanperch   10 romanfeet
+stade        125 romanpaces
+stadia       stade
+stadium      stade
+romanmile    8 stadia        # 1000 paces
+romanleague  1.5 romanmile
+schoenus     4 romanmile
+
+# Other values for the Roman foot (from Dilke)
+
+earlyromanfoot    29.73 cm
+pesdrusianus      33.3 cm    # or 33.35 cm, used in Gaul & Germany in 1st c BC
+lateromanfoot     29.42 cm
+
+# Roman areas
+
+actuslength  120 romanfeet     # length of a Roman furrow
+actus        120*4 romanfeet^2 # area of the furrow
+squareactus  120^2 romanfeet^2 # actus quadratus
+acnua        squareactus
+iugerum      2 squareactus
+iugera       iugerum
+jugerum      iugerum
+jugera       iugerum
+heredium     2 iugera          # heritable plot
+heredia      heredium
+centuria     100 heredia
+centurium    centuria
+
+# Roman volumes
+
+sextarius       35.4 in^3      # Basic unit of Roman volume.  As always,
+sextarii        sextarius      # there is uncertainty.  Six large Roman
+                               # measures survive with volumes ranging from
+                               # 34.4 in^3 to 39.55 in^3.  Three of them
+                               # cluster around the size given here.
+                               #
+                               # But the values for this unit vary wildly
+                               # in other sources.  One reference  gives 0.547
+                               # liters, but then says the amphora is a
+                               # cubic Roman foot.  This gives a value for the
+                               # sextarius of 0.540 liters.  And the
+                               # encyclopedia Britannica lists 0.53 liters for
+                               # this unit.  Both [7] and [11], which were
+                               # written by scholars of weights and measures,
+                               # give the value of 35.4 cubic inches.
+cochlearia      1|48 sextarius
+cyathi          1|12 sextarius
+acetabula       1|8 sextarius
+quartaria       1|4 sextarius
+quartarius      quartaria
+heminae         1|2 sextarius
+hemina          heminae
+cheonix         1.5 sextarii
+
+# Dry volume measures (usually)
+
+semodius        8 sextarius
+semodii         semodius
+modius          16 sextarius
+modii           modius
+
+# Liquid volume measures (usually)
+
+congius         12 heminae
+congii          congius
+amphora         8 congii
+amphorae        amphora      # Also a dry volume measure
+culleus         20 amphorae
+quadrantal      amphora
+
+# Roman weights
+
+libra           5052 grain   # The Roman pound varied significantly
+librae          libra        # from 4210 grains to 5232 grains.  Most of
+romanpound      libra        # the standards were obtained from the weight
+uncia           1|12 libra   # of particular coins.  The one given here is
+unciae          uncia        # based on the Gold Aureus of Augustus which
+romanounce      uncia        # was in use from BC 27 to AD 296.
+deunx           11 uncia
+dextans         10 uncia
+dodrans         9 uncia
+bes             8 uncia
+seprunx         7 uncia
+semis           6 uncia
+quincunx        5 uncia
+triens          4 uncia
+quadrans        3 uncia
+sextans         2 uncia
+sescuncia       1.5 uncia
+semuncia        1|2 uncia
+siscilius       1|4 uncia
+sextula         1|6 uncia
+semisextula     1|12 uncia
+scriptulum      1|24 uncia
+scrupula        scriptulum
+romanobol       1|2 scrupula
+
+romanaspound    4210 grain    # Old pound based on bronze coinage, the
+                              # earliest money of Rome BC 338 to BC 268.
+
+# Egyptian length measure
+
+egyptianroyalcubit      20.63 in    # plus or minus .2 in
+egyptianpalm            1|7 egyptianroyalcubit
+egyptiandigit           1|4 egyptianpalm
+egyptianshortcubit      6 egyptianpalm
+
+doubleremen             29.16 in  # Length of the diagonal of a square with
+remendigit       1|40 doubleremen # side length of 1 royal egyptian cubit.
+                                  # This is divided into 40 digits which are
+                                  # not the same size as the digits based on
+                                  # the royal cubit.
+
+# Greek length measures
+
+greekfoot               12.45 in      # Listed as being derived from the
+greekfeet               greekfoot     # Egyptian Royal cubit in [11].  It is
+greekcubit              1.5 greekfoot # said to be 3|5 of a 20.75 in cubit.
+pous                    greekfoot
+podes                   greekfoot
+orguia                  6 greekfoot
+greekfathom             orguia
+stadion                 100 orguia
+akaina                  10 greekfeet
+plethron                10 akaina
+greekfinger             1|16 greekfoot
+homericcubit            20 greekfingers  # Elbow to end of knuckles.
+shortgreekcubit         18 greekfingers  # Elbow to start of fingers.
+
+ionicfoot               296 mm
+doricfoot               326 mm
+
+olympiccubit            25 remendigit    # These olympic measures were not as
+olympicfoot             2|3 olympiccubit # common as the other greek measures.
+olympicfinger           1|16 olympicfoot # They were used in agriculture.
+olympicfeet             olympicfoot
+olympicdakylos          olympicfinger
+olympicpalm             1|4 olympicfoot
+olympicpalestra         olympicpalm
+olympicspithame         3|4 foot
+olympicspan             olympicspithame
+olympicbema             2.5 olympicfeet
+olympicpace             olympicbema
+olympicorguia           6 olympicfeet
+olympicfathom           olympicorguia
+olympiccord             60 olympicfeet
+olympicamma             olympiccord
+olympicplethron         100 olympicfeet
+olympicstadion          600 olympicfeet
+
+# Greek capacity measure
+
+greekkotyle             270 ml           # This approximate value is obtained
+xestes                  2 greekkotyle    # from two earthenware vessels that
+khous                   12 greekkotyle   # were reconstructed from fragments.
+metretes                12 khous         # The kotyle is a day's corn ration
+choinix                 4 greekkotyle    # for one man.
+hekteos                 8 choinix
+medimnos                6 hekteos
+
+# Greek weight.  Two weight standards were used, an Aegina standard based
+# on the Beqa shekel and an Athens (attic) standard.
+
+aeginastater            192 grain        # Varies up to 199 grain
+aeginadrachmae          1|2 aeginastater
+aeginaobol              1|6 aeginadrachmae
+aeginamina              50 aeginastaters
+aeginatalent            60 aeginamina    # Supposedly the mass of a cubic foot
+                                         # of water (whichever foot was in use)
+
+atticstater             135 grain        # Varies 134-138 grain
+atticdrachmae           1|2 atticstater
+atticobol               1|6 atticdrachmae
+atticmina               50 atticstaters
+attictalent             60 atticmina     # Supposedly the mass of a cubic foot
+                                         # of water (whichever foot was in use)
+
+# "Northern" cubit and foot.  This was used by the pre-Aryan civilization in
+# the Indus valley.  It was used in Mesopotamia, Egypt, North Africa, China,
+# central and Western Europe until modern times when it was displaced by
+# the metric system.
+
+northerncubit           26.6 in           # plus/minus .2 in
+northernfoot            1|2 northerncubit
+
+sumeriancubit           495 mm
+kus                     sumeriancubit
+sumerianfoot            2|3 sumeriancubit
+
+assyriancubit           21.6 in
+assyrianfoot            1|2 assyriancubit
+assyrianpalm            1|3 assyrianfoot
+assyriansusi            1|20 assyrianpalm
+susi                    assyriansusi
+persianroyalcubit       7 assyrianpalm
+
+
+# Arabic measures.  The arabic standards were meticulously kept.  Glass weights
+# accurate to .2 grains were made during AD 714-900.
+
+hashimicubit            25.56 in          # Standard of linear measure used
+                                          # in Persian dominions of the Arabic
+                                          # empire 7-8th cent.  Is equal to two
+                                          # French feet.
+
+blackcubit              21.28 in
+arabicfeet              1|2 blackcubit
+arabicfoot              arabicfeet
+arabicinch              1|12 arabicfoot
+arabicmile              4000 blackcubit
+
+silverdirhem            45 grain  # The weights were derived from these two
+tradedirhem             48 grain  # units with two identically named systems
+                                  # used for silver and used for trade purposes
+
+silverkirat             1|16 silverdirhem
+silverwukiyeh           10 silverdirhem
+silverrotl              12 silverwukiyeh
+arabicsilverpound       silverrotl
+
+tradekirat              1|16 tradedirhem
+tradewukiyeh            10 tradedirhem
+traderotl               12 tradewukiyeh
+arabictradepound        traderotl
+
+# Miscellaneous ancient units
+
+parasang                3.5 mile # Persian unit of length usually thought
+                                 # to be between 3 and 3.5 miles
+biblicalcubit           21.8 in
+hebrewcubit             17.58 in
+li                      10|27.8 mile  # Chinese unit of length
+                                      #   100 li is considered a day's march
+liang                   11|3 oz       # Chinese weight unit
+
+
+# Medieval time units.  According to the OED, these appear in Du Cange
+# by Papias.
+
+timepoint               1|5 hour  # also given as 1|4
+timeminute              1|10 hour
+timeostent              1|60 hour
+timeounce               1|8 timeostent
+timeatom                1|47 timeounce
+
+# Given in [15], these subdivisions of the grain were supposedly used
+# by jewelers.  The mite may have been used but the blanc could not
+# have been accurately measured.
+
+mite                    1|20 grain
+droit                   1|24 mite
+periot                  1|20 droit
+blanc                   1|24 periot
+
+#
+# Localization
+#
+
+!var UNITS_ENGLISH US
+hundredweight           ushundredweight
+ton                     uston
+scruple                 apscruple
+fluidounce              usfluidounce
+gallon                  usgallon
+bushel                  usbushel
+quarter                 quarterweight
+cup                     uscup
+tablespoon              ustablespoon
+teaspoon                usteaspoon
+dollar                  US$
+cent                    $ 0.01
+penny                   cent
+minim                   minimvolume
+pony                    ponyvolume
+grand                   usgrand
+firkin                  usfirkin
+hogshead                ushogshead
+cable                   uscable
+!endvar
+
+!var UNITS_ENGLISH GB
+hundredweight           brhundredweight
+ton                     brton
+scruple                 brscruple
+fluidounce              brfluidounce
+gallon                  brgallon
+bushel                  brbushel
+quarter                 brquarter
+chaldron                brchaldron
+cup                     brcup
+teacup                  brteacup
+tablespoon              brtablespoon
+teaspoon                brteaspoon
+dollar                  US$
+cent                    $ 0.01
+penny                   brpenny
+minim                   minimnote
+pony                    brpony
+grand                   brgrand
+firkin                  brfirkin
+hogshead                brhogshead
+cable                   brcable
+!endvar
+
+!varnot UNITS_ENGLISH GB US
+!message Unknown value for environment variable UNITS_ENGLISH.  Should be GB or US.
+!endvar
+
+
+!utf8
+⅛-                      1|8
+¼-                      1|4
+⅜-                      3|8
+½-                      1|2
+⅝-                      5|8
+¾-                      3|4
+⅞-                      7|8
+⅙-                      1|6
+⅓-                      1|3
+⅔-                      2|3
+⅚-                      5|6
+⅕-                      1|5
+⅖-                      2|5
+⅗-                      3|5
+⅘-                      4|5
+# U+2150-               1|7  For some reason these characters are getting
+# U+2151-               1|9  flagged as invalid UTF8.
+# U+2152-               1|10
+#⅐-               1|7   # fails under MacOS
+#⅑-               1|9   # fails under MacOS
+#⅒-               1|10  # fails under MacOS
+ℯ                       exp(1)      # U+212F, base of natural log
+µ-                      micro       # micro sign U+00B5
+μ-                      micro       # small mu U+03BC
+ångström                angstrom
+Å                       angstrom    # angstrom symbol U+212B
+Å                       angstrom    # A with ring U+00C5
+röntgen                 roentgen
+°C                      degC
+°F                      degF
+°K                      K           # °K is incorrect notation
+°R                      degR
+°                       degree
+℃                       degC
+℉                       degF
+K                       K          # Kelvin symbol, U+212A
+ℓ                       liter      # unofficial abbreviation used in some places
+Ω                       ohm       # Ohm symbol U+2126
+Ω                       ohm       # Greek capital omega U+03A9
+℧                       mho
+G₀                      G0
+H₀                      H0              
+Z₀                      Z0
+a₀                      a0
+n₀                      n0
+ε₀                      epsilon0
+μ₀                      mu0
+Φ₀                      Phi0
+R∞                      Rinfinity
+R_∞                     Rinfinity
+λ_C                     lambda_C
+μ_B                     mu_B
+ν_133Cs                 nu_133Cs  
+ʒ                        dram     # U+0292
+℈                       scruple
+℥                       ounce
+℔                       lb
+ℎ                       h
+ℏ                       hbar
+τ                       tau    
+π                       pi      # Greek letter pi
+𝜋                       pi      # mathematical italic small pi
+α                       alpha  
+σ                       sigma  
+‰                       1|1000
+‱                       1|10000
+′                       '        # U+2032
+″                       "        # U+2033
+
+#
+# Unicode currency symbols
+#
+
+¢                       cent
+£                       britainpound
+¥                       japanyen
+€                       euro
+₩                       southkoreawon
+₪                       israelnewshekel
+₤                       lira
+# ₺                       turkeylira  # fails under MacOS
+₨                       rupee           # unofficial legacy rupee sign
+# ₹                       indiarupee      # official rupee sign # MacOS fail
+#؋                       afghanafghani    # fails under MacOS
+฿                       thailandbaht
+₡                       costaricacolon
+₣                       francefranc
+₦                       nigerianaira
+₧                       spainpeseta
+₫                       vietnamdong
+₭                       laokip 
+₮                       mongoliatugrik
+₯                       greecedrachma
+₱                       philippinepeso
+# ₲                       paraguayguarani # fails under MacOS
+#₴                       ukrainehryvnia   # fails under MacOS
+#₵                       ghanacedi        # fails under MacOS
+#₸                       kazakhstantenge  # fails under MacOS
+#₼                       azerbaijanmanat # fails under MacOS
+#₽                       russiaruble     # fails under MacOS
+#₾                       georgialari     # fails under MacOS
+﷼                       iranrial
+﹩                      $
+¢                      ¢
+£                      £
+¥                      ¥
+₩                      ₩
+
+#
+# Square Unicode symbols starting at U+3371
+#
+
+㍱                      hPa
+㍲                      da
+㍳                      au
+㍴                      bar
+# ㍵                          oV???
+㍶                      pc
+#㍷                      dm      invalid on Mac
+#㍸                      dm^2    invalid on Mac
+#㍹                      dm^3    invalid on Mac
+㎀                      pA
+㎁                      nA
+㎂                      µA
+㎃                      mA
+㎄                      kA
+㎅                      kB
+㎆                      MB
+㎇                      GB
+㎈                      cal
+㎉                      kcal
+㎊                      pF
+㎋                      nF
+㎌                      µF
+㎍                      µg
+㎎                      mg
+㎏                      kg
+㎐                      Hz
+㎑                      kHz
+㎒                      MHz
+㎓                      GHz
+㎔                      THz
+㎕                      µL
+㎖                      mL
+㎗                      dL
+㎘                      kL
+㎙                      fm
+㎚                      nm
+㎛                      µm
+㎜                      mm
+㎝                      cm
+㎞                      km
+㎟                      mm^2
+㎠                      cm^2
+㎡                      m^2
+㎢                      km^2
+㎣                      mm^3
+㎤                      cm^3
+㎥                      m^3
+㎦                      km^3
+㎧                      m/s
+㎨                      m/s^2
+㎩                      Pa
+㎪                      kPa
+㎫                      MPa
+㎬                      GPa
+㎭                      rad
+㎮                      rad/s
+㎯                      rad/s^2
+㎰                      ps
+㎱                      ns
+㎲                      µs
+㎳                      ms
+㎴                      pV
+㎵                      nV
+㎶                      µV
+㎷                      mV
+㎸                      kV
+㎹                      MV
+㎺                      pW
+㎻                      nW
+㎼                      µW
+㎽                      mW
+㎾                      kW
+㎿                      MW
+㏀                      kΩ
+㏁                      MΩ
+㏃                      Bq
+㏄                      cc
+㏅                      cd
+㏆                      C/kg
+㏈()                    dB
+㏉                      Gy
+㏊                      ha
+# ㏋  HP??
+㏌                      in
+# ㏍                      KK??
+# ㏎                      KM???
+㏏                      kt
+㏐                      lm
+# ㏑                      ln
+# ㏒                      log
+㏓                      lx
+㏔                      mb
+㏕                      mil
+㏖                      mol
+㏗()                    pH
+㏙                      ppm
+#   ㏚     PR???
+㏛                      sr
+㏜                      Sv
+㏝                      Wb
+#㏞                      V/m     Invalid on Mac
+#㏟                      A/m     Invalid on Mac
+#㏿                      gal     Invalid on Mac
+
+!endutf8
+
+############################################################################
+#
+# Unit list aliases
+#
+# These provide a shorthand for conversions to unit lists.
+#
+############################################################################
+
+!unitlist uswt lb;oz
+!unitlist hms hr;min;sec
+!unitlist time year;day;hr;min;sec
+!unitlist dms deg;arcmin;arcsec
+!unitlist ftin ft;in;1|8 in
+!unitlist inchfine in;1|8 in;1|16 in;1|32 in;1|64 in
+!unitlist usvol cup;3|4 cup;2|3 cup;1|2 cup;1|3 cup;1|4 cup;\
+                tbsp;tsp;1|2 tsp;1|4 tsp;1|8 tsp
+
+############################################################################
+#
+# The following units were in the Unix units database but do not appear in
+# this file:
+#
+#      wey        used for cheese, salt and other goods.  Measured mass or
+#      waymass    volume depending on what was measured and where the measuring
+#                 took place.  A wey of cheese ranged from 200 to 324 pounds.
+#
+#      sack       No precise definition
+#
+#      spindle    The length depends on the type of yarn
+#
+#      block      Defined variously on different computer systems
+#
+#      erlang     A unit of telephone traffic defined variously.
+#                 Omitted because there are no other units for this
+#                 dimension.  Is this true?  What about CCS = 1/36 erlang?
+#                 Erlang is supposed to be dimensionless.  One erlang means
+#                 a single channel occupied for one hour.
+#
+############################################################################
+#
+# The following have been suggested or considered and deemed out of scope.
+# They will not be added to GNU units.  
+#
+# Conversions between different calendar systems used in different countries or
+# different historical periods are out of scope for units and will not be added.
+#
+# Wind chill and heat index cannot be handled because they are bivarite,
+# with dependence on both the temperature and wind speed or humidity.  
+#
+# Plain english text output like "one hectare is equivalent to one hundred
+# million square centimeters" is out of scope.  
+#
diff --git a/dehtml b/dehtml
new file mode 100755
index 0000000..6c4d870
--- /dev/null
+++ b/dehtml
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+{
+  local $/=undef;
+  open FILE, $ARGV[0] or die "Could not open file";
+  $data = <FILE>;
+  close FILE;
+}
+
+$data =~ s/<.*?>//sg;
+$data =~ s/&.*?;//sg;
+print $data;
diff --git a/elemcvt.sh b/elemcvt.sh
new file mode 100755
index 0000000..98e8d7f
--- /dev/null
+++ b/elemcvt.sh
@@ -0,0 +1,504 @@
+#! /bin/sh
+# ******************************************************************************
+#  elemcvt: convert NIST "Linear ASCII" table of elements to units(1) format
+#  Usage: elemcvt [options] [<file>]
+#  Author: Jeff Conrad
+#  Date:  2024-01-06
+# ******************************************************************************
+
+# Adjust PATH to suit.
+# For Windows w/MKS Toolkit, this assumes /bin is a symbolic link to
+# $ROOTDIR/mksnt.
+
+PATH=/bin
+
+progname=${0##*[/\\]}
+progname=${progname%.sh}
+export TITLEBAR=$progname
+
+umsg="Usage: $progname [options] [file]
+Options:
+  -d  Show elements for which no standard atomic mass is given and exit
+  -v  Verbose"
+
+show_no_std_atomic_mass=	# show elements for which no std atomic mass is given
+verbose=
+errors=
+DUALCASE=1	# used in MKS Toolkit to make options case sensitive
+
+while getopts :dv arg
+do
+    case $arg in
+    d)
+	show_no_std_atomic_mass=YES ;;
+    v)
+	verbose=YES ;;
+    :)
+	# OPTARG contains the option missing the argument
+	print -ru2 -- "$progname: option $OPTARG requires an argument"
+	errors=YES
+	;;
+    [?])
+	# OPTARG contains the invalid option
+	print -ru2 -- "$progname: unknown option $OPTARG"
+	errors=YES
+	;;
+    esac
+done
+shift $((OPTIND - 1))
+unset DUALCASE
+
+if [ -n "$errors" ]
+then
+    print -ru2 -- "$umsg"
+    exit 1
+fi
+
+awk '
+
+function show_element_info(atomic_number, atomic_symbol, std_atomic_mass_str)
+{
+    printf("# %s: %s (%d)", names[atomic_number], atomic_symbol, atomic_number)
+    if (std_atomic_mass_str)
+	printf("  std atomic weight: %s", std_atomic_mass_str)
+    print ""
+}
+
+# <name>_<atomic num>  <mass> # <mole fraction>
+function show_isotope(name, num)
+{
+    printf("%-*s%*s%*.*f", max_isotope_len, sprintf("%s_%d", name, num),
+	    sepwid, " ", isoprecis + 4, isoprecis, mass[num])
+    if (composition[num])
+	printf("   # %.*f", compprecis, composition[num])
+    print ""
+}
+
+function show_element_name(name)
+{
+    printf("%-*s%*s", max_name_len, name, sepwid, " ")
+}
+
+# <mole fraction> <name>_<mass num>
+function mole_fraction(atomic_number, names, mass_num,  mass_wid)
+{
+    if (composition[mass_num] == 1) {
+	mass_wid = length(int(mass_number[n_isotopes]))
+	# align with 1st digit of atomic mass
+	printf("%*s%s_%d", sepwid + 2 - mass_wid, " ", names[atomic_number], mass_num)
+    }
+    else
+	printf("%.*f %s_%d", compprecis, composition[mass_num],
+		names[atomic_number], mass_num)
+}
+
+# add line continuation and '+' sign
+function add_continuation()
+{
+    printf(" \\\n")
+    printf("%-*s+ ", max_name_len + sepwid - 2, " ")
+}
+
+# <name>_<mass num> # most stable
+function use_most_stable(atomic_number, mass,  mass_wid)
+{
+    mass_wid = length(int(mass_number[n_isotopes]))
+    printf("%*s%-*s   # most stable", sepwid + 2 - mass_wid, " ",
+	    isoprecis + 1 + mass_wid, sprintf("%s_%d", names[atomic_number], mass))
+}
+
+# <name>_<mass num> # standard atomic mass
+function use_std_mass(atomic_number, mass,  mass_wid)
+{
+    mass_wid = length(int(mass_number[n_isotopes]))
+    printf("%*s%-*s   # standard atomic mass", sepwid + 2 - mass_wid, " ",
+	    isoprecis + 1 + mass_wid, sprintf("%s_%d", names[atomic_number], mass))
+}
+
+# show isotopes and the sum of mole fraction-mass products
+function show_element_data(atomic_number, names)
+{
+    # isotopes and relative abundances
+    for (ndx = 1; ndx <= n_isotopes; ndx++) {
+	mass_num = mass_number[ndx]
+	show_isotope(names[atomic_number], mass_num)
+    }
+
+    # show a value for atomic mass if one of these is available;
+    # otherwise, show only isotope masses.
+    if (total_composition > 0 || most_stable_mass || std_atomic_mass)
+	show_element = 1
+    else
+	show_element = 0
+
+    # atomic mass: sum of mole fraction-mass products
+    # element name
+    if (show_element)
+	show_element_name(names[atomic_number])
+
+    mass_num = mass_number[1]
+    firstval = 0
+
+    # first isotope
+    if (composition[mass_num] > 0) {
+	mole_fraction(atomic_number, names, mass_num)
+	firstval = 1
+    }
+    if (n_isotopes > 1) {
+	for (ndx = 2; ndx < n_isotopes; ndx++) {
+	    mass_num = mass_number[ndx]
+	    if (composition[mass_num] > 0) {
+		if (firstval == 1)
+		    add_continuation()
+		mole_fraction(atomic_number, names, mass_num)
+		firstval = 1
+	    }
+	}
+	# last isotope
+	mass_num = mass_number[n_isotopes]
+	if (composition[mass_num] > 0) {
+	    if (firstval == 1)
+		add_continuation()
+	    mole_fraction(atomic_number, names, mass_num)
+	    print ""
+	}
+    }
+    else
+	print ""
+
+    # options if mole fraction is not given for any isotope
+    if (total_composition == 0) {
+	if (most_stable_mass)
+	    use_most_stable(atomic_number, most_stable_mass)
+	else if (std_atomic_mass)
+	    use_std_mass(atomic_number, std_atomic_mass)
+    }
+
+    if (! composition[mass_number[n_isotopes]])
+	print ""
+}
+
+function output(atomic_number)
+{
+    sepwid = 5		# width of column separation
+    compprecis = 8	# for mole fraction
+    isoprecis = 10	# for isotope mass
+
+    # NIST show H, D, and T
+    if (atomic_number == 1)
+	atomic_symbol = "H"
+
+    show_element_info(atomic_number, atomic_symbol, std_atomic_mass_str)
+
+    if (am_names[atomic_number])
+	print "# IUPAC spelling"
+
+    show_element_data(atomic_number, names)
+
+    # show American spelling if different from IUPAC
+    if (am_names[atomic_number]) {
+	print "# American spelling"
+	show_element_data(atomic_number, am_names)
+    }
+    if (show_element)
+	print ""	# blank line between elements
+
+    most_stable_mass = 0
+    total_composition = 0
+}
+
+function gnu_notes()
+{
+    print "\
+# This file is the elements database for use with GNU units, a units\n\
+# conversion program by Adrian Mariano adrianm@gnu.org\n\
+#\n\
+# January 2024 Version 1.0\n\
+#\n\
+# Copyright (C) 2024\n\
+# Free Software Foundation, Inc\n\
+#\n\
+# This program is free software; you can redistribute it and/or modify\n\
+# it under the terms of the GNU General Public License as published by\n\
+# the Free Software Foundation; either version 3 of the License, or\n\
+# (at your option) any later version.\n\
+#\n\
+# This data is distributed in the hope that it will be useful,\n\
+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
+# GNU General Public License for more details.\n\
+#\n\
+# You should have received a copy of the GNU General Public License\n\
+# along with this program; if not, write to the Free Software\n\
+# Foundation, Inc., 51 Franklin Street, Fifth Floor,\n\
+# Boston, MA  02110-1301  USA\n"
+}
+
+function nist_notes()
+{
+    print "# From https://www.nist.gov/pml/atomic-weights-and-isotopic-compositions-relative-atomic-masses\n"
+
+    # notes from https://www.nist.gov/pml/atomic-weights-and-isotopic-compositions-column-descriptions
+    print "\
+#   For several elements, the standard atomic weight A_r is given as an\n\
+#   atomic-weight interval with the symbol [a,b] to denote the set of\n\
+#   atomic-weight values in normal materials; thus, [a <= A_r(E) <= b].\n\
+#   The symbols a and b denote the lower and upper bounds of the\n\
+#   interval [a,b], respectively.  The values in parentheses, following\n\
+#   the last significant digit to which they are attributed, are\n\
+#   uncertainties.\n\
+#\n\
+#   Brackets [ ] enclosing a single value indicate the mass number of\n\
+#   the most stable isotope.  For radioactive elements with atomic\n\
+#   numbers 95 or greater, the mass number of the most stable isotope is\n\
+#   not specified, as the list of studied isotopes is still\n\
+#   incomplete.\n"
+}
+
+function units_notes()
+{
+    print "\
+# When composition mole fractions of isotopes are given, the atomic mass\n\
+# of an element is given as the sum of the product(s) of mole\n\
+# fraction(s) and the atomic masses of the relevant isotopes.  When composition\n\
+# mole fractions are not given, the atomic mass is given as\n\
+#\n\
+#   * the mass of the most stable isotope, if available, or\n\
+#   * the standard atomic mass of the element, if available.\n\
+#\n\
+# If neither the most stable isotope nore a standard atomic mass is\n\
+# available, no atomic mass for the element is given; the user must\n\
+# select the isotope most suitable for their purposes.\n\
+#\n\
+# If the standard atomic mass is a range, the value given is the\n\
+# midpoint of that range, which may differ from the value determined\n\
+# from the sum of the products of composition mole fraction and isotope\n\
+# atomic mass.\n"
+}
+
+BEGIN {
+    FS = " *= *"
+    show_no_std_atomic_mass = "'"$show_no_std_atomic_mass"'"
+    verbose = "'"$verbose"'"
+    console = "/dev/console"
+
+    # IUPAC spellings
+    names[1] = "hydrogen"
+    names[2] = "helium"
+    names[3] = "lithium"
+    names[4] = "beryllium"
+    names[5] = "boron"
+    names[6] = "carbon"
+    names[7] = "nitrogen"
+    names[8] = "oxygen"
+    names[9] = "fluorine"
+    names[10] = "neon"
+    names[11] = "sodium"
+    names[12] = "magnesium"
+    names[13] = "aluminium"
+    names[14] = "silicon"
+    names[15] = "phosphorus"
+    names[16] = "sulfur"
+    names[17] = "chlorine"
+    names[18] = "argon"
+    names[19] = "potassium"
+    names[20] = "calcium"
+    names[21] = "scandium"
+    names[22] = "titanium"
+    names[23] = "vanadium"
+    names[24] = "chromium"
+    names[25] = "manganese"
+    names[26] = "iron"
+    names[27] = "cobalt"
+    names[28] = "nickel"
+    names[29] = "copper"
+    names[30] = "zinc"
+    names[31] = "gallium"
+    names[32] = "germanium"
+    names[33] = "arsenic"
+    names[34] = "selenium"
+    names[35] = "bromine"
+    names[36] = "krypton"
+    names[37] = "rubidium"
+    names[38] = "strontium"
+    names[39] = "yttrium"
+    names[40] = "zirconium"
+    names[41] = "niobium"
+    names[42] = "molybdenum"
+    names[43] = "technetium"
+    names[44] = "ruthenium"
+    names[45] = "rhodium"
+    names[46] = "palladium"
+    names[47] = "silver"
+    names[48] = "cadmium"
+    names[49] = "indium"
+    names[50] = "tin"
+    names[51] = "antimony"
+    names[52] = "tellurium"
+    names[53] = "iodine"
+    names[54] = "xenon"
+    names[55] = "caesium"
+    names[56] = "barium"
+    names[57] = "lanthanum"
+    names[58] = "cerium"
+    names[59] = "praseodymium"
+    names[60] = "neodymium"
+    names[61] = "promethium"
+    names[62] = "samarium"
+    names[63] = "europium"
+    names[64] = "gadolinium"
+    names[65] = "terbium"
+    names[66] = "dysprosium"
+    names[67] = "holmium"
+    names[68] = "erbium"
+    names[69] = "thulium"
+    names[70] = "ytterbium"
+    names[71] = "lutetium"
+    names[72] = "hafnium"
+    names[73] = "tantalum"
+    names[74] = "tungsten"
+    names[75] = "rhenium"
+    names[76] = "osmium"
+    names[77] = "iridium"
+    names[78] = "platinum"
+    names[79] = "gold"
+    names[80] = "mercury"
+    names[81] = "thallium"
+    names[82] = "lead"
+    names[83] = "bismuth"
+    names[84] = "polonium"
+    names[85] = "astatine"
+    names[86] = "radon"
+    names[87] = "francium"
+    names[88] = "radium"
+    names[89] = "actinium"
+    names[90] = "thorium"
+    names[91] = "protactinium"
+    names[92] = "uranium"
+    names[93] = "neptunium"
+    names[94] = "plutonium"
+    names[95] = "americium"
+    names[96] = "curium"
+    names[97] = "berkelium"
+    names[98] = "californium"
+    names[99] = "einsteinium"
+    names[100] = "fermium"
+    names[101] = "mendelevium"
+    names[102] = "nobelium"
+    names[103] = "lawrencium"
+    names[104] = "rutherfordium"
+    names[105] = "dubnium"
+    names[106] = "seaborgium"
+    names[107] = "bohrium"
+    names[108] = "hassium"
+    names[109] = "meitnerium"
+    names[110] = "darmstadtium"
+    names[111] = "roentgenium"
+    names[112] = "copernicium"
+    names[113] = "nihonium"
+    names[114] = "flerovium"
+    names[115] = "moscovium"
+    names[116] = "livermorium"
+    names[117] = "tennessine"
+    names[118] = "oganesson"
+
+    # American spellings
+    am_names[13] = "aluminum"
+    am_names[55] = "cesium"
+
+    max_name_len = 0		# length of longest element name
+    for (i = 1; i <= 118; i++) {
+	len = length(names[i])
+	if (len > max_name_len) {
+	    max_name_len = len;
+	    longestname = names[i]
+	}
+    }
+    max_isotope_len = max_name_len + 4	# allow for "_xxx" suffix
+
+    if (! show_no_std_atomic_mass) {
+	gnu_notes()
+	nist_notes()
+	units_notes()
+    }
+
+    if (verbose)
+	printf("Longest element name: %s (%d)\n\n", longestname, max_name_len)
+
+    n_isotopes = 0
+    mass_number[1] = 0
+}
+
+# begin file processing
+
+# skip JavaScript and HTML before data
+NR == 1, $0 ~ /<pre/ { next }
+# skip HTML after data
+$0 ~ /<\/pre>/ { exit }
+
+# remove trailing space and unpaddable spaces
+{ 
+  gsub(/&nbsp;/, "")
+  gsub(/ +$/, "")
+}
+
+$1 ~ /Atomic Number/ {
+    last_atomic_number = atomic_number
+    atomic_number = $2 + 0
+    if (atomic_number != last_atomic_number && atomic_number > 1) {
+	if (show_no_std_atomic_mass) {
+	    if (! std_atomic_mass_str)
+		print names[last_atomic_number]
+	}
+	else
+	    output(last_atomic_number)
+    }
+}
+
+$1 ~ /Atomic Symbol/ {
+    atomic_symbol = $2
+}
+
+$1 ~ /Mass Number/ {
+    if (atomic_number != last_atomic_number) {
+	for (i = 1; i <= n_isotopes; i++)
+	    delete mass_number[i]
+	n_isotopes = 0
+    }
+    mass_number[++n_isotopes] = $2
+}
+
+$1 ~ /Relative Atomic Mass/ {
+    atomic_mass = $2
+    sub(/\([[:digit:]#]+\)/, "", atomic_mass)
+    mass[mass_number[n_isotopes]] = atomic_mass
+}
+
+$1 ~ /Isotopic Composition/ {
+    isotopic_composition = $2
+    sub(/\([[:digit:]#]+\)/, "", isotopic_composition)
+    composition[mass_number[n_isotopes]] = isotopic_composition
+    total_composition += isotopic_composition
+}
+
+$1 ~ /Standard Atomic Weight/ {
+    std_atomic_mass = std_atomic_mass_str = $2
+    gsub(/\([^)]+\)/, "", std_atomic_mass)
+    gsub(/[][]/, "", std_atomic_mass)
+    if (std_atomic_mass ~ /,/) {
+	split(std_atomic_mass, range, /,/)
+	std_atomic_mass = (range[1] + range[2]) / 2
+    }
+    if (std_atomic_mass_str ~ /\[[[:digit:].]+\]/)
+	most_stable_mass = std_atomic_mass
+    last_atomic_number = atomic_number
+}
+
+END {
+    if (show_no_std_atomic_mass) {
+	if (! std_atomic_mass_str)
+	    print names[last_atomic_number]
+    }
+    else
+	output(last_atomic_number)
+} ' $*
diff --git a/elements.units b/elements.units
new file mode 100644
index 0000000..a18169b
--- /dev/null
+++ b/elements.units
@@ -0,0 +1,4011 @@
+# This file is the elements database for use with GNU units, a units
+# conversion program by Adrian Mariano adrianm@gnu.org
+#
+# January 2024 Version 1.0
+#
+# Copyright (C) 2024
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This data is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA  02110-1301  USA
+
+# From https://www.nist.gov/pml/atomic-weights-and-isotopic-compositions-relative-atomic-masses
+
+#   For several elements, the standard atomic weight A_r is given as an
+#   atomic-weight interval with the symbol [a,b] to denote the set of
+#   atomic-weight values in normal materials; thus, [a <= A_r(E) <= b].
+#   The symbols a and b denote the lower and upper bounds of the
+#   interval [a,b], respectively.  The values in parentheses, following
+#   the last significant digit to which they are attributed, are
+#   uncertainties.
+#
+#   Brackets [ ] enclosing a single value indicate the mass number of
+#   the most stable isotope.  For radioactive elements with atomic
+#   numbers 95 or greater, the mass number of the most stable isotope is
+#   not specified, as the list of studied isotopes is still
+#   incomplete.
+
+# When composition mole fractions of isotopes are given, the atomic mass
+# of an element is given as the sum of the product(s) of mole
+# fraction(s) and the atomic masses of the relevant isotopes.  When composition
+# mole fractions are not given, the atomic mass is given as
+#
+#   * the mass of the most stable isotope, if available, or
+#   * the standard atomic mass of the element, if available.
+#
+# If neither the most stable isotope nore a standard atomic mass is
+# available, no atomic mass for the element is given; the user must
+# select the isotope most suitable for their purposes.
+#
+# If the standard atomic mass is a range, the value given is the
+# midpoint of that range, which may differ from the value determined
+# from the sum of the products of composition mole fraction and isotope
+# atomic mass.
+
+# hydrogen: H (1)  std atomic weight: [1.00784,1.00811]
+hydrogen_1              1.0078250322   # 0.99988500
+hydrogen_2              2.0141017781   # 0.00011500
+hydrogen_3              3.0160492779
+hydrogen_4              4.0264300000
+hydrogen_5              5.0353110000
+hydrogen_6              6.0449600000
+hydrogen_7              7.0527000000
+hydrogen          0.99988500 hydrogen_1 \
+                + 0.00011500 hydrogen_2
+
+# helium: He (2)  std atomic weight: 4.002602(2)
+helium_3                3.0160293201   # 0.00000134
+helium_4                4.0026032541   # 0.99999866
+helium_5                5.0120570000
+helium_6                6.0188858910
+helium_7                7.0279907000
+helium_8                8.0339343900
+helium_9                9.0439460000
+helium_10              10.0527900000
+helium            0.00000134 helium_3 \
+                + 0.99999866 helium_4
+
+# lithium: Li (3)  std atomic weight: [6.938,6.997]
+lithium_3               3.0308000000
+lithium_4               4.0271900000
+lithium_5               5.0125380000
+lithium_6               6.0151228874   # 0.07590000
+lithium_7               7.0160034366   # 0.92410000
+lithium_8               8.0224862460
+lithium_9               9.0267901900
+lithium_10             10.0354830000
+lithium_11             11.0437235800
+lithium_12             12.0525170000
+lithium_13             13.0626300000
+lithium           0.07590000 lithium_6 \
+                + 0.92410000 lithium_7
+
+# beryllium: Be (4)  std atomic weight: 9.0121831(5)
+beryllium_5             5.0399000000
+beryllium_6             6.0197264000
+beryllium_7             7.0169287170
+beryllium_8             8.0053051020
+beryllium_9             9.0121830650   # 1.00000000
+beryllium_10           10.0135346950
+beryllium_11           11.0216610800
+beryllium_12           12.0269221000
+beryllium_13           13.0361350000
+beryllium_14           14.0428900000
+beryllium_15           15.0534200000
+beryllium_16           16.0616700000
+beryllium              beryllium_9
+
+# boron: B (5)  std atomic weight: [10.806,10.821]
+boron_6                 6.0508000000
+boron_7                 7.0297120000
+boron_8                 8.0246073000
+boron_9                 9.0133296500
+boron_10               10.0129369500   # 0.19900000
+boron_11               11.0093053600   # 0.80100000
+boron_12               12.0143527000
+boron_13               13.0177802000
+boron_14               14.0254040000
+boron_15               15.0310880000
+boron_16               16.0398420000
+boron_17               17.0469900000
+boron_18               18.0556600000
+boron_19               19.0631000000
+boron_20               20.0720700000
+boron_21               21.0812900000
+boron             0.19900000 boron_10 \
+                + 0.80100000 boron_11
+
+# carbon: C (6)  std atomic weight: [12.0096,12.0116]
+carbon_8                8.0376430000
+carbon_9                9.0310372000
+carbon_10              10.0168533100
+carbon_11              11.0114336000
+carbon_12              12.0000000000   # 0.98930000
+carbon_13              13.0033548351   # 0.01070000
+carbon_14              14.0032419884
+carbon_15              15.0105992600
+carbon_16              16.0147013000
+carbon_17              17.0225770000
+carbon_18              18.0267510000
+carbon_19              19.0348000000
+carbon_20              20.0403200000
+carbon_21              21.0490000000
+carbon_22              22.0575300000
+carbon_23              23.0689000000
+carbon            0.98930000 carbon_12 \
+                + 0.01070000 carbon_13
+
+# nitrogen: N (7)  std atomic weight: [14.00643,14.00728]
+nitrogen_10            10.0416500000
+nitrogen_11            11.0260910000
+nitrogen_12            12.0186132000
+nitrogen_13            13.0057386100
+nitrogen_14            14.0030740044   # 0.99636000
+nitrogen_15            15.0001088989   # 0.00364000
+nitrogen_16            16.0061019000
+nitrogen_17            17.0084490000
+nitrogen_18            18.0140780000
+nitrogen_19            19.0170220000
+nitrogen_20            20.0233660000
+nitrogen_21            21.0271100000
+nitrogen_22            22.0343900000
+nitrogen_23            23.0411400000
+nitrogen_24            24.0503900000
+nitrogen_25            25.0601000000
+nitrogen          0.99636000 nitrogen_14 \
+                + 0.00364000 nitrogen_15
+
+# oxygen: O (8)  std atomic weight: [15.99903,15.99977]
+oxygen_12              12.0342620000
+oxygen_13              13.0248150000
+oxygen_14              14.0085963600
+oxygen_15              15.0030656200
+oxygen_16              15.9949146196   # 0.99757000
+oxygen_17              16.9991317565   # 0.00038000
+oxygen_18              17.9991596129   # 0.00205000
+oxygen_19              19.0035780000
+oxygen_20              20.0040753500
+oxygen_21              21.0086550000
+oxygen_22              22.0099660000
+oxygen_23              23.0156960000
+oxygen_24              24.0198600000
+oxygen_25              25.0293600000
+oxygen_26              26.0372900000
+oxygen_27              27.0477200000
+oxygen_28              28.0559100000
+oxygen            0.99757000 oxygen_16 \
+                + 0.00038000 oxygen_17 \
+                + 0.00205000 oxygen_18
+
+# fluorine: F (9)  std atomic weight: 18.998403163(6)
+fluorine_14            14.0343150000
+fluorine_15            15.0180430000
+fluorine_16            16.0114657000
+fluorine_17            17.0020952400
+fluorine_18            18.0009373300
+fluorine_19            18.9984031627   # 1.00000000
+fluorine_20            19.9999812520
+fluorine_21            20.9999489000
+fluorine_22            22.0029990000
+fluorine_23            23.0035570000
+fluorine_24            24.0081150000
+fluorine_25            25.0121990000
+fluorine_26            26.0200380000
+fluorine_27            27.0264400000
+fluorine_28            28.0353400000
+fluorine_29            29.0425400000
+fluorine_30            30.0516500000
+fluorine_31            31.0597100000
+fluorine               fluorine_19
+
+# neon: Ne (10)  std atomic weight: 20.1797(6)
+neon_16                16.0257500000
+neon_17                17.0177139600
+neon_18                18.0057087000
+neon_19                19.0018809100
+neon_20                19.9924401762   # 0.90480000
+neon_21                20.9938466850   # 0.00270000
+neon_22                21.9913851140   # 0.09250000
+neon_23                22.9944669100
+neon_24                23.9936106500
+neon_25                24.9977890000
+neon_26                26.0005150000
+neon_27                27.0075530000
+neon_28                28.0121200000
+neon_29                29.0197500000
+neon_30                30.0247300000
+neon_31                31.0331000000
+neon_32                32.0397200000
+neon_33                33.0493800000
+neon_34                34.0567300000
+neon              0.90480000 neon_20 \
+                + 0.00270000 neon_21 \
+                + 0.09250000 neon_22
+
+# sodium: Na (11)  std atomic weight: 22.98976928(2)
+sodium_18              18.0268800000
+sodium_19              19.0138800000
+sodium_20              20.0073544000
+sodium_21              20.9976546900
+sodium_22              21.9944374100
+sodium_23              22.9897692820   # 1.00000000
+sodium_24              23.9909629500
+sodium_25              24.9899540000
+sodium_26              25.9926346000
+sodium_27              26.9940765000
+sodium_28              27.9989390000
+sodium_29              29.0028771000
+sodium_30              30.0090979000
+sodium_31              31.0131630000
+sodium_32              32.0201900000
+sodium_33              33.0257300000
+sodium_34              34.0335900000
+sodium_35              35.0406200000
+sodium_36              36.0492900000
+sodium_37              37.0570500000
+sodium                 sodium_23
+
+# magnesium: Mg (12)  std atomic weight: [24.304,24.307]
+magnesium_19           19.0341690000
+magnesium_20           20.0188500000
+magnesium_21           21.0117160000
+magnesium_22           21.9995706500
+magnesium_23           22.9941242100
+magnesium_24           23.9850416970   # 0.78990000
+magnesium_25           24.9858369760   # 0.10000000
+magnesium_26           25.9825929680   # 0.11010000
+magnesium_27           26.9843406240
+magnesium_28           27.9838767000
+magnesium_29           28.9886170000
+magnesium_30           29.9904629000
+magnesium_31           30.9966480000
+magnesium_32           31.9991102000
+magnesium_33           33.0053271000
+magnesium_34           34.0089350000
+magnesium_35           35.0167900000
+magnesium_36           36.0218800000
+magnesium_37           37.0303700000
+magnesium_38           38.0365800000
+magnesium_39           39.0453800000
+magnesium_40           40.0521800000
+magnesium         0.78990000 magnesium_24 \
+                + 0.10000000 magnesium_25 \
+                + 0.11010000 magnesium_26
+
+# aluminium: Al (13)  std atomic weight: 26.9815385(7)
+# IUPAC spelling
+aluminium_21           21.0289700000
+aluminium_22           22.0195400000
+aluminium_23           23.0072443500
+aluminium_24           23.9999489000
+aluminium_25           24.9904281000
+aluminium_26           25.9868919040
+aluminium_27           26.9815385300   # 1.00000000
+aluminium_28           27.9819102100
+aluminium_29           28.9804565000
+aluminium_30           29.9829600000
+aluminium_31           30.9839450000
+aluminium_32           31.9880850000
+aluminium_33           32.9909090000
+aluminium_34           33.9967050000
+aluminium_35           34.9997640000
+aluminium_36           36.0063900000
+aluminium_37           37.0105300000
+aluminium_38           38.0174000000
+aluminium_39           39.0225400000
+aluminium_40           40.0300300000
+aluminium_41           41.0363800000
+aluminium_42           42.0438400000
+aluminium_43           43.0514700000
+aluminium              aluminium_27
+# American spelling
+aluminum_21            21.0289700000
+aluminum_22            22.0195400000
+aluminum_23            23.0072443500
+aluminum_24            23.9999489000
+aluminum_25            24.9904281000
+aluminum_26            25.9868919040
+aluminum_27            26.9815385300   # 1.00000000
+aluminum_28            27.9819102100
+aluminum_29            28.9804565000
+aluminum_30            29.9829600000
+aluminum_31            30.9839450000
+aluminum_32            31.9880850000
+aluminum_33            32.9909090000
+aluminum_34            33.9967050000
+aluminum_35            34.9997640000
+aluminum_36            36.0063900000
+aluminum_37            37.0105300000
+aluminum_38            38.0174000000
+aluminum_39            39.0225400000
+aluminum_40            40.0300300000
+aluminum_41            41.0363800000
+aluminum_42            42.0438400000
+aluminum_43            43.0514700000
+aluminum               aluminum_27
+
+# silicon: Si (14)  std atomic weight: [28.084,28.086]
+silicon_22             22.0357900000
+silicon_23             23.0254400000
+silicon_24             24.0115350000
+silicon_25             25.0041090000
+silicon_26             25.9923338400
+silicon_27             26.9867048100
+silicon_28             27.9769265347   # 0.92223000
+silicon_29             28.9764946649   # 0.04685000
+silicon_30             29.9737701360   # 0.03092000
+silicon_31             30.9753631940
+silicon_32             31.9741515400
+silicon_33             32.9779769600
+silicon_34             33.9785760000
+silicon_35             34.9845830000
+silicon_36             35.9866950000
+silicon_37             36.9929210000
+silicon_38             37.9955230000
+silicon_39             39.0024910000
+silicon_40             40.0058300000
+silicon_41             41.0130100000
+silicon_42             42.0177800000
+silicon_43             43.0248000000
+silicon_44             44.0306100000
+silicon_45             45.0399500000
+silicon           0.92223000 silicon_28 \
+                + 0.04685000 silicon_29 \
+                + 0.03092000 silicon_30
+
+# phosphorus: P (15)  std atomic weight: 30.973761998(5)
+phosphorus_24          24.0357700000
+phosphorus_25          25.0211900000
+phosphorus_26          26.0117800000
+phosphorus_27          26.9992240000
+phosphorus_28          27.9923266000
+phosphorus_29          28.9818007900
+phosphorus_30          29.9783137500
+phosphorus_31          30.9737619984   # 1.00000000
+phosphorus_32          31.9739076430
+phosphorus_33          32.9717257000
+phosphorus_34          33.9736458900
+phosphorus_35          34.9733141000
+phosphorus_36          35.9782600000
+phosphorus_37          36.9796070000
+phosphorus_38          37.9842520000
+phosphorus_39          38.9862270000
+phosphorus_40          39.9913300000
+phosphorus_41          40.9946540000
+phosphorus_42          42.0010800000
+phosphorus_43          43.0050200000
+phosphorus_44          44.0112100000
+phosphorus_45          45.0164500000
+phosphorus_46          46.0244600000
+phosphorus_47          47.0313900000
+phosphorus             phosphorus_31
+
+# sulfur: S (16)  std atomic weight: [32.059,32.076]
+sulfur_26              26.0290700000
+sulfur_27              27.0182800000
+sulfur_28              28.0043700000
+sulfur_29              28.9966110000
+sulfur_30              29.9849070300
+sulfur_31              30.9795570100
+sulfur_32              31.9720711744   # 0.94990000
+sulfur_33              32.9714589098   # 0.00750000
+sulfur_34              33.9678670040   # 0.04250000
+sulfur_35              34.9690323100
+sulfur_36              35.9670807100   # 0.00010000
+sulfur_37              36.9711255100
+sulfur_38              37.9711633000
+sulfur_39              38.9751340000
+sulfur_40              39.9754826000
+sulfur_41              40.9795935000
+sulfur_42              41.9810651000
+sulfur_43              42.9869076000
+sulfur_44              43.9901188000
+sulfur_45              44.9957200000
+sulfur_46              46.0000400000
+sulfur_47              47.0079500000
+sulfur_48              48.0137000000
+sulfur_49              49.0227600000
+sulfur            0.94990000 sulfur_32 \
+                + 0.00750000 sulfur_33 \
+                + 0.04250000 sulfur_34 \
+                + 0.00010000 sulfur_36
+
+# chlorine: Cl (17)  std atomic weight: [35.446,35.457]
+chlorine_28            28.0295400000
+chlorine_29            29.0147800000
+chlorine_30            30.0047700000
+chlorine_31            30.9924140000
+chlorine_32            31.9856846400
+chlorine_33            32.9774519900
+chlorine_34            33.9737624850
+chlorine_35            34.9688526820   # 0.75760000
+chlorine_36            35.9683068090
+chlorine_37            36.9659026020   # 0.24240000
+chlorine_38            37.9680104400
+chlorine_39            38.9680082000
+chlorine_40            39.9704150000
+chlorine_41            40.9706850000
+chlorine_42            41.9732500000
+chlorine_43            42.9738900000
+chlorine_44            43.9778700000
+chlorine_45            44.9802900000
+chlorine_46            45.9851700000
+chlorine_47            46.9891600000
+chlorine_48            47.9956400000
+chlorine_49            49.0012300000
+chlorine_50            50.0090500000
+chlorine_51            51.0155400000
+chlorine          0.75760000 chlorine_35 \
+                + 0.24240000 chlorine_37
+
+# argon: Ar (18)  std atomic weight: 39.948(1)
+argon_30               30.0230700000
+argon_31               31.0121200000
+argon_32               31.9976378000
+argon_33               32.9899255500
+argon_34               33.9802700900
+argon_35               34.9752575900
+argon_36               35.9675451050   # 0.00333600
+argon_37               36.9667763300
+argon_38               37.9627321100   # 0.00062900
+argon_39               38.9643130000
+argon_40               39.9623831237   # 0.99603500
+argon_41               40.9645005700
+argon_42               41.9630457000
+argon_43               42.9656361000
+argon_44               43.9649238000
+argon_45               44.9680397300
+argon_46               45.9680830000
+argon_47               46.9729350000
+argon_48               47.9759100000
+argon_49               48.9819000000
+argon_50               49.9861300000
+argon_51               50.9937000000
+argon_52               51.9989600000
+argon_53               53.0072900000
+argon             0.00333600 argon_36 \
+                + 0.00062900 argon_38 \
+                + 0.99603500 argon_40
+
+# potassium: K (19)  std atomic weight: 39.0983(1)
+potassium_32           32.0226500000
+potassium_33           33.0075600000
+potassium_34           33.9986900000
+potassium_35           34.9880054100
+potassium_36           35.9813020100
+potassium_37           36.9733758900
+potassium_38           37.9690811200
+potassium_39           38.9637064864   # 0.93258100
+potassium_40           39.9639981660   # 0.00011700
+potassium_41           40.9618252579   # 0.06730200
+potassium_42           41.9624023100
+potassium_43           42.9607347000
+potassium_44           43.9615869900
+potassium_45           44.9606914900
+potassium_46           45.9619815900
+potassium_47           46.9616616000
+potassium_48           47.9653411900
+potassium_49           48.9682107500
+potassium_50           49.9723800000
+potassium_51           50.9758280000
+potassium_52           51.9822400000
+potassium_53           52.9874600000
+potassium_54           53.9946300000
+potassium_55           55.0007600000
+potassium_56           56.0085100000
+potassium         0.93258100 potassium_39 \
+                + 0.00011700 potassium_40 \
+                + 0.06730200 potassium_41
+
+# calcium: Ca (20)  std atomic weight: 40.078(4)
+calcium_34             34.0148700000
+calcium_35             35.0051400000
+calcium_36             35.9930740000
+calcium_37             36.9858978500
+calcium_38             37.9763192200
+calcium_39             38.9707108100
+calcium_40             39.9625908630   # 0.96941000
+calcium_41             40.9622779200
+calcium_42             41.9586178300   # 0.00647000
+calcium_43             42.9587664400   # 0.00135000
+calcium_44             43.9554815600   # 0.02086000
+calcium_45             44.9561863500
+calcium_46             45.9536890000   # 0.00004000
+calcium_47             46.9545424000
+calcium_48             47.9525227600   # 0.00187000
+calcium_49             48.9556627400
+calcium_50             49.9574992000
+calcium_51             50.9609890000
+calcium_52             51.9632170000
+calcium_53             52.9694500000
+calcium_54             53.9734000000
+calcium_55             54.9803000000
+calcium_56             55.9850800000
+calcium_57             56.9926200000
+calcium_58             57.9979400000
+calcium           0.96941000 calcium_40 \
+                + 0.00647000 calcium_42 \
+                + 0.00135000 calcium_43 \
+                + 0.02086000 calcium_44 \
+                + 0.00004000 calcium_46 \
+                + 0.00187000 calcium_48
+
+# scandium: Sc (21)  std atomic weight: 44.955908(5)
+scandium_36            36.0164800000
+scandium_37            37.0037400000
+scandium_38            37.9951200000
+scandium_39            38.9847850000
+scandium_40            39.9779673000
+scandium_41            40.9692511050
+scandium_42            41.9655165300
+scandium_43            42.9611505000
+scandium_44            43.9594029000
+scandium_45            44.9559082800   # 1.00000000
+scandium_46            45.9551682600
+scandium_47            46.9524037000
+scandium_48            47.9522236000
+scandium_49            48.9500146000
+scandium_50            49.9521760000
+scandium_51            50.9535920000
+scandium_52            51.9568800000
+scandium_53            52.9590900000
+scandium_54            53.9639300000
+scandium_55            54.9678200000
+scandium_56            55.9734500000
+scandium_57            56.9777700000
+scandium_58            57.9840300000
+scandium_59            58.9889400000
+scandium_60            59.9956500000
+scandium_61            61.0010000000
+scandium               scandium_45
+
+# titanium: Ti (22)  std atomic weight: 47.867(1)
+titanium_38            38.0114500000
+titanium_39            39.0023600000
+titanium_40            39.9905000000
+titanium_41            40.9831480000
+titanium_42            41.9730490300
+titanium_43            42.9685225000
+titanium_44            43.9596899500
+titanium_45            44.9581219800
+titanium_46            45.9526277200   # 0.08250000
+titanium_47            46.9517587900   # 0.07440000
+titanium_48            47.9479419800   # 0.73720000
+titanium_49            48.9478656800   # 0.05410000
+titanium_50            49.9447868900   # 0.05180000
+titanium_51            50.9466106500
+titanium_52            51.9468930000
+titanium_53            52.9497300000
+titanium_54            53.9510500000
+titanium_55            54.9552700000
+titanium_56            55.9579100000
+titanium_57            56.9636400000
+titanium_58            57.9666000000
+titanium_59            58.9724700000
+titanium_60            59.9760300000
+titanium_61            60.9824500000
+titanium_62            61.9865100000
+titanium_63            62.9937500000
+titanium          0.08250000 titanium_46 \
+                + 0.07440000 titanium_47 \
+                + 0.73720000 titanium_48 \
+                + 0.05410000 titanium_49 \
+                + 0.05180000 titanium_50
+
+# vanadium: V (23)  std atomic weight: 50.9415(1)
+vanadium_40            40.0127600000
+vanadium_41            41.0002100000
+vanadium_42            41.9918200000
+vanadium_43            42.9807660000
+vanadium_44            43.9741100000
+vanadium_45            44.9657748000
+vanadium_46            45.9601987800
+vanadium_47            46.9549049100
+vanadium_48            47.9522522000
+vanadium_49            48.9485118000
+vanadium_50            49.9471560100   # 0.00250000
+vanadium_51            50.9439570400   # 0.99750000
+vanadium_52            51.9447730100
+vanadium_53            52.9443367000
+vanadium_54            53.9464390000
+vanadium_55            54.9472400000
+vanadium_56            55.9504800000
+vanadium_57            56.9525200000
+vanadium_58            57.9567200000
+vanadium_59            58.9593900000
+vanadium_60            59.9643100000
+vanadium_61            60.9672500000
+vanadium_62            61.9726500000
+vanadium_63            62.9763900000
+vanadium_64            63.9826400000
+vanadium_65            64.9875000000
+vanadium_66            65.9939800000
+vanadium          0.00250000 vanadium_50 \
+                + 0.99750000 vanadium_51
+
+# chromium: Cr (24)  std atomic weight: 51.9961(6)
+chromium_42            42.0067000000
+chromium_43            42.9975300000
+chromium_44            43.9853600000
+chromium_45            44.9790500000
+chromium_46            45.9683590000
+chromium_47            46.9628974000
+chromium_48            47.9540291000
+chromium_49            48.9513333000
+chromium_50            49.9460418300   # 0.04345000
+chromium_51            50.9447650200
+chromium_52            51.9405062300   # 0.83789000
+chromium_53            52.9406481500   # 0.09501000
+chromium_54            53.9388791600   # 0.02365000
+chromium_55            54.9408384300
+chromium_56            55.9406531000
+chromium_57            56.9436130000
+chromium_58            57.9443500000
+chromium_59            58.9485900000
+chromium_60            59.9500800000
+chromium_61            60.9544200000
+chromium_62            61.9561000000
+chromium_63            62.9616500000
+chromium_64            63.9640800000
+chromium_65            64.9699600000
+chromium_66            65.9736600000
+chromium_67            66.9801600000
+chromium_68            67.9840300000
+chromium          0.04345000 chromium_50 \
+                + 0.83789000 chromium_52 \
+                + 0.09501000 chromium_53 \
+                + 0.02365000 chromium_54
+
+# manganese: Mn (25)  std atomic weight: 54.938044(3)
+manganese_44           44.0071500000
+manganese_45           44.9944900000
+manganese_46           45.9860900000
+manganese_47           46.9757750000
+manganese_48           47.9685200000
+manganese_49           48.9595950000
+manganese_50           49.9542377800
+manganese_51           50.9482084700
+manganese_52           51.9455639000
+manganese_53           52.9412888900
+manganese_54           53.9403576000
+manganese_55           54.9380439100   # 1.00000000
+manganese_56           55.9389036900
+manganese_57           56.9382861000
+manganese_58           57.9400666000
+manganese_59           58.9403911000
+manganese_60           59.9431366000
+manganese_61           60.9444525000
+manganese_62           61.9479500000
+manganese_63           62.9496647000
+manganese_64           63.9538494000
+manganese_65           64.9560198000
+manganese_66           65.9605470000
+manganese_67           66.9642400000
+manganese_68           67.9696200000
+manganese_69           68.9736600000
+manganese_70           69.9793700000
+manganese_71           70.9836800000
+manganese              manganese_55
+
+# iron: Fe (26)  std atomic weight: 55.845(2)
+iron_45                45.0144200000
+iron_46                46.0006300000
+iron_47                46.9918500000
+iron_48                47.9802300000
+iron_49                48.9734290000
+iron_50                49.9629750000
+iron_51                50.9568410000
+iron_52                51.9481131000
+iron_53                52.9453064000
+iron_54                53.9396089900   # 0.05845000
+iron_55                54.9382919900
+iron_56                55.9349363300   # 0.91754000
+iron_57                56.9353928400   # 0.02119000
+iron_58                57.9332744300   # 0.00282000
+iron_59                58.9348743400
+iron_60                59.9340711000
+iron_61                60.9367462000
+iron_62                61.9367918000
+iron_63                62.9402727000
+iron_64                63.9409878000
+iron_65                64.9450115000
+iron_66                65.9462500000
+iron_67                66.9505400000
+iron_68                67.9529500000
+iron_69                68.9580700000
+iron_70                69.9610200000
+iron_71                70.9667200000
+iron_72                71.9698300000
+iron_73                72.9757200000
+iron_74                73.9793500000
+iron              0.05845000 iron_54 \
+                + 0.91754000 iron_56 \
+                + 0.02119000 iron_57 \
+                + 0.00282000 iron_58
+
+# cobalt: Co (27)  std atomic weight: 58.933194(4)
+cobalt_47              47.0105700000
+cobalt_48              48.0009300000
+cobalt_49              48.9889100000
+cobalt_50              49.9809100000
+cobalt_51              50.9706470000
+cobalt_52              51.9635100000
+cobalt_53              52.9542041000
+cobalt_54              53.9484598700
+cobalt_55              54.9419972000
+cobalt_56              55.9398388000
+cobalt_57              56.9362905700
+cobalt_58              57.9357521000
+cobalt_59              58.9331942900   # 1.00000000
+cobalt_60              59.9338163000
+cobalt_61              60.9324766200
+cobalt_62              61.9340590000
+cobalt_63              62.9336000000
+cobalt_64              63.9358110000
+cobalt_65              64.9364621000
+cobalt_66              65.9394430000
+cobalt_67              66.9406096000
+cobalt_68              67.9442600000
+cobalt_69              68.9461400000
+cobalt_70              69.9496300000
+cobalt_71              70.9523700000
+cobalt_72              71.9572900000
+cobalt_73              72.9603900000
+cobalt_74              73.9651500000
+cobalt_75              74.9687600000
+cobalt_76              75.9741300000
+cobalt                 cobalt_59
+
+# nickel: Ni (28)  std atomic weight: 58.6934(4)
+nickel_48              48.0176900000
+nickel_49              49.0077000000
+nickel_50              49.9947400000
+nickel_51              50.9861100000
+nickel_52              51.9748000000
+nickel_53              52.9681900000
+nickel_54              53.9578920000
+nickel_55              54.9513306300
+nickel_56              55.9421285500
+nickel_57              56.9397921800
+nickel_58              57.9353424100   # 0.68077000
+nickel_59              58.9343462000
+nickel_60              59.9307858800   # 0.26223000
+nickel_61              60.9310555700   # 0.01139900
+nickel_62              61.9283453700   # 0.03634600
+nickel_63              62.9296696300
+nickel_64              63.9279668200   # 0.00925500
+nickel_65              64.9300851700
+nickel_66              65.9291393000
+nickel_67              66.9315694000
+nickel_68              67.9318688000
+nickel_69              68.9356103000
+nickel_70              69.9364313000
+nickel_71              70.9405190000
+nickel_72              71.9417859000
+nickel_73              72.9462067000
+nickel_74              73.9479800000
+nickel_75              74.9525000000
+nickel_76              75.9553300000
+nickel_77              76.9605500000
+nickel_78              77.9633600000
+nickel_79              78.9702500000
+nickel            0.68077000 nickel_58 \
+                + 0.26223000 nickel_60 \
+                + 0.01139900 nickel_61 \
+                + 0.03634600 nickel_62 \
+                + 0.00925500 nickel_64
+
+# copper: Cu (29)  std atomic weight: 63.546(3)
+copper_52              51.9967100000
+copper_53              52.9845900000
+copper_54              53.9766600000
+copper_55              54.9660400000
+copper_56              55.9589500000
+copper_57              56.9492125000
+copper_58              57.9445330500
+copper_59              58.9394974800
+copper_60              59.9373645000
+copper_61              60.9334576000
+copper_62              61.9325954100
+copper_63              62.9295977200   # 0.69150000
+copper_64              63.9297643400
+copper_65              64.9277897000   # 0.30850000
+copper_66              65.9288690300
+copper_67              66.9277303000
+copper_68              67.9296109000
+copper_69              68.9294293000
+copper_70              69.9323921000
+copper_71              70.9326768000
+copper_72              71.9358203000
+copper_73              72.9366744000
+copper_74              73.9398749000
+copper_75              74.9415226000
+copper_76              75.9452750000
+copper_77              76.9479200000
+copper_78              77.9522300000
+copper_79              78.9550200000
+copper_80              79.9608900000
+copper_81              80.9658700000
+copper_82              81.9724400000
+copper            0.69150000 copper_63 \
+                + 0.30850000 copper_65
+
+# zinc: Zn (30)  std atomic weight: 65.38(2)
+zinc_54                53.9920400000
+zinc_55                54.9839800000
+zinc_56                55.9725400000
+zinc_57                56.9650600000
+zinc_58                57.9545910000
+zinc_59                58.9493126600
+zinc_60                59.9418421000
+zinc_61                60.9395070000
+zinc_62                61.9343339700
+zinc_63                62.9332115000
+zinc_64                63.9291420100   # 0.49170000
+zinc_65                64.9292407700
+zinc_66                65.9260338100   # 0.27730000
+zinc_67                66.9271277500   # 0.04040000
+zinc_68                67.9248445500   # 0.18450000
+zinc_69                68.9265507000
+zinc_70                69.9253192000   # 0.00610000
+zinc_71                70.9277196000
+zinc_72                71.9268428000
+zinc_73                72.9295826000
+zinc_74                73.9294073000
+zinc_75                74.9328402000
+zinc_76                75.9331150000
+zinc_77                76.9368872000
+zinc_78                77.9382892000
+zinc_79                78.9426381000
+zinc_80                79.9445529000
+zinc_81                80.9504026000
+zinc_82                81.9542600000
+zinc_83                82.9605600000
+zinc_84                83.9652100000
+zinc_85                84.9722600000
+zinc              0.49170000 zinc_64 \
+                + 0.27730000 zinc_66 \
+                + 0.04040000 zinc_67 \
+                + 0.18450000 zinc_68 \
+                + 0.00610000 zinc_70
+
+# gallium: Ga (31)  std atomic weight: 69.723(1)
+gallium_56             55.9953600000
+gallium_57             56.9832000000
+gallium_58             57.9747800000
+gallium_59             58.9635300000
+gallium_60             59.9572900000
+gallium_61             60.9493990000
+gallium_62             61.9441902500
+gallium_63             62.9392942000
+gallium_64             63.9368404000
+gallium_65             64.9327345900
+gallium_66             65.9315894000
+gallium_67             66.9282025000
+gallium_68             67.9279805000
+gallium_69             68.9255735000   # 0.60108000
+gallium_70             69.9260219000
+gallium_71             70.9247025800   # 0.39892000
+gallium_72             71.9263674700
+gallium_73             72.9251747000
+gallium_74             73.9269457000
+gallium_75             74.9265002000
+gallium_76             75.9288276000
+gallium_77             76.9291543000
+gallium_78             77.9316088000
+gallium_79             78.9328523000
+gallium_80             79.9364208000
+gallium_81             80.9381338000
+gallium_82             81.9431765000
+gallium_83             82.9471203000
+gallium_84             83.9524600000
+gallium_85             84.9569900000
+gallium_86             85.9630100000
+gallium_87             86.9682400000
+gallium           0.60108000 gallium_69 \
+                + 0.39892000 gallium_71
+
+# germanium: Ge (32)  std atomic weight: 72.630(8)
+germanium_58           57.9917200000
+germanium_59           58.9824900000
+germanium_60           59.9703600000
+germanium_61           60.9637900000
+germanium_62           61.9550200000
+germanium_63           62.9496280000
+germanium_64           63.9416899000
+germanium_65           64.9393681000
+germanium_66           65.9338621000
+germanium_67           66.9327339000
+germanium_68           67.9280953000
+germanium_69           68.9279645000
+germanium_70           69.9242487500   # 0.20570000
+germanium_71           70.9249523300
+germanium_72           71.9220758260   # 0.27450000
+germanium_73           72.9234589560   # 0.07750000
+germanium_74           73.9211777610   # 0.36500000
+germanium_75           74.9228583700
+germanium_76           75.9214027260   # 0.07730000
+germanium_77           76.9235498430
+germanium_78           77.9228529000
+germanium_79           78.9253600000
+germanium_80           79.9253508000
+germanium_81           80.9288329000
+germanium_82           81.9297740000
+germanium_83           82.9345391000
+germanium_84           83.9375751000
+germanium_85           84.9429697000
+germanium_86           85.9465800000
+germanium_87           86.9526800000
+germanium_88           87.9569100000
+germanium_89           88.9637900000
+germanium_90           89.9686300000
+germanium         0.20570000 germanium_70 \
+                + 0.27450000 germanium_72 \
+                + 0.07750000 germanium_73 \
+                + 0.36500000 germanium_74 \
+                + 0.07730000 germanium_76
+
+# arsenic: As (33)  std atomic weight: 74.921595(6)
+arsenic_60             59.9938800000
+arsenic_61             60.9811200000
+arsenic_62             61.9736100000
+arsenic_63             62.9639000000
+arsenic_64             63.9574300000
+arsenic_65             64.9496110000
+arsenic_66             65.9441488000
+arsenic_67             66.9392511100
+arsenic_68             67.9367741000
+arsenic_69             68.9322460000
+arsenic_70             69.9309260000
+arsenic_71             70.9271138000
+arsenic_72             71.9267523000
+arsenic_73             72.9238291000
+arsenic_74             73.9239286000
+arsenic_75             74.9215945700   # 1.00000000
+arsenic_76             75.9223920200
+arsenic_77             76.9206476000
+arsenic_78             77.9218280000
+arsenic_79             78.9209484000
+arsenic_80             79.9224746000
+arsenic_81             80.9221323000
+arsenic_82             81.9247412000
+arsenic_83             82.9252069000
+arsenic_84             83.9293033000
+arsenic_85             84.9321637000
+arsenic_86             85.9367015000
+arsenic_87             86.9402917000
+arsenic_88             87.9455500000
+arsenic_89             88.9497600000
+arsenic_90             89.9556300000
+arsenic_91             90.9603900000
+arsenic_92             91.9667400000
+arsenic                arsenic_75
+
+# selenium: Se (34)  std atomic weight: 78.971(8)
+selenium_64            63.9710900000
+selenium_65            64.9644000000
+selenium_66            65.9555900000
+selenium_67            66.9499940000
+selenium_68            67.9418252400
+selenium_69            68.9394148000
+selenium_70            69.9335155000
+selenium_71            70.9322094000
+selenium_72            71.9271405000
+selenium_73            72.9267549000
+selenium_74            73.9224759340   # 0.00890000
+selenium_75            74.9225228700
+selenium_76            75.9192137040   # 0.09370000
+selenium_77            76.9199141540   # 0.07630000
+selenium_78            77.9173092800   # 0.23770000
+selenium_79            78.9184992900
+selenium_80            79.9165218000   # 0.49610000
+selenium_81            80.9179930000
+selenium_82            81.9166995000   # 0.08730000
+selenium_83            82.9191186000
+selenium_84            83.9184668000
+selenium_85            84.9222608000
+selenium_86            85.9243117000
+selenium_87            86.9286886000
+selenium_88            87.9314175000
+selenium_89            88.9366691000
+selenium_90            89.9401000000
+selenium_91            90.9459600000
+selenium_92            91.9498400000
+selenium_93            92.9562900000
+selenium_94            93.9604900000
+selenium_95            94.9673000000
+selenium          0.00890000 selenium_74 \
+                + 0.09370000 selenium_76 \
+                + 0.07630000 selenium_77 \
+                + 0.23770000 selenium_78 \
+                + 0.49610000 selenium_80 \
+                + 0.08730000 selenium_82
+
+# bromine: Br (35)  std atomic weight: [79.901,79.907]
+bromine_67             66.9646500000
+bromine_68             67.9587300000
+bromine_69             68.9504970000
+bromine_70             69.9447920000
+bromine_71             70.9393422000
+bromine_72             71.9365886000
+bromine_73             72.9316715000
+bromine_74             73.9299102000
+bromine_75             74.9258105000
+bromine_76             75.9245420000
+bromine_77             76.9213792000
+bromine_78             77.9211459000
+bromine_79             78.9183376000   # 0.50690000
+bromine_80             79.9185298000
+bromine_81             80.9162897000   # 0.49310000
+bromine_82             81.9168032000
+bromine_83             82.9151756000
+bromine_84             83.9164960000
+bromine_85             84.9156458000
+bromine_86             85.9188054000
+bromine_87             86.9206740000
+bromine_88             87.9240833000
+bromine_89             88.9267046000
+bromine_90             89.9312928000
+bromine_91             90.9343986000
+bromine_92             91.9396316000
+bromine_93             92.9431300000
+bromine_94             93.9489000000
+bromine_95             94.9530100000
+bromine_96             95.9590300000
+bromine_97             96.9634400000
+bromine_98             97.9694600000
+bromine           0.50690000 bromine_79 \
+                + 0.49310000 bromine_81
+
+# krypton: Kr (36)  std atomic weight: 83.798(2)
+krypton_69             68.9651800000
+krypton_70             69.9560400000
+krypton_71             70.9502700000
+krypton_72             71.9420924000
+krypton_73             72.9392892000
+krypton_74             73.9330840000
+krypton_75             74.9309457000
+krypton_76             75.9259103000
+krypton_77             76.9246700000
+krypton_78             77.9203649400   # 0.00355000
+krypton_79             78.9200829000
+krypton_80             79.9163780800   # 0.02286000
+krypton_81             80.9165912000
+krypton_82             81.9134827300   # 0.11593000
+krypton_83             82.9141271600   # 0.11500000
+krypton_84             83.9114977282   # 0.56987000
+krypton_85             84.9125273000
+krypton_86             85.9106106269   # 0.17279000
+krypton_87             86.9133547600
+krypton_88             87.9144479000
+krypton_89             88.9178355000
+krypton_90             89.9195279000
+krypton_91             90.9238063000
+krypton_92             91.9261731000
+krypton_93             92.9311472000
+krypton_94             93.9341400000
+krypton_95             94.9397110000
+krypton_96             95.9430170000
+krypton_97             96.9490900000
+krypton_98             97.9524300000
+krypton_99             98.9583900000
+krypton_100            99.9623700000
+krypton_101           100.9687300000
+krypton           0.00355000 krypton_78 \
+                + 0.02286000 krypton_80 \
+                + 0.11593000 krypton_82 \
+                + 0.11500000 krypton_83 \
+                + 0.56987000 krypton_84 \
+                + 0.17279000 krypton_86
+
+# rubidium: Rb (37)  std atomic weight: 85.4678(3)
+rubidium_71            70.9653200000
+rubidium_72            71.9590800000
+rubidium_73            72.9505300000
+rubidium_74            73.9442659000
+rubidium_75            74.9385732000
+rubidium_76            75.9350730000
+rubidium_77            76.9304016000
+rubidium_78            77.9281419000
+rubidium_79            78.9239899000
+rubidium_80            79.9225164000
+rubidium_81            80.9189939000
+rubidium_82            81.9182090000
+rubidium_83            82.9151142000
+rubidium_84            83.9143752000
+rubidium_85            84.9117897379   # 0.72170000
+rubidium_86            85.9111674300
+rubidium_87            86.9091805310   # 0.27830000
+rubidium_88            87.9113155900
+rubidium_89            88.9122783000
+rubidium_90            89.9147985000
+rubidium_91            90.9165372000
+rubidium_92            91.9197284000
+rubidium_93            92.9220393000
+rubidium_94            93.9263948000
+rubidium_95            94.9292600000
+rubidium_96            95.9341334000
+rubidium_97            96.9371771000
+rubidium_98            97.9416869000
+rubidium_99            98.9450300000
+rubidium_100           99.9500300000
+rubidium_101          100.9540400000
+rubidium_102          101.9595200000
+rubidium_103          102.9639200000
+rubidium          0.72170000 rubidium_85 \
+                + 0.27830000 rubidium_87
+
+# strontium: Sr (38)  std atomic weight: 87.62(1)
+strontium_73           72.9657000000
+strontium_74           73.9561700000
+strontium_75           74.9499500000
+strontium_76           75.9417630000
+strontium_77           76.9379455000
+strontium_78           77.9321800000
+strontium_79           78.9297077000
+strontium_80           79.9245175000
+strontium_81           80.9232114000
+strontium_82           81.9183999000
+strontium_83           82.9175544000
+strontium_84           83.9134191000   # 0.00560000
+strontium_85           84.9129320000
+strontium_86           85.9092606000   # 0.09860000
+strontium_87           86.9088775000   # 0.07000000
+strontium_88           87.9056125000   # 0.82580000
+strontium_89           88.9074511000
+strontium_90           89.9077300000
+strontium_91           90.9101954000
+strontium_92           91.9110382000
+strontium_93           92.9140242000
+strontium_94           93.9153556000
+strontium_95           94.9193529000
+strontium_96           95.9217066000
+strontium_97           96.9263740000
+strontium_98           97.9286888000
+strontium_99           98.9328907000
+strontium_100          99.9357700000
+strontium_101         100.9403520000
+strontium_102         101.9437910000
+strontium_103         102.9490900000
+strontium_104         103.9526500000
+strontium_105         104.9585500000
+strontium_106         105.9626500000
+strontium_107         106.9689700000
+strontium         0.00560000 strontium_84 \
+                + 0.09860000 strontium_86 \
+                + 0.07000000 strontium_87 \
+                + 0.82580000 strontium_88
+
+# yttrium: Y (39)  std atomic weight: 88.90584(2)
+yttrium_76             75.9585600000
+yttrium_77             76.9497810000
+yttrium_78             77.9436100000
+yttrium_79             78.9373500000
+yttrium_80             79.9343561000
+yttrium_81             80.9294556000
+yttrium_82             81.9269314000
+yttrium_83             82.9224850000
+yttrium_84             83.9206721000
+yttrium_85             84.9164330000
+yttrium_86             85.9148860000
+yttrium_87             86.9108761000
+yttrium_88             87.9095016000
+yttrium_89             88.9058403000   # 1.00000000
+yttrium_90             89.9071439000
+yttrium_91             90.9072974000
+yttrium_92             91.9089451000
+yttrium_93             92.9095780000
+yttrium_94             93.9115906000
+yttrium_95             94.9128161000
+yttrium_96             95.9158968000
+yttrium_97             96.9182741000
+yttrium_98             97.9223821000
+yttrium_99             98.9241480000
+yttrium_100            99.9277150000
+yttrium_101           100.9301477000
+yttrium_102           101.9343277000
+yttrium_103           102.9372430000
+yttrium_104           103.9419600000
+yttrium_105           104.9454400000
+yttrium_106           105.9505600000
+yttrium_107           106.9545200000
+yttrium_108           107.9599600000
+yttrium_109           108.9643600000
+yttrium               yttrium_89
+
+# zirconium: Zr (40)  std atomic weight: 91.224(2)
+zirconium_78           77.9556600000
+zirconium_79           78.9494800000
+zirconium_80           79.9404000000
+zirconium_81           80.9373100000
+zirconium_82           81.9313500000
+zirconium_83           82.9292421000
+zirconium_84           83.9233269000
+zirconium_85           84.9214444000
+zirconium_86           85.9162972000
+zirconium_87           86.9148180000
+zirconium_88           87.9102213000
+zirconium_89           88.9088814000
+zirconium_90           89.9046977000   # 0.51450000
+zirconium_91           90.9056396000   # 0.11220000
+zirconium_92           91.9050347000   # 0.17150000
+zirconium_93           92.9064699000
+zirconium_94           93.9063108000   # 0.17380000
+zirconium_95           94.9080385000
+zirconium_96           95.9082714000   # 0.02800000
+zirconium_97           96.9109512000
+zirconium_98           97.9127289000
+zirconium_99           98.9166670000
+zirconium_100          99.9180006000
+zirconium_101         100.9214480000
+zirconium_102         101.9231409000
+zirconium_103         102.9271910000
+zirconium_104         103.9294360000
+zirconium_105         104.9340080000
+zirconium_106         105.9367600000
+zirconium_107         106.9417400000
+zirconium_108         107.9448700000
+zirconium_109         108.9504100000
+zirconium_110         109.9539600000
+zirconium_111         110.9596800000
+zirconium_112         111.9637000000
+zirconium         0.51450000 zirconium_90 \
+                + 0.11220000 zirconium_91 \
+                + 0.17150000 zirconium_92 \
+                + 0.17380000 zirconium_94 \
+                + 0.02800000 zirconium_96
+
+# niobium: Nb (41)  std atomic weight: 92.90637(2)
+niobium_81             80.9496000000
+niobium_82             81.9439600000
+niobium_83             82.9372900000
+niobium_84             83.9344900000
+niobium_85             84.9288458000
+niobium_86             85.9257828000
+niobium_87             86.9206937000
+niobium_88             87.9182220000
+niobium_89             88.9134450000
+niobium_90             89.9112584000
+niobium_91             90.9069897000
+niobium_92             91.9071881000
+niobium_93             92.9063730000   # 1.00000000
+niobium_94             93.9072788000
+niobium_95             94.9068324000
+niobium_96             95.9080973000
+niobium_97             96.9080959000
+niobium_98             97.9103265000
+niobium_99             98.9116130000
+niobium_100            99.9143276000
+niobium_101           100.9153103000
+niobium_102           101.9180772000
+niobium_103           102.9194572000
+niobium_104           103.9228925000
+niobium_105           104.9249465000
+niobium_106           105.9289317000
+niobium_107           106.9315937000
+niobium_108           107.9360748000
+niobium_109           108.9392200000
+niobium_110           109.9440300000
+niobium_111           110.9475300000
+niobium_112           111.9524700000
+niobium_113           112.9565100000
+niobium_114           113.9620100000
+niobium_115           114.9663400000
+niobium               niobium_93
+
+# molybdenum: Mo (42)  std atomic weight: 95.95(1)
+molybdenum_83          82.9498800000
+molybdenum_84          83.9414900000
+molybdenum_85          84.9382610000
+molybdenum_86          85.9311748000
+molybdenum_87          86.9281962000
+molybdenum_88          87.9219678000
+molybdenum_89          88.9194682000
+molybdenum_90          89.9139309000
+molybdenum_91          90.9117453000
+molybdenum_92          91.9068079600   # 0.14530000
+molybdenum_93          92.9068095800
+molybdenum_94          93.9050849000   # 0.09150000
+molybdenum_95          94.9058387700   # 0.15840000
+molybdenum_96          95.9046761200   # 0.16670000
+molybdenum_97          96.9060181200   # 0.09600000
+molybdenum_98          97.9054048200   # 0.24390000
+molybdenum_99          98.9077085100
+molybdenum_100         99.9074718000   # 0.09820000
+molybdenum_101        100.9103414000
+molybdenum_102        101.9102834000
+molybdenum_103        102.9130790000
+molybdenum_104        103.9137344000
+molybdenum_105        104.9169690000
+molybdenum_106        105.9182590000
+molybdenum_107        106.9221060000
+molybdenum_108        107.9240330000
+molybdenum_109        108.9284240000
+molybdenum_110        109.9307040000
+molybdenum_111        110.9356540000
+molybdenum_112        111.9383100000
+molybdenum_113        112.9433500000
+molybdenum_114        113.9465300000
+molybdenum_115        114.9519600000
+molybdenum_116        115.9554500000
+molybdenum_117        116.9611700000
+molybdenum        0.14530000 molybdenum_92 \
+                + 0.09150000 molybdenum_94 \
+                + 0.15840000 molybdenum_95 \
+                + 0.16670000 molybdenum_96 \
+                + 0.09600000 molybdenum_97 \
+                + 0.24390000 molybdenum_98 \
+                + 0.09820000 molybdenum_100
+
+# technetium: Tc (43)  std atomic weight: [98]
+technetium_85          84.9505800000
+technetium_86          85.9449300000
+technetium_87          86.9380672000
+technetium_88          87.9337800000
+technetium_89          88.9276487000
+technetium_90          89.9240739000
+technetium_91          90.9184254000
+technetium_92          91.9152698000
+technetium_93          92.9102460000
+technetium_94          93.9096536000
+technetium_95          94.9076536000
+technetium_96          95.9078680000
+technetium_97          96.9063667000
+technetium_98          97.9072124000
+technetium_99          98.9062508000
+technetium_100         99.9076539000
+technetium_101        100.9073090000
+technetium_102        101.9092097000
+technetium_103        102.9091760000
+technetium_104        103.9114250000
+technetium_105        104.9116550000
+technetium_106        105.9143580000
+technetium_107        106.9154606000
+technetium_108        107.9184957000
+technetium_109        108.9202560000
+technetium_110        109.9237440000
+technetium_111        110.9259010000
+technetium_112        111.9299458000
+technetium_113        112.9325690000
+technetium_114        113.9369100000
+technetium_115        114.9399800000
+technetium_116        115.9447600000
+technetium_117        116.9480600000
+technetium_118        117.9529900000
+technetium_119        118.9566600000
+technetium_120        119.9618700000
+technetium            technetium_98    # most stable
+
+# ruthenium: Ru (44)  std atomic weight: 101.07(2)
+ruthenium_87           86.9506900000
+ruthenium_88           87.9416000000
+ruthenium_89           88.9376200000
+ruthenium_90           89.9303444000
+ruthenium_91           90.9267419000
+ruthenium_92           91.9202344000
+ruthenium_93           92.9171044000
+ruthenium_94           93.9113429000
+ruthenium_95           94.9104060000
+ruthenium_96           95.9075902500   # 0.05540000
+ruthenium_97           96.9075471000
+ruthenium_98           97.9052868000   # 0.01870000
+ruthenium_99           98.9059341000   # 0.12760000
+ruthenium_100          99.9042143000   # 0.12600000
+ruthenium_101         100.9055769000   # 0.17060000
+ruthenium_102         101.9043441000   # 0.31550000
+ruthenium_103         102.9063186000
+ruthenium_104         103.9054275000   # 0.18620000
+ruthenium_105         104.9077476000
+ruthenium_106         105.9073291000
+ruthenium_107         106.9099720000
+ruthenium_108         107.9101880000
+ruthenium_109         108.9133260000
+ruthenium_110         109.9140407000
+ruthenium_111         110.9175700000
+ruthenium_112         111.9188090000
+ruthenium_113         112.9228440000
+ruthenium_114         113.9246136000
+ruthenium_115         114.9288200000
+ruthenium_116         115.9312192000
+ruthenium_117         116.9361000000
+ruthenium_118         117.9385300000
+ruthenium_119         118.9435700000
+ruthenium_120         119.9463100000
+ruthenium_121         120.9516400000
+ruthenium_122         121.9544700000
+ruthenium_123         122.9598900000
+ruthenium_124         123.9630500000
+ruthenium         0.05540000 ruthenium_96 \
+                + 0.01870000 ruthenium_98 \
+                + 0.12760000 ruthenium_99 \
+                + 0.12600000 ruthenium_100 \
+                + 0.17060000 ruthenium_101 \
+                + 0.31550000 ruthenium_102 \
+                + 0.18620000 ruthenium_104
+
+# rhodium: Rh (45)  std atomic weight: 102.90550(2)
+rhodium_89             88.9505800000
+rhodium_90             89.9442200000
+rhodium_91             90.9368800000
+rhodium_92             91.9323677000
+rhodium_93             92.9259128000
+rhodium_94             93.9217305000
+rhodium_95             94.9158979000
+rhodium_96             95.9144530000
+rhodium_97             96.9113290000
+rhodium_98             97.9107080000
+rhodium_99             98.9081282000
+rhodium_100            99.9081170000
+rhodium_101           100.9061606000
+rhodium_102           101.9068374000
+rhodium_103           102.9054980000   # 1.00000000
+rhodium_104           103.9066492000
+rhodium_105           104.9056885000
+rhodium_106           105.9072868000
+rhodium_107           106.9067480000
+rhodium_108           107.9087140000
+rhodium_109           108.9087488000
+rhodium_110           109.9110790000
+rhodium_111           110.9116423000
+rhodium_112           111.9144030000
+rhodium_113           112.9154393000
+rhodium_114           113.9187180000
+rhodium_115           114.9203116000
+rhodium_116           115.9240590000
+rhodium_117           116.9260354000
+rhodium_118           117.9303400000
+rhodium_119           118.9325570000
+rhodium_120           119.9368600000
+rhodium_121           120.9394200000
+rhodium_122           121.9439900000
+rhodium_123           122.9468500000
+rhodium_124           123.9515100000
+rhodium_125           124.9546900000
+rhodium_126           125.9594600000
+rhodium               rhodium_103
+
+# palladium: Pd (46)  std atomic weight: 106.42(1)
+palladium_91           90.9503200000
+palladium_92           91.9408800000
+palladium_93           92.9365100000
+palladium_94           93.9290376000
+palladium_95           94.9248898000
+palladium_96           95.9182151000
+palladium_97           96.9164720000
+palladium_98           97.9126983000
+palladium_99           98.9117748000
+palladium_100          99.9085050000
+palladium_101         100.9082864000
+palladium_102         101.9056022000   # 0.01020000
+palladium_103         102.9060809000
+palladium_104         103.9040305000   # 0.11140000
+palladium_105         104.9050796000   # 0.22330000
+palladium_106         105.9034804000   # 0.27330000
+palladium_107         106.9051282000
+palladium_108         107.9038916000   # 0.26460000
+palladium_109         108.9059504000
+palladium_110         109.9051722000   # 0.11720000
+palladium_111         110.9076896800
+palladium_112         111.9073297000
+palladium_113         112.9102610000
+palladium_114         113.9103686000
+palladium_115         114.9136590000
+palladium_116         115.9142970000
+palladium_117         116.9179547000
+palladium_118         117.9190667000
+palladium_119         118.9233402000
+palladium_120         119.9245511000
+palladium_121         120.9289503000
+palladium_122         121.9306320000
+palladium_123         122.9351400000
+palladium_124         123.9371400000
+palladium_125         124.9417900000
+palladium_126         125.9441600000
+palladium_127         126.9490700000
+palladium_128         127.9518300000
+palladium         0.01020000 palladium_102 \
+                + 0.11140000 palladium_104 \
+                + 0.22330000 palladium_105 \
+                + 0.27330000 palladium_106 \
+                + 0.26460000 palladium_108 \
+                + 0.11720000 palladium_110
+
+# silver: Ag (47)  std atomic weight: 107.8682(2)
+silver_93              92.9503300000
+silver_94              93.9437300000
+silver_95              94.9360200000
+silver_96              95.9307440000
+silver_97              96.9239700000
+silver_98              97.9215600000
+silver_99              98.9176458000
+silver_100             99.9161154000
+silver_101            100.9126840000
+silver_102            101.9117047000
+silver_103            102.9089631000
+silver_104            103.9086239000
+silver_105            104.9065256000
+silver_106            105.9066636000
+silver_107            106.9050916000   # 0.51839000
+silver_108            107.9059503000
+silver_109            108.9047553000   # 0.48161000
+silver_110            109.9061102000
+silver_111            110.9052959000
+silver_112            111.9070486000
+silver_113            112.9065730000
+silver_114            113.9088230000
+silver_115            114.9087670000
+silver_116            115.9113868000
+silver_117            116.9117740000
+silver_118            117.9145955000
+silver_119            118.9155700000
+silver_120            119.9187848000
+silver_121            120.9201250000
+silver_122            121.9236640000
+silver_123            122.9253370000
+silver_124            123.9289300000
+silver_125            124.9310500000
+silver_126            125.9347500000
+silver_127            126.9371100000
+silver_128            127.9410600000
+silver_129            128.9439500000
+silver_130            129.9507000000
+silver            0.51839000 silver_107 \
+                + 0.48161000 silver_109
+
+# cadmium: Cd (48)  std atomic weight: 112.414(4)
+cadmium_95             94.9499400000
+cadmium_96             95.9403400000
+cadmium_97             96.9351000000
+cadmium_98             97.9273890000
+cadmium_99             98.9249258000
+cadmium_100            99.9203488000
+cadmium_101           100.9185862000
+cadmium_102           101.9144820000
+cadmium_103           102.9134165000
+cadmium_104           103.9098564000
+cadmium_105           104.9094639000
+cadmium_106           105.9064599000   # 0.01250000
+cadmium_107           106.9066121000
+cadmium_108           107.9041834000   # 0.00890000
+cadmium_109           108.9049867000
+cadmium_110           109.9030066100   # 0.12490000
+cadmium_111           110.9041828700   # 0.12800000
+cadmium_112           111.9027628700   # 0.24130000
+cadmium_113           112.9044081300   # 0.12220000
+cadmium_114           113.9033650900   # 0.28730000
+cadmium_115           114.9054375100
+cadmium_116           115.9047631500   # 0.07490000
+cadmium_117           116.9072260000
+cadmium_118           117.9069220000
+cadmium_119           118.9098470000
+cadmium_120           119.9098681000
+cadmium_121           120.9129637000
+cadmium_122           121.9134591000
+cadmium_123           122.9168925000
+cadmium_124           123.9176574000
+cadmium_125           124.9212576000
+cadmium_126           125.9224291000
+cadmium_127           126.9264720000
+cadmium_128           127.9278129000
+cadmium_129           128.9318200000
+cadmium_130           129.9339400000
+cadmium_131           130.9406000000
+cadmium_132           131.9460400000
+cadmium_133           132.9528500000
+cadmium           0.01250000 cadmium_106 \
+                + 0.00890000 cadmium_108 \
+                + 0.12490000 cadmium_110 \
+                + 0.12800000 cadmium_111 \
+                + 0.24130000 cadmium_112 \
+                + 0.12220000 cadmium_113 \
+                + 0.28730000 cadmium_114 \
+                + 0.07490000 cadmium_116
+
+# indium: In (49)  std atomic weight: 114.818(1)
+indium_97              96.9493400000
+indium_98              97.9421400000
+indium_99              98.9341100000
+indium_100             99.9309600000
+indium_101            100.9263400000
+indium_102            101.9241071000
+indium_103            102.9198819000
+indium_104            103.9182145000
+indium_105            104.9145020000
+indium_106            105.9134640000
+indium_107            106.9102900000
+indium_108            107.9096935000
+indium_109            108.9071514000
+indium_110            109.9071700000
+indium_111            110.9051085000
+indium_112            111.9055377000
+indium_113            112.9040618400   # 0.04290000
+indium_114            113.9049179100
+indium_115            114.9038787760   # 0.95710000
+indium_116            115.9052599900
+indium_117            116.9045157000
+indium_118            117.9063566000
+indium_119            118.9058507000
+indium_120            119.9079670000
+indium_121            120.9078510000
+indium_122            121.9102810000
+indium_123            122.9104340000
+indium_124            123.9131820000
+indium_125            124.9136050000
+indium_126            125.9165070000
+indium_127            126.9174460000
+indium_128            127.9204000000
+indium_129            128.9218053000
+indium_130            129.9249770000
+indium_131            130.9269715000
+indium_132            131.9330010000
+indium_133            132.9383100000
+indium_134            133.9445400000
+indium_135            134.9500500000
+indium            0.04290000 indium_113 \
+                + 0.95710000 indium_115
+
+# tin: Sn (50)  std atomic weight: 118.710(7)
+tin_99                 98.9485300000
+tin_100                99.9385000000
+tin_101               100.9352600000
+tin_102               101.9302900000
+tin_103               102.9281050000
+tin_104               103.9231052000
+tin_105               104.9212684000
+tin_106               105.9169574000
+tin_107               106.9157137000
+tin_108               107.9118943000
+tin_109               108.9112921000
+tin_110               109.9078450000
+tin_111               110.9077401000
+tin_112               111.9048238700   # 0.00970000
+tin_113               112.9051757000
+tin_114               113.9027827000   # 0.00660000
+tin_115               114.9033446990   # 0.00340000
+tin_116               115.9017428000   # 0.14540000
+tin_117               116.9029539800   # 0.07680000
+tin_118               117.9016065700   # 0.24220000
+tin_119               118.9033111700   # 0.08590000
+tin_120               119.9022016300   # 0.32580000
+tin_121               120.9042426000
+tin_122               121.9034438000   # 0.04630000
+tin_123               122.9057252000
+tin_124               123.9052766000   # 0.05790000
+tin_125               124.9077864000
+tin_126               125.9076590000
+tin_127               126.9103900000
+tin_128               127.9105070000
+tin_129               128.9134650000
+tin_130               129.9139738000
+tin_131               130.9170450000
+tin_132               131.9178267000
+tin_133               132.9239134000
+tin_134               133.9286821000
+tin_135               134.9349086000
+tin_136               135.9399900000
+tin_137               136.9465500000
+tin_138               137.9518400000
+tin               0.00970000 tin_112 \
+                + 0.00660000 tin_114 \
+                + 0.00340000 tin_115 \
+                + 0.14540000 tin_116 \
+                + 0.07680000 tin_117 \
+                + 0.24220000 tin_118 \
+                + 0.08590000 tin_119 \
+                + 0.32580000 tin_120 \
+                + 0.04630000 tin_122 \
+                + 0.05790000 tin_124
+
+# antimony: Sb (51)  std atomic weight: 121.760(1)
+antimony_103          102.9396900000
+antimony_104          103.9364800000
+antimony_105          104.9312760000
+antimony_106          105.9286380000
+antimony_107          106.9241506000
+antimony_108          107.9222267000
+antimony_109          108.9181411000
+antimony_110          109.9168543000
+antimony_111          110.9132182000
+antimony_112          111.9124000000
+antimony_113          112.9093750000
+antimony_114          113.9092900000
+antimony_115          114.9065980000
+antimony_116          115.9067931000
+antimony_117          116.9048415000
+antimony_118          117.9055321000
+antimony_119          118.9039455000
+antimony_120          119.9050794000
+antimony_121          120.9038120000   # 0.57210000
+antimony_122          121.9051699000
+antimony_123          122.9042132000   # 0.42790000
+antimony_124          123.9059350000
+antimony_125          124.9052530000
+antimony_126          125.9072530000
+antimony_127          126.9069243000
+antimony_128          127.9091460000
+antimony_129          128.9091470000
+antimony_130          129.9116620000
+antimony_131          130.9119888000
+antimony_132          131.9145077000
+antimony_133          132.9152732000
+antimony_134          133.9205357000
+antimony_135          134.9251851000
+antimony_136          135.9307459000
+antimony_137          136.9355500000
+antimony_138          137.9414500000
+antimony_139          138.9465500000
+antimony_140          139.9528300000
+antimony          0.57210000 antimony_121 \
+                + 0.42790000 antimony_123
+
+# tellurium: Te (52)  std atomic weight: 127.60(3)
+tellurium_105         104.9433000000
+tellurium_106         105.9375000000
+tellurium_107         106.9350120000
+tellurium_108         107.9293805000
+tellurium_109         108.9273045000
+tellurium_110         109.9224581000
+tellurium_111         110.9210006000
+tellurium_112         111.9167279000
+tellurium_113         112.9158910000
+tellurium_114         113.9120890000
+tellurium_115         114.9119020000
+tellurium_116         115.9084600000
+tellurium_117         116.9086460000
+tellurium_118         117.9058540000
+tellurium_119         118.9064071000
+tellurium_120         119.9040593000   # 0.00090000
+tellurium_121         120.9049440000
+tellurium_122         121.9030435000   # 0.02550000
+tellurium_123         122.9042698000   # 0.00890000
+tellurium_124         123.9028171000   # 0.04740000
+tellurium_125         124.9044299000   # 0.07070000
+tellurium_126         125.9033109000   # 0.18840000
+tellurium_127         126.9052257000
+tellurium_128         127.9044612800   # 0.31740000
+tellurium_129         128.9065964600
+tellurium_130         129.9062227480   # 0.34080000
+tellurium_131         130.9085222130
+tellurium_132         131.9085467000
+tellurium_133         132.9109688000
+tellurium_134         133.9113940000
+tellurium_135         134.9165557000
+tellurium_136         135.9201006000
+tellurium_137         136.9255989000
+tellurium_138         137.9294722000
+tellurium_139         138.9353672000
+tellurium_140         139.9394990000
+tellurium_141         140.9458000000
+tellurium_142         141.9502200000
+tellurium_143         142.9567600000
+tellurium         0.00090000 tellurium_120 \
+                + 0.02550000 tellurium_122 \
+                + 0.00890000 tellurium_123 \
+                + 0.04740000 tellurium_124 \
+                + 0.07070000 tellurium_125 \
+                + 0.18840000 tellurium_126 \
+                + 0.31740000 tellurium_128 \
+                + 0.34080000 tellurium_130
+
+# iodine: I (53)  std atomic weight: 126.90447(3)
+iodine_107            106.9467800000
+iodine_108            107.9434800000
+iodine_109            108.9380853000
+iodine_110            109.9350890000
+iodine_111            110.9302692000
+iodine_112            111.9280050000
+iodine_113            112.9236501000
+iodine_114            113.9218500000
+iodine_115            114.9180480000
+iodine_116            115.9168100000
+iodine_117            116.9136480000
+iodine_118            117.9130740000
+iodine_119            118.9100740000
+iodine_120            119.9100870000
+iodine_121            120.9074051000
+iodine_122            121.9075888000
+iodine_123            122.9055885000
+iodine_124            123.9062090000
+iodine_125            124.9046294000
+iodine_126            125.9056233000
+iodine_127            126.9044719000   # 1.00000000
+iodine_128            127.9058086000
+iodine_129            128.9049837000
+iodine_130            129.9066702000
+iodine_131            130.9061263000
+iodine_132            131.9079935000
+iodine_133            132.9077970000
+iodine_134            133.9097588000
+iodine_135            134.9100488000
+iodine_136            135.9146040000
+iodine_137            136.9180282000
+iodine_138            137.9227264000
+iodine_139            138.9265060000
+iodine_140            139.9317300000
+iodine_141            140.9356900000
+iodine_142            141.9412000000
+iodine_143            142.9456500000
+iodine_144            143.9513900000
+iodine_145            144.9560500000
+iodine                iodine_127
+
+# xenon: Xe (54)  std atomic weight: 131.293(6)
+xenon_109             108.9504300000
+xenon_110             109.9442600000
+xenon_111             110.9416070000
+xenon_112             111.9355590000
+xenon_113             112.9332217000
+xenon_114             113.9279800000
+xenon_115             114.9262940000
+xenon_116             115.9215810000
+xenon_117             116.9203590000
+xenon_118             117.9161790000
+xenon_119             118.9154110000
+xenon_120             119.9117840000
+xenon_121             120.9114530000
+xenon_122             121.9083680000
+xenon_123             122.9084820000
+xenon_124             123.9058920000   # 0.00095200
+xenon_125             124.9063944000
+xenon_126             125.9042983000   # 0.00089000
+xenon_127             126.9051829000
+xenon_128             127.9035310000   # 0.01910200
+xenon_129             128.9047808611   # 0.26400600
+xenon_130             129.9035093490   # 0.04071000
+xenon_131             130.9050840600   # 0.21232400
+xenon_132             131.9041550856   # 0.26908600
+xenon_133             132.9059108000
+xenon_134             133.9053946600   # 0.10435700
+xenon_135             134.9072278000
+xenon_136             135.9072144840   # 0.08857300
+xenon_137             136.9115577800
+xenon_138             137.9141463000
+xenon_139             138.9187922000
+xenon_140             139.9216458000
+xenon_141             140.9267872000
+xenon_142             141.9299731000
+xenon_143             142.9353696000
+xenon_144             143.9389451000
+xenon_145             144.9447200000
+xenon_146             145.9485180000
+xenon_147             146.9542600000
+xenon_148             147.9581300000
+xenon             0.00095200 xenon_124 \
+                + 0.00089000 xenon_126 \
+                + 0.01910200 xenon_128 \
+                + 0.26400600 xenon_129 \
+                + 0.04071000 xenon_130 \
+                + 0.21232400 xenon_131 \
+                + 0.26908600 xenon_132 \
+                + 0.10435700 xenon_134 \
+                + 0.08857300 xenon_136
+
+# caesium: Cs (55)  std atomic weight: 132.90545196(6)
+# IUPAC spelling
+caesium_112           111.9503090000
+caesium_113           112.9444291000
+caesium_114           113.9412960000
+caesium_115           114.9359100000
+caesium_116           115.9333700000
+caesium_117           116.9286170000
+caesium_118           117.9265600000
+caesium_119           118.9223770000
+caesium_120           119.9206770000
+caesium_121           120.9172270000
+caesium_122           121.9161080000
+caesium_123           122.9129960000
+caesium_124           123.9122578000
+caesium_125           124.9097280000
+caesium_126           125.9094460000
+caesium_127           126.9074174000
+caesium_128           127.9077487000
+caesium_129           128.9060657000
+caesium_130           129.9067093000
+caesium_131           130.9054649000
+caesium_132           131.9064339000
+caesium_133           132.9054519610   # 1.00000000
+caesium_134           133.9067185030
+caesium_135           134.9059770000
+caesium_136           135.9073114000
+caesium_137           136.9070892300
+caesium_138           137.9110171000
+caesium_139           138.9133638000
+caesium_140           139.9172831000
+caesium_141           140.9200455000
+caesium_142           141.9242960000
+caesium_143           142.9273490000
+caesium_144           143.9320760000
+caesium_145           144.9355270000
+caesium_146           145.9403440000
+caesium_147           146.9441560000
+caesium_148           147.9492300000
+caesium_149           148.9530200000
+caesium_150           149.9583300000
+caesium_151           150.9625800000
+caesium               caesium_133
+# American spelling
+cesium_112            111.9503090000
+cesium_113            112.9444291000
+cesium_114            113.9412960000
+cesium_115            114.9359100000
+cesium_116            115.9333700000
+cesium_117            116.9286170000
+cesium_118            117.9265600000
+cesium_119            118.9223770000
+cesium_120            119.9206770000
+cesium_121            120.9172270000
+cesium_122            121.9161080000
+cesium_123            122.9129960000
+cesium_124            123.9122578000
+cesium_125            124.9097280000
+cesium_126            125.9094460000
+cesium_127            126.9074174000
+cesium_128            127.9077487000
+cesium_129            128.9060657000
+cesium_130            129.9067093000
+cesium_131            130.9054649000
+cesium_132            131.9064339000
+cesium_133            132.9054519610   # 1.00000000
+cesium_134            133.9067185030
+cesium_135            134.9059770000
+cesium_136            135.9073114000
+cesium_137            136.9070892300
+cesium_138            137.9110171000
+cesium_139            138.9133638000
+cesium_140            139.9172831000
+cesium_141            140.9200455000
+cesium_142            141.9242960000
+cesium_143            142.9273490000
+cesium_144            143.9320760000
+cesium_145            144.9355270000
+cesium_146            145.9403440000
+cesium_147            146.9441560000
+cesium_148            147.9492300000
+cesium_149            148.9530200000
+cesium_150            149.9583300000
+cesium_151            150.9625800000
+cesium                cesium_133
+
+# barium: Ba (56)  std atomic weight: 137.327(7)
+barium_114            113.9506600000
+barium_115            114.9473700000
+barium_116            115.9412800000
+barium_117            116.9381400000
+barium_118            117.9330600000
+barium_119            118.9306600000
+barium_120            119.9260500000
+barium_121            120.9240500000
+barium_122            121.9199040000
+barium_123            122.9187810000
+barium_124            123.9150940000
+barium_125            124.9144720000
+barium_126            125.9112500000
+barium_127            126.9110910000
+barium_128            127.9083420000
+barium_129            128.9086810000
+barium_130            129.9063207000   # 0.00106000
+barium_131            130.9069410000
+barium_132            131.9050611000   # 0.00101000
+barium_133            132.9060074000
+barium_134            133.9045081800   # 0.02417000
+barium_135            134.9056883800   # 0.06592000
+barium_136            135.9045757300   # 0.07854000
+barium_137            136.9058271400   # 0.11232000
+barium_138            137.9052470000   # 0.71698000
+barium_139            138.9088411000
+barium_140            139.9106057000
+barium_141            140.9144033000
+barium_142            141.9164324000
+barium_143            142.9206253000
+barium_144            143.9229549000
+barium_145            144.9275184000
+barium_146            145.9302840000
+barium_147            146.9353040000
+barium_148            147.9381710000
+barium_149            148.9430800000
+barium_150            149.9460500000
+barium_151            150.9512700000
+barium_152            151.9548100000
+barium_153            152.9603600000
+barium            0.00106000 barium_130 \
+                + 0.00101000 barium_132 \
+                + 0.02417000 barium_134 \
+                + 0.06592000 barium_135 \
+                + 0.07854000 barium_136 \
+                + 0.11232000 barium_137 \
+                + 0.71698000 barium_138
+
+# lanthanum: La (57)  std atomic weight: 138.90547(7)
+lanthanum_116         115.9563000000
+lanthanum_117         116.9499900000
+lanthanum_118         117.9467300000
+lanthanum_119         118.9409900000
+lanthanum_120         119.9380700000
+lanthanum_121         120.9331500000
+lanthanum_122         121.9307100000
+lanthanum_123         122.9263000000
+lanthanum_124         123.9245740000
+lanthanum_125         124.9208160000
+lanthanum_126         125.9195130000
+lanthanum_127         126.9163750000
+lanthanum_128         127.9155920000
+lanthanum_129         128.9126940000
+lanthanum_130         129.9123690000
+lanthanum_131         130.9100700000
+lanthanum_132         131.9101190000
+lanthanum_133         132.9082180000
+lanthanum_134         133.9085140000
+lanthanum_135         134.9069840000
+lanthanum_136         135.9076350000
+lanthanum_137         136.9064504000
+lanthanum_138         137.9071149000   # 0.00088810
+lanthanum_139         138.9063563000   # 0.99911190
+lanthanum_140         139.9094806000
+lanthanum_141         140.9109660000
+lanthanum_142         141.9140909000
+lanthanum_143         142.9160795000
+lanthanum_144         143.9196460000
+lanthanum_145         144.9218080000
+lanthanum_146         145.9258750000
+lanthanum_147         146.9284180000
+lanthanum_148         147.9326790000
+lanthanum_149         148.9353500000
+lanthanum_150         149.9394700000
+lanthanum_151         150.9423200000
+lanthanum_152         151.9468200000
+lanthanum_153         152.9503600000
+lanthanum_154         153.9551700000
+lanthanum_155         154.9590100000
+lanthanum         0.00088810 lanthanum_138 \
+                + 0.99911190 lanthanum_139
+
+# cerium: Ce (58)  std atomic weight: 140.116(1)
+cerium_119            118.9527100000
+cerium_120            119.9465400000
+cerium_121            120.9433500000
+cerium_122            121.9378700000
+cerium_123            122.9352800000
+cerium_124            123.9303100000
+cerium_125            124.9284400000
+cerium_126            125.9239710000
+cerium_127            126.9227270000
+cerium_128            127.9189110000
+cerium_129            128.9181020000
+cerium_130            129.9147360000
+cerium_131            130.9144290000
+cerium_132            131.9114640000
+cerium_133            132.9115200000
+cerium_134            133.9089280000
+cerium_135            134.9091610000
+cerium_136            135.9071292100   # 0.00185000
+cerium_137            136.9077623600
+cerium_138            137.9059910000   # 0.00251000
+cerium_139            138.9066551000
+cerium_140            139.9054431000   # 0.88450000
+cerium_141            140.9082807000
+cerium_142            141.9092504000   # 0.11114000
+cerium_143            142.9123921000
+cerium_144            143.9136529000
+cerium_145            144.9172650000
+cerium_146            145.9188020000
+cerium_147            146.9226899000
+cerium_148            147.9244240000
+cerium_149            148.9284270000
+cerium_150            149.9303840000
+cerium_151            150.9342720000
+cerium_152            151.9366000000
+cerium_153            152.9409300000
+cerium_154            153.9438000000
+cerium_155            154.9485500000
+cerium_156            155.9518300000
+cerium_157            156.9570500000
+cerium            0.00185000 cerium_136 \
+                + 0.00251000 cerium_138 \
+                + 0.88450000 cerium_140 \
+                + 0.11114000 cerium_142
+
+# praseodymium: Pr (59)  std atomic weight: 140.90766(2)
+praseodymium_121      120.9553200000
+praseodymium_122      121.9517500000
+praseodymium_123      122.9459600000
+praseodymium_124      123.9429400000
+praseodymium_125      124.9377000000
+praseodymium_126      125.9352400000
+praseodymium_127      126.9307100000
+praseodymium_128      127.9287910000
+praseodymium_129      128.9250950000
+praseodymium_130      129.9235900000
+praseodymium_131      130.9202350000
+praseodymium_132      131.9192550000
+praseodymium_133      132.9163310000
+praseodymium_134      133.9156970000
+praseodymium_135      134.9131120000
+praseodymium_136      135.9126770000
+praseodymium_137      136.9106792000
+praseodymium_138      137.9107540000
+praseodymium_139      138.9089408000
+praseodymium_140      139.9090803000
+praseodymium_141      140.9076576000   # 1.00000000
+praseodymium_142      141.9100496000
+praseodymium_143      142.9108228000
+praseodymium_144      143.9133109000
+praseodymium_145      144.9145182000
+praseodymium_146      145.9176800000
+praseodymium_147      146.9190080000
+praseodymium_148      147.9221300000
+praseodymium_149      148.9237360000
+praseodymium_150      149.9266765000
+praseodymium_151      150.9283090000
+praseodymium_152      151.9315530000
+praseodymium_153      152.9339040000
+praseodymium_154      153.9375300000
+praseodymium_155      154.9405090000
+praseodymium_156      155.9446400000
+praseodymium_157      156.9478900000
+praseodymium_158      157.9524100000
+praseodymium_159      158.9558900000
+praseodymium          praseodymium_141
+
+# neodymium: Nd (60)  std atomic weight: 144.242(3)
+neodymium_124         123.9522000000
+neodymium_125         124.9489000000
+neodymium_126         125.9431100000
+neodymium_127         126.9403800000
+neodymium_128         127.9352500000
+neodymium_129         128.9331000000
+neodymium_130         129.9285060000
+neodymium_131         130.9272480000
+neodymium_132         131.9233210000
+neodymium_133         132.9223480000
+neodymium_134         133.9187900000
+neodymium_135         134.9181810000
+neodymium_136         135.9149760000
+neodymium_137         136.9145620000
+neodymium_138         137.9119500000
+neodymium_139         138.9119540000
+neodymium_140         139.9095500000
+neodymium_141         140.9096147000
+neodymium_142         141.9077290000   # 0.27152000
+neodymium_143         142.9098200000   # 0.12174000
+neodymium_144         143.9100930000   # 0.23798000
+neodymium_145         144.9125793000   # 0.08293000
+neodymium_146         145.9131226000   # 0.17189000
+neodymium_147         146.9161061000
+neodymium_148         147.9168993000   # 0.05756000
+neodymium_149         148.9201548000
+neodymium_150         149.9209022000   # 0.05638000
+neodymium_151         150.9238403000
+neodymium_152         151.9246920000
+neodymium_153         152.9277180000
+neodymium_154         153.9294800000
+neodymium_155         154.9331357000
+neodymium_156         155.9350800000
+neodymium_157         156.9393860000
+neodymium_158         157.9419700000
+neodymium_159         158.9465300000
+neodymium_160         159.9494000000
+neodymium_161         160.9542800000
+neodymium         0.27152000 neodymium_142 \
+                + 0.12174000 neodymium_143 \
+                + 0.23798000 neodymium_144 \
+                + 0.08293000 neodymium_145 \
+                + 0.17189000 neodymium_146 \
+                + 0.05756000 neodymium_148 \
+                + 0.05638000 neodymium_150
+
+# promethium: Pm (61)  std atomic weight: [145]
+promethium_126        125.9579200000
+promethium_127        126.9519200000
+promethium_128        127.9487000000
+promethium_129        128.9432300000
+promethium_130        129.9405300000
+promethium_131        130.9356700000
+promethium_132        131.9338400000
+promethium_133        132.9297820000
+promethium_134        133.9283530000
+promethium_135        134.9248230000
+promethium_136        135.9235850000
+promethium_137        136.9204800000
+promethium_138        137.9195480000
+promethium_139        138.9168000000
+promethium_140        139.9160400000
+promethium_141        140.9135550000
+promethium_142        141.9128900000
+promethium_143        142.9109383000
+promethium_144        143.9125964000
+promethium_145        144.9127559000
+promethium_146        145.9147024000
+promethium_147        146.9151450000
+promethium_148        147.9174819000
+promethium_149        148.9183423000
+promethium_150        149.9209910000
+promethium_151        150.9212175000
+promethium_152        151.9235060000
+promethium_153        152.9241567000
+promethium_154        153.9264720000
+promethium_155        154.9281370000
+promethium_156        155.9311175000
+promethium_157        156.9331214000
+promethium_158        157.9365650000
+promethium_159        158.9392870000
+promethium_160        159.9431000000
+promethium_161        160.9460700000
+promethium_162        161.9502200000
+promethium_163        162.9535700000
+promethium            promethium_145   # most stable
+
+# samarium: Sm (62)  std atomic weight: 150.36(2)
+samarium_128          127.9584200000
+samarium_129          128.9547600000
+samarium_130          129.9490000000
+samarium_131          130.9461800000
+samarium_132          131.9408700000
+samarium_133          132.9385600000
+samarium_134          133.9341100000
+samarium_135          134.9325200000
+samarium_136          135.9282760000
+samarium_137          136.9269710000
+samarium_138          137.9232440000
+samarium_139          138.9222970000
+samarium_140          139.9189950000
+samarium_141          140.9184816000
+samarium_142          141.9152044000
+samarium_143          142.9146353000
+samarium_144          143.9120065000   # 0.03070000
+samarium_145          144.9134173000
+samarium_146          145.9130470000
+samarium_147          146.9149044000   # 0.14990000
+samarium_148          147.9148292000   # 0.11240000
+samarium_149          148.9171921000   # 0.13820000
+samarium_150          149.9172829000   # 0.07380000
+samarium_151          150.9199398000
+samarium_152          151.9197397000   # 0.26750000
+samarium_153          152.9221047000
+samarium_154          153.9222169000   # 0.22750000
+samarium_155          154.9246477000
+samarium_156          155.9255360000
+samarium_157          156.9284187000
+samarium_158          157.9299510000
+samarium_159          158.9332172000
+samarium_160          159.9353353000
+samarium_161          160.9391602000
+samarium_162          161.9414600000
+samarium_163          162.9455500000
+samarium_164          163.9483600000
+samarium_165          164.9529700000
+samarium          0.03070000 samarium_144 \
+                + 0.14990000 samarium_147 \
+                + 0.11240000 samarium_148 \
+                + 0.13820000 samarium_149 \
+                + 0.07380000 samarium_150 \
+                + 0.26750000 samarium_152 \
+                + 0.22750000 samarium_154
+
+# europium: Eu (63)  std atomic weight: 151.964(1)
+europium_130          129.9636900000
+europium_131          130.9578400000
+europium_132          131.9546700000
+europium_133          132.9492900000
+europium_134          133.9464000000
+europium_135          134.9418700000
+europium_136          135.9396200000
+europium_137          136.9354600000
+europium_138          137.9337090000
+europium_139          138.9297920000
+europium_140          139.9280880000
+europium_141          140.9249320000
+europium_142          141.9234420000
+europium_143          142.9202990000
+europium_144          143.9188200000
+europium_145          144.9162726000
+europium_146          145.9172110000
+europium_147          146.9167527000
+europium_148          147.9180890000
+europium_149          148.9179378000
+europium_150          149.9197077000
+europium_151          150.9198578000   # 0.47810000
+europium_152          151.9217522000
+europium_153          152.9212380000   # 0.52190000
+europium_154          153.9229870000
+europium_155          154.9229011000
+europium_156          155.9247605000
+europium_157          156.9254334000
+europium_158          157.9277990000
+europium_159          158.9291001000
+europium_160          159.9318510000
+europium_161          160.9336640000
+europium_162          161.9369890000
+europium_163          162.9391960000
+europium_164          163.9427400000
+europium_165          164.9455900000
+europium_166          165.9496200000
+europium_167          166.9528900000
+europium          0.47810000 europium_151 \
+                + 0.52190000 europium_153
+
+# gadolinium: Gd (64)  std atomic weight: 157.25(3)
+gadolinium_133        132.9613300000
+gadolinium_134        133.9556600000
+gadolinium_135        134.9524500000
+gadolinium_136        135.9473000000
+gadolinium_137        136.9450200000
+gadolinium_138        137.9402500000
+gadolinium_139        138.9381300000
+gadolinium_140        139.9336740000
+gadolinium_141        140.9321260000
+gadolinium_142        141.9281160000
+gadolinium_143        142.9267500000
+gadolinium_144        143.9229630000
+gadolinium_145        144.9217130000
+gadolinium_146        145.9183188000
+gadolinium_147        146.9191014000
+gadolinium_148        147.9181215000
+gadolinium_149        148.9193481000
+gadolinium_150        149.9186644000
+gadolinium_151        150.9203560000
+gadolinium_152        151.9197995000   # 0.00200000
+gadolinium_153        152.9217580000
+gadolinium_154        153.9208741000   # 0.02180000
+gadolinium_155        154.9226305000   # 0.14800000
+gadolinium_156        155.9221312000   # 0.20470000
+gadolinium_157        156.9239686000   # 0.15650000
+gadolinium_158        157.9241123000   # 0.24840000
+gadolinium_159        158.9263970000
+gadolinium_160        159.9270624000   # 0.21860000
+gadolinium_161        160.9296775000
+gadolinium_162        161.9309930000
+gadolinium_163        162.9341769000
+gadolinium_164        163.9358300000
+gadolinium_165        164.9393600000
+gadolinium_166        165.9414600000
+gadolinium_167        166.9454500000
+gadolinium_168        167.9480800000
+gadolinium_169        168.9526000000
+gadolinium        0.00200000 gadolinium_152 \
+                + 0.02180000 gadolinium_154 \
+                + 0.14800000 gadolinium_155 \
+                + 0.20470000 gadolinium_156 \
+                + 0.15650000 gadolinium_157 \
+                + 0.24840000 gadolinium_158 \
+                + 0.21860000 gadolinium_160
+
+# terbium: Tb (65)  std atomic weight: 158.92535(2)
+terbium_135           134.9647600000
+terbium_136           135.9612900000
+terbium_137           136.9560200000
+terbium_138           137.9531200000
+terbium_139           138.9483300000
+terbium_140           139.9458100000
+terbium_141           140.9414500000
+terbium_142           141.9392800000
+terbium_143           142.9351370000
+terbium_144           143.9330450000
+terbium_145           144.9288200000
+terbium_146           145.9272530000
+terbium_147           146.9240548000
+terbium_148           147.9242820000
+terbium_149           148.9232535000
+terbium_150           149.9236649000
+terbium_151           150.9231096000
+terbium_152           151.9240830000
+terbium_153           152.9234424000
+terbium_154           153.9246850000
+terbium_155           154.9235110000
+terbium_156           155.9247552000
+terbium_157           156.9240330000
+terbium_158           157.9254209000
+terbium_159           158.9253547000   # 1.00000000
+terbium_160           159.9271756000
+terbium_161           160.9275778000
+terbium_162           161.9294950000
+terbium_163           162.9306547000
+terbium_164           163.9333600000
+terbium_165           164.9349800000
+terbium_166           165.9378600000
+terbium_167           166.9399600000
+terbium_168           167.9434000000
+terbium_169           168.9459700000
+terbium_170           169.9498400000
+terbium_171           170.9527300000
+terbium               terbium_159
+
+# dysprosium: Dy (66)  std atomic weight: 162.500(1)
+dysprosium_138        137.9625000000
+dysprosium_139        138.9595900000
+dysprosium_140        139.9540200000
+dysprosium_141        140.9512800000
+dysprosium_142        141.9461900000
+dysprosium_143        142.9439940000
+dysprosium_144        143.9392695000
+dysprosium_145        144.9374740000
+dysprosium_146        145.9328445000
+dysprosium_147        146.9310827000
+dysprosium_148        147.9271570000
+dysprosium_149        148.9273220000
+dysprosium_150        149.9255933000
+dysprosium_151        150.9261916000
+dysprosium_152        151.9247253000
+dysprosium_153        152.9257724000
+dysprosium_154        153.9244293000
+dysprosium_155        154.9257590000
+dysprosium_156        155.9242847000   # 0.00056000
+dysprosium_157        156.9254707000
+dysprosium_158        157.9244159000   # 0.00095000
+dysprosium_159        158.9257470000
+dysprosium_160        159.9252046000   # 0.02329000
+dysprosium_161        160.9269405000   # 0.18889000
+dysprosium_162        161.9268056000   # 0.25475000
+dysprosium_163        162.9287383000   # 0.24896000
+dysprosium_164        163.9291819000   # 0.28260000
+dysprosium_165        164.9317105000
+dysprosium_166        165.9328139000
+dysprosium_167        166.9356610000
+dysprosium_168        167.9371300000
+dysprosium_169        168.9403100000
+dysprosium_170        169.9423900000
+dysprosium_171        170.9461200000
+dysprosium_172        171.9484600000
+dysprosium_173        172.9528300000
+dysprosium        0.00056000 dysprosium_156 \
+                + 0.00095000 dysprosium_158 \
+                + 0.02329000 dysprosium_160 \
+                + 0.18889000 dysprosium_161 \
+                + 0.25475000 dysprosium_162 \
+                + 0.24896000 dysprosium_163 \
+                + 0.28260000 dysprosium_164
+
+# holmium: Ho (67)  std atomic weight: 164.93033(2)
+holmium_140           139.9685900000
+holmium_141           140.9631100000
+holmium_142           141.9600100000
+holmium_143           142.9548600000
+holmium_144           143.9521097000
+holmium_145           144.9472674000
+holmium_146           145.9449935000
+holmium_147           146.9401423000
+holmium_148           147.9377440000
+holmium_149           148.9338030000
+holmium_150           149.9334980000
+holmium_151           150.9316983000
+holmium_152           151.9317240000
+holmium_153           152.9302064000
+holmium_154           153.9306068000
+holmium_155           154.9291040000
+holmium_156           155.9297060000
+holmium_157           156.9282540000
+holmium_158           157.9289460000
+holmium_159           158.9277197000
+holmium_160           159.9287370000
+holmium_161           160.9278615000
+holmium_162           161.9291023000
+holmium_163           162.9287410000
+holmium_164           163.9302403000
+holmium_165           164.9303288000   # 1.00000000
+holmium_166           165.9322909000
+holmium_167           166.9331385000
+holmium_168           167.9355220000
+holmium_169           168.9368780000
+holmium_170           169.9396250000
+holmium_171           170.9414700000
+holmium_172           171.9447300000
+holmium_173           172.9470200000
+holmium_174           173.9509500000
+holmium_175           174.9536200000
+holmium               holmium_165
+
+# erbium: Er (68)  std atomic weight: 167.259(3)
+erbium_142            141.9701000000
+erbium_143            142.9666200000
+erbium_144            143.9607000000
+erbium_145            144.9580500000
+erbium_146            145.9524184000
+erbium_147            146.9499640000
+erbium_148            147.9447350000
+erbium_149            148.9423060000
+erbium_150            149.9379160000
+erbium_151            150.9374490000
+erbium_152            151.9350570000
+erbium_153            152.9350800000
+erbium_154            153.9327908000
+erbium_155            154.9332159000
+erbium_156            155.9310670000
+erbium_157            156.9319490000
+erbium_158            157.9298930000
+erbium_159            158.9306918000
+erbium_160            159.9290770000
+erbium_161            160.9300046000
+erbium_162            161.9287884000   # 0.00139000
+erbium_163            162.9300408000
+erbium_164            163.9292088000   # 0.01601000
+erbium_165            164.9307345000
+erbium_166            165.9302995000   # 0.33503000
+erbium_167            166.9320546000   # 0.22869000
+erbium_168            167.9323767000   # 0.26978000
+erbium_169            168.9345968000
+erbium_170            169.9354702000   # 0.14910000
+erbium_171            170.9380357000
+erbium_172            171.9393619000
+erbium_173            172.9424000000
+erbium_174            173.9442300000
+erbium_175            174.9477700000
+erbium_176            175.9499400000
+erbium_177            176.9539900000
+erbium            0.00139000 erbium_162 \
+                + 0.01601000 erbium_164 \
+                + 0.33503000 erbium_166 \
+                + 0.22869000 erbium_167 \
+                + 0.26978000 erbium_168 \
+                + 0.14910000 erbium_170
+
+# thulium: Tm (69)  std atomic weight: 168.93422(2)
+thulium_144           143.9762800000
+thulium_145           144.9703900000
+thulium_146           145.9668400000
+thulium_147           146.9613799000
+thulium_148           147.9583840000
+thulium_149           148.9528900000
+thulium_150           149.9500900000
+thulium_151           150.9454880000
+thulium_152           151.9444220000
+thulium_153           152.9420400000
+thulium_154           153.9415700000
+thulium_155           154.9392100000
+thulium_156           155.9389920000
+thulium_157           156.9369440000
+thulium_158           157.9369800000
+thulium_159           158.9349750000
+thulium_160           159.9352630000
+thulium_161           160.9335490000
+thulium_162           161.9340020000
+thulium_163           162.9326592000
+thulium_164           163.9335440000
+thulium_165           164.9324431000
+thulium_166           165.9335610000
+thulium_167           166.9328562000
+thulium_168           167.9341774000
+thulium_169           168.9342179000   # 1.00000000
+thulium_170           169.9358060000
+thulium_171           170.9364339000
+thulium_172           171.9384055000
+thulium_173           172.9396084000
+thulium_174           173.9421730000
+thulium_175           174.9438410000
+thulium_176           175.9470000000
+thulium_177           176.9490400000
+thulium_178           177.9526400000
+thulium_179           178.9553400000
+thulium               thulium_169
+
+# ytterbium: Yb (70)  std atomic weight: 173.054(5)
+ytterbium_148         147.9675800000
+ytterbium_149         148.9643600000
+ytterbium_150         149.9585200000
+ytterbium_151         150.9554000000
+ytterbium_152         151.9502700000
+ytterbium_153         152.9493200000
+ytterbium_154         153.9463960000
+ytterbium_155         154.9457830000
+ytterbium_156         155.9428250000
+ytterbium_157         156.9426450000
+ytterbium_158         157.9398705000
+ytterbium_159         158.9400550000
+ytterbium_160         159.9375570000
+ytterbium_161         160.9379070000
+ytterbium_162         161.9357740000
+ytterbium_163         162.9363400000
+ytterbium_164         163.9344950000
+ytterbium_165         164.9352700000
+ytterbium_166         165.9338747000
+ytterbium_167         166.9349530000
+ytterbium_168         167.9338896000   # 0.00123000
+ytterbium_169         168.9351825000
+ytterbium_170         169.9347664000   # 0.02982000
+ytterbium_171         170.9363302000   # 0.14090000
+ytterbium_172         171.9363859000   # 0.21680000
+ytterbium_173         172.9382151000   # 0.16103000
+ytterbium_174         173.9388664000   # 0.32026000
+ytterbium_175         174.9412808000
+ytterbium_176         175.9425764000   # 0.12996000
+ytterbium_177         176.9452656000
+ytterbium_178         177.9466510000
+ytterbium_179         178.9500400000
+ytterbium_180         179.9521200000
+ytterbium_181         180.9558900000
+ytterbium         0.00123000 ytterbium_168 \
+                + 0.02982000 ytterbium_170 \
+                + 0.14090000 ytterbium_171 \
+                + 0.21680000 ytterbium_172 \
+                + 0.16103000 ytterbium_173 \
+                + 0.32026000 ytterbium_174 \
+                + 0.12996000 ytterbium_176
+
+# lutetium: Lu (71)  std atomic weight: 174.9668(1)
+lutetium_150          149.9735500000
+lutetium_151          150.9676800000
+lutetium_152          151.9641200000
+lutetium_153          152.9587500000
+lutetium_154          153.9573600000
+lutetium_155          154.9543210000
+lutetium_156          155.9530330000
+lutetium_157          156.9501270000
+lutetium_158          157.9493160000
+lutetium_159          158.9466360000
+lutetium_160          159.9460330000
+lutetium_161          160.9435720000
+lutetium_162          161.9432830000
+lutetium_163          162.9411790000
+lutetium_164          163.9413390000
+lutetium_165          164.9394070000
+lutetium_166          165.9398590000
+lutetium_167          166.9382700000
+lutetium_168          167.9387360000
+lutetium_169          168.9376441000
+lutetium_170          169.9384780000
+lutetium_171          170.9379170000
+lutetium_172          171.9390891000
+lutetium_173          172.9389340000
+lutetium_174          173.9403409000
+lutetium_175          174.9407752000   # 0.97401000
+lutetium_176          175.9426897000   # 0.02599000
+lutetium_177          176.9437615000
+lutetium_178          177.9459580000
+lutetium_179          178.9473309000
+lutetium_180          179.9498880000
+lutetium_181          180.9519100000
+lutetium_182          181.9550400000
+lutetium_183          182.9573630000
+lutetium_184          183.9609100000
+lutetium_185          184.9636200000
+lutetium          0.97401000 lutetium_175 \
+                + 0.02599000 lutetium_176
+
+# hafnium: Hf (72)  std atomic weight: 178.49(2)
+hafnium_153           152.9706900000
+hafnium_154           153.9648600000
+hafnium_155           154.9631100000
+hafnium_156           155.9593500000
+hafnium_157           156.9582400000
+hafnium_158           157.9548010000
+hafnium_159           158.9539960000
+hafnium_160           159.9506910000
+hafnium_161           160.9502780000
+hafnium_162           161.9472148000
+hafnium_163           162.9471130000
+hafnium_164           163.9443710000
+hafnium_165           164.9445670000
+hafnium_166           165.9421800000
+hafnium_167           166.9426000000
+hafnium_168           167.9405680000
+hafnium_169           168.9412590000
+hafnium_170           169.9396090000
+hafnium_171           170.9404920000
+hafnium_172           171.9394500000
+hafnium_173           172.9405130000
+hafnium_174           173.9400461000   # 0.00160000
+hafnium_175           174.9415092000
+hafnium_176           175.9414076000   # 0.05260000
+hafnium_177           176.9432277000   # 0.18600000
+hafnium_178           177.9437058000   # 0.27280000
+hafnium_179           178.9458232000   # 0.13620000
+hafnium_180           179.9465570000   # 0.35080000
+hafnium_181           180.9491083000
+hafnium_182           181.9505612000
+hafnium_183           182.9535300000
+hafnium_184           183.9554460000
+hafnium_185           184.9588620000
+hafnium_186           185.9608970000
+hafnium_187           186.9647700000
+hafnium_188           187.9668500000
+hafnium_189           188.9708400000
+hafnium           0.00160000 hafnium_174 \
+                + 0.05260000 hafnium_176 \
+                + 0.18600000 hafnium_177 \
+                + 0.27280000 hafnium_178 \
+                + 0.13620000 hafnium_179 \
+                + 0.35080000 hafnium_180
+
+# tantalum: Ta (73)  std atomic weight: 180.94788(2)
+tantalum_155          154.9742400000
+tantalum_156          155.9720300000
+tantalum_157          156.9681800000
+tantalum_158          157.9665400000
+tantalum_159          158.9630230000
+tantalum_160          159.9614880000
+tantalum_161          160.9584520000
+tantalum_162          161.9572940000
+tantalum_163          162.9543370000
+tantalum_164          163.9535340000
+tantalum_165          164.9507810000
+tantalum_166          165.9505120000
+tantalum_167          166.9480930000
+tantalum_168          167.9480470000
+tantalum_169          168.9460110000
+tantalum_170          169.9461750000
+tantalum_171          170.9444760000
+tantalum_172          171.9448950000
+tantalum_173          172.9437500000
+tantalum_174          173.9444540000
+tantalum_175          174.9437370000
+tantalum_176          175.9448570000
+tantalum_177          176.9444795000
+tantalum_178          177.9456780000
+tantalum_179          178.9459366000
+tantalum_180          179.9474648000   # 0.00012010
+tantalum_181          180.9479958000   # 0.99987990
+tantalum_182          181.9501519000
+tantalum_183          182.9513726000
+tantalum_184          183.9540080000
+tantalum_185          184.9555590000
+tantalum_186          185.9585510000
+tantalum_187          186.9603860000
+tantalum_188          187.9639160000
+tantalum_189          188.9658300000
+tantalum_190          189.9693900000
+tantalum_191          190.9715600000
+tantalum_192          191.9751400000
+tantalum          0.00012010 tantalum_180 \
+                + 0.99987990 tantalum_181
+
+# tungsten: W (74)  std atomic weight: 183.84(1)
+tungsten_157          156.9788400000
+tungsten_158          157.9745600000
+tungsten_159          158.9726400000
+tungsten_160          159.9684600000
+tungsten_161          160.9672000000
+tungsten_162          161.9634990000
+tungsten_163          162.9625240000
+tungsten_164          163.9589610000
+tungsten_165          164.9582810000
+tungsten_166          165.9550310000
+tungsten_167          166.9548050000
+tungsten_168          167.9518060000
+tungsten_169          168.9517790000
+tungsten_170          169.9492320000
+tungsten_171          170.9494510000
+tungsten_172          171.9472920000
+tungsten_173          172.9476890000
+tungsten_174          173.9460790000
+tungsten_175          174.9467170000
+tungsten_176          175.9456340000
+tungsten_177          176.9466430000
+tungsten_178          177.9458830000
+tungsten_179          178.9470770000
+tungsten_180          179.9467108000   # 0.00120000
+tungsten_181          180.9481978000
+tungsten_182          181.9482039400   # 0.26500000
+tungsten_183          182.9502227500   # 0.14310000
+tungsten_184          183.9509309200   # 0.30640000
+tungsten_185          184.9534189700
+tungsten_186          185.9543628000   # 0.28430000
+tungsten_187          186.9571588000
+tungsten_188          187.9584862000
+tungsten_189          188.9617630000
+tungsten_190          189.9630910000
+tungsten_191          190.9665310000
+tungsten_192          191.9681700000
+tungsten_193          192.9717800000
+tungsten_194          193.9736700000
+tungsten          0.00120000 tungsten_180 \
+                + 0.26500000 tungsten_182 \
+                + 0.14310000 tungsten_183 \
+                + 0.30640000 tungsten_184 \
+                + 0.28430000 tungsten_186
+
+# rhenium: Re (75)  std atomic weight: 186.207(1)
+rhenium_159           158.9841800000
+rhenium_160           159.9818200000
+rhenium_161           160.9775700000
+rhenium_162           161.9758400000
+rhenium_163           162.9720800000
+rhenium_164           163.9704530000
+rhenium_165           164.9671030000
+rhenium_166           165.9657610000
+rhenium_167           166.9625950000
+rhenium_168           167.9615730000
+rhenium_169           168.9587660000
+rhenium_170           169.9582200000
+rhenium_171           170.9557160000
+rhenium_172           171.9554200000
+rhenium_173           172.9532430000
+rhenium_174           173.9531150000
+rhenium_175           174.9513810000
+rhenium_176           175.9516230000
+rhenium_177           176.9503280000
+rhenium_178           177.9509890000
+rhenium_179           178.9499890000
+rhenium_180           179.9507920000
+rhenium_181           180.9500580000
+rhenium_182           181.9512100000
+rhenium_183           182.9508196000
+rhenium_184           183.9525228000
+rhenium_185           184.9529545000   # 0.37400000
+rhenium_186           185.9549856000
+rhenium_187           186.9557501000   # 0.62600000
+rhenium_188           187.9581115000
+rhenium_189           188.9592260000
+rhenium_190           189.9617440000
+rhenium_191           190.9631220000
+rhenium_192           191.9660880000
+rhenium_193           192.9675410000
+rhenium_194           193.9707600000
+rhenium_195           194.9725400000
+rhenium_196           195.9758000000
+rhenium_197           196.9779900000
+rhenium_198           197.9816000000
+rhenium           0.37400000 rhenium_185 \
+                + 0.62600000 rhenium_187
+
+# osmium: Os (76)  std atomic weight: 190.23(3)
+osmium_161            160.9890300000
+osmium_162            161.9844300000
+osmium_163            162.9824100000
+osmium_164            163.9780200000
+osmium_165            164.9766000000
+osmium_166            165.9726920000
+osmium_167            166.9715490000
+osmium_168            167.9678080000
+osmium_169            168.9670180000
+osmium_170            169.9635780000
+osmium_171            170.9631740000
+osmium_172            171.9600170000
+osmium_173            172.9598080000
+osmium_174            173.9570640000
+osmium_175            174.9569450000
+osmium_176            175.9548060000
+osmium_177            176.9549660000
+osmium_178            177.9532540000
+osmium_179            178.9538170000
+osmium_180            179.9523750000
+osmium_181            180.9532470000
+osmium_182            181.9521100000
+osmium_183            182.9531250000
+osmium_184            183.9524885000   # 0.00020000
+osmium_185            184.9540417000
+osmium_186            185.9538350000   # 0.01590000
+osmium_187            186.9557474000   # 0.01960000
+osmium_188            187.9558352000   # 0.13240000
+osmium_189            188.9581442000   # 0.16150000
+osmium_190            189.9584437000   # 0.26260000
+osmium_191            190.9609264000
+osmium_192            191.9614770000   # 0.40780000
+osmium_193            192.9641479000
+osmium_194            193.9651772000
+osmium_195            194.9683180000
+osmium_196            195.9696410000
+osmium_197            196.9728300000
+osmium_198            197.9744100000
+osmium_199            198.9780100000
+osmium_200            199.9798400000
+osmium_201            200.9836400000
+osmium_202            201.9859500000
+osmium            0.00020000 osmium_184 \
+                + 0.01590000 osmium_186 \
+                + 0.01960000 osmium_187 \
+                + 0.13240000 osmium_188 \
+                + 0.16150000 osmium_189 \
+                + 0.26260000 osmium_190 \
+                + 0.40780000 osmium_192
+
+# iridium: Ir (77)  std atomic weight: 192.217(3)
+iridium_164           163.9919100000
+iridium_165           164.9875000000
+iridium_166           165.9856600000
+iridium_167           166.9816660000
+iridium_168           167.9799070000
+iridium_169           168.9762980000
+iridium_170           169.9749220000
+iridium_171           170.9716400000
+iridium_172           171.9706070000
+iridium_173           172.9675060000
+iridium_174           173.9668610000
+iridium_175           174.9641500000
+iridium_176           175.9636500000
+iridium_177           176.9613010000
+iridium_178           177.9610820000
+iridium_179           178.9591200000
+iridium_180           179.9592290000
+iridium_181           180.9576250000
+iridium_182           181.9580760000
+iridium_183           182.9568400000
+iridium_184           183.9574760000
+iridium_185           184.9566980000
+iridium_186           185.9579440000
+iridium_187           186.9575420000
+iridium_188           187.9588280000
+iridium_189           188.9587150000
+iridium_190           189.9605412000
+iridium_191           190.9605893000   # 0.37300000
+iridium_192           191.9626002000
+iridium_193           192.9629216000   # 0.62700000
+iridium_194           193.9650735000
+iridium_195           194.9659747000
+iridium_196           195.9683970000
+iridium_197           196.9696550000
+iridium_198           197.9722800000
+iridium_199           198.9738050000
+iridium_200           199.9768000000
+iridium_201           200.9786400000
+iridium_202           201.9819900000
+iridium_203           202.9842300000
+iridium_204           203.9896000000
+iridium           0.37300000 iridium_191 \
+                + 0.62700000 iridium_193
+
+# platinum: Pt (78)  std atomic weight: 195.084(9)
+platinum_166          165.9948600000
+platinum_167          166.9926900000
+platinum_168          167.9881300000
+platinum_169          168.9865700000
+platinum_170          169.9824960000
+platinum_171          170.9812450000
+platinum_172          171.9773510000
+platinum_173          172.9764430000
+platinum_174          173.9728200000
+platinum_175          174.9724100000
+platinum_176          175.9689380000
+platinum_177          176.9684700000
+platinum_178          177.9656500000
+platinum_179          178.9653590000
+platinum_180          179.9630320000
+platinum_181          180.9630980000
+platinum_182          181.9611720000
+platinum_183          182.9615970000
+platinum_184          183.9599150000
+platinum_185          184.9606140000
+platinum_186          185.9593510000
+platinum_187          186.9606170000
+platinum_188          187.9593889000
+platinum_189          188.9608310000
+platinum_190          189.9599297000   # 0.00012000
+platinum_191          190.9616729000
+platinum_192          191.9610387000   # 0.00782000
+platinum_193          192.9629824000
+platinum_194          193.9626809000   # 0.32860000
+platinum_195          194.9647917000   # 0.33780000
+platinum_196          195.9649520900   # 0.25210000
+platinum_197          196.9673406900
+platinum_198          197.9678949000   # 0.07356000
+platinum_199          198.9705952000
+platinum_200          199.9714430000
+platinum_201          200.9745130000
+platinum_202          201.9756390000
+platinum_203          202.9789300000
+platinum_204          203.9807600000
+platinum_205          204.9860800000
+platinum_206          205.9896600000
+platinum          0.00012000 platinum_190 \
+                + 0.00782000 platinum_192 \
+                + 0.32860000 platinum_194 \
+                + 0.33780000 platinum_195 \
+                + 0.25210000 platinum_196 \
+                + 0.07356000 platinum_198
+
+# gold: Au (79)  std atomic weight: 196.966569(5)
+gold_169              168.9980800000
+gold_170              169.9959700000
+gold_171              170.9918760000
+gold_172              171.9899420000
+gold_173              172.9862410000
+gold_174              173.9847170000
+gold_175              174.9813040000
+gold_176              175.9802500000
+gold_177              176.9768700000
+gold_178              177.9760320000
+gold_179              178.9731740000
+gold_180              179.9725230000
+gold_181              180.9700790000
+gold_182              181.9696180000
+gold_183              182.9675910000
+gold_184              183.9674520000
+gold_185              184.9657900000
+gold_186              185.9659530000
+gold_187              186.9645430000
+gold_188              187.9653490000
+gold_189              188.9639480000
+gold_190              189.9646980000
+gold_191              190.9637020000
+gold_192              191.9648140000
+gold_193              192.9641373000
+gold_194              193.9654178000
+gold_195              194.9650352000
+gold_196              195.9665699000
+gold_197              196.9665687900   # 1.00000000
+gold_198              197.9682424200
+gold_199              198.9687652800
+gold_200              199.9707560000
+gold_201              200.9716575000
+gold_202              201.9738560000
+gold_203              202.9751544000
+gold_204              203.9778300000
+gold_205              204.9798500000
+gold_206              205.9847400000
+gold_207              206.9884000000
+gold_208              207.9934500000
+gold_209              208.9973500000
+gold_210              210.0025000000
+gold                  gold_197
+
+# mercury: Hg (80)  std atomic weight: 200.592(3)
+mercury_171           171.0035300000
+mercury_172           171.9988100000
+mercury_173           172.9970900000
+mercury_174           173.9928650000
+mercury_175           174.9914410000
+mercury_176           175.9873610000
+mercury_177           176.9862770000
+mercury_178           177.9824840000
+mercury_179           178.9818310000
+mercury_180           179.9782600000
+mercury_181           180.9778190000
+mercury_182           181.9746890000
+mercury_183           182.9744448000
+mercury_184           183.9717140000
+mercury_185           184.9718990000
+mercury_186           185.9693620000
+mercury_187           186.9698140000
+mercury_188           187.9675670000
+mercury_189           188.9681950000
+mercury_190           189.9663230000
+mercury_191           190.9671570000
+mercury_192           191.9656350000
+mercury_193           192.9666530000
+mercury_194           193.9654491000
+mercury_195           194.9667210000
+mercury_196           195.9658326000   # 0.00150000
+mercury_197           196.9672128000
+mercury_198           197.9667686000   # 0.09970000
+mercury_199           198.9682806400   # 0.16870000
+mercury_200           199.9683265900   # 0.23100000
+mercury_201           200.9703028400   # 0.13180000
+mercury_202           201.9706434000   # 0.29860000
+mercury_203           202.9728728000
+mercury_204           203.9734939800   # 0.06870000
+mercury_205           204.9760734000
+mercury_206           205.9775140000
+mercury_207           206.9823000000
+mercury_208           207.9857590000
+mercury_209           208.9907200000
+mercury_210           209.9942400000
+mercury_211           210.9993300000
+mercury_212           212.0029600000
+mercury_213           213.0082300000
+mercury_214           214.0120000000
+mercury_215           215.0174000000
+mercury_216           216.0213200000
+mercury           0.00150000 mercury_196 \
+                + 0.09970000 mercury_198 \
+                + 0.16870000 mercury_199 \
+                + 0.23100000 mercury_200 \
+                + 0.13180000 mercury_201 \
+                + 0.29860000 mercury_202 \
+                + 0.06870000 mercury_204
+
+# thallium: Tl (81)  std atomic weight: [204.382,204.385]
+thallium_176          176.0006240000
+thallium_177          176.9964310000
+thallium_178          177.9948500000
+thallium_179          178.9911110000
+thallium_180          179.9900570000
+thallium_181          180.9862600000
+thallium_182          181.9857130000
+thallium_183          182.9821930000
+thallium_184          183.9818860000
+thallium_185          184.9787890000
+thallium_186          185.9786510000
+thallium_187          186.9759063000
+thallium_188          187.9760210000
+thallium_189          188.9735880000
+thallium_190          189.9738280000
+thallium_191          190.9717842000
+thallium_192          191.9722250000
+thallium_193          192.9705020000
+thallium_194          193.9710810000
+thallium_195          194.9697740000
+thallium_196          195.9704810000
+thallium_197          196.9695760000
+thallium_198          197.9704830000
+thallium_199          198.9698770000
+thallium_200          199.9709633000
+thallium_201          200.9708220000
+thallium_202          201.9721020000
+thallium_203          202.9723446000   # 0.29520000
+thallium_204          203.9738639000
+thallium_205          204.9744278000   # 0.70480000
+thallium_206          205.9761106000
+thallium_207          206.9774197000
+thallium_208          207.9820190000
+thallium_209          208.9853594000
+thallium_210          209.9900740000
+thallium_211          210.9934750000
+thallium_212          211.9983400000
+thallium_213          213.0019150000
+thallium_214          214.0069400000
+thallium_215          215.0106400000
+thallium_216          216.0158000000
+thallium_217          217.0196600000
+thallium_218          218.0247900000
+thallium          0.29520000 thallium_203 \
+                + 0.70480000 thallium_205
+
+# lead: Pb (82)  std atomic weight: 207.2(1)
+lead_178              178.0038310000
+lead_179              179.0022010000
+lead_180              179.9979280000
+lead_181              180.9966530000
+lead_182              181.9926720000
+lead_183              182.9918720000
+lead_184              183.9881360000
+lead_185              184.9876100000
+lead_186              185.9842380000
+lead_187              186.9839109000
+lead_188              187.9808750000
+lead_189              188.9808070000
+lead_190              189.9780820000
+lead_191              190.9782760000
+lead_192              191.9757750000
+lead_193              192.9761730000
+lead_194              193.9740120000
+lead_195              194.9745430000
+lead_196              195.9727740000
+lead_197              196.9734312000
+lead_198              197.9720340000
+lead_199              198.9729130000
+lead_200              199.9718190000
+lead_201              200.9728830000
+lead_202              201.9721520000
+lead_203              202.9733911000
+lead_204              203.9730440000   # 0.01400000
+lead_205              204.9744822000
+lead_206              205.9744657000   # 0.24100000
+lead_207              206.9758973000   # 0.22100000
+lead_208              207.9766525000   # 0.52400000
+lead_209              208.9810905000
+lead_210              209.9841889000
+lead_211              210.9887371000
+lead_212              211.9918977000
+lead_213              212.9965629000
+lead_214              213.9998059000
+lead_215              215.0047400000
+lead_216              216.0080300000
+lead_217              217.0131400000
+lead_218              218.0165900000
+lead_219              219.0217700000
+lead_220              220.0254100000
+lead              0.01400000 lead_204 \
+                + 0.24100000 lead_206 \
+                + 0.22100000 lead_207 \
+                + 0.52400000 lead_208
+
+# bismuth: Bi (83)  std atomic weight: 208.98040(1)
+bismuth_184           184.0012750000
+bismuth_185           184.9976000000
+bismuth_186           185.9966440000
+bismuth_187           186.9931470000
+bismuth_188           187.9922870000
+bismuth_189           188.9891950000
+bismuth_190           189.9886220000
+bismuth_191           190.9857866000
+bismuth_192           191.9854690000
+bismuth_193           192.9829600000
+bismuth_194           193.9827850000
+bismuth_195           194.9806488000
+bismuth_196           195.9806670000
+bismuth_197           196.9788651000
+bismuth_198           197.9792060000
+bismuth_199           198.9776730000
+bismuth_200           199.9781310000
+bismuth_201           200.9770100000
+bismuth_202           201.9777340000
+bismuth_203           202.9768930000
+bismuth_204           203.9778361000
+bismuth_205           204.9773867000
+bismuth_206           205.9784993000
+bismuth_207           206.9784710000
+bismuth_208           207.9797425000
+bismuth_209           208.9803991000   # 1.00000000
+bismuth_210           209.9841207000
+bismuth_211           210.9872697000
+bismuth_212           211.9912860000
+bismuth_213           212.9943851000
+bismuth_214           213.9987120000
+bismuth_215           215.0017700000
+bismuth_216           216.0063060000
+bismuth_217           217.0093720000
+bismuth_218           218.0141880000
+bismuth_219           219.0174800000
+bismuth_220           220.0223500000
+bismuth_221           221.0258700000
+bismuth_222           222.0307800000
+bismuth_223           223.0345000000
+bismuth_224           224.0394700000
+bismuth               bismuth_209
+
+# polonium: Po (84)  std atomic weight: [209]
+polonium_186          186.0043930000
+polonium_187          187.0030410000
+polonium_188          187.9994160000
+polonium_189          188.9984730000
+polonium_190          189.9951010000
+polonium_191          190.9945585000
+polonium_192          191.9913360000
+polonium_193          192.9910260000
+polonium_194          193.9881860000
+polonium_195          194.9881260000
+polonium_196          195.9855260000
+polonium_197          196.9856600000
+polonium_198          197.9833890000
+polonium_199          198.9836670000
+polonium_200          199.9817990000
+polonium_201          200.9822598000
+polonium_202          201.9807580000
+polonium_203          202.9814161000
+polonium_204          203.9803100000
+polonium_205          204.9812030000
+polonium_206          205.9804740000
+polonium_207          206.9815938000
+polonium_208          207.9812461000
+polonium_209          208.9824308000
+polonium_210          209.9828741000
+polonium_211          210.9866536000
+polonium_212          211.9888684000
+polonium_213          212.9928576000
+polonium_214          213.9952017000
+polonium_215          214.9994201000
+polonium_216          216.0019152000
+polonium_217          217.0063182000
+polonium_218          218.0089735000
+polonium_219          219.0136140000
+polonium_220          220.0163860000
+polonium_221          221.0212280000
+polonium_222          222.0241400000
+polonium_223          223.0290700000
+polonium_224          224.0321100000
+polonium_225          225.0370700000
+polonium_226          226.0403100000
+polonium_227          227.0453900000
+polonium              polonium_209     # most stable
+
+# astatine: At (85)  std atomic weight: [210]
+astatine_191          191.0041480000
+astatine_192          192.0031520000
+astatine_193          192.9999270000
+astatine_194          193.9992360000
+astatine_195          194.9962685000
+astatine_196          195.9958000000
+astatine_197          196.9931890000
+astatine_198          197.9927840000
+astatine_199          198.9905277000
+astatine_200          199.9903510000
+astatine_201          200.9884171000
+astatine_202          201.9886300000
+astatine_203          202.9869430000
+astatine_204          203.9872510000
+astatine_205          204.9860760000
+astatine_206          205.9866570000
+astatine_207          206.9858000000
+astatine_208          207.9866133000
+astatine_209          208.9861702000
+astatine_210          209.9871479000
+astatine_211          210.9874966000
+astatine_212          211.9907377000
+astatine_213          212.9929370000
+astatine_214          213.9963721000
+astatine_215          214.9986528000
+astatine_216          216.0024236000
+astatine_217          217.0047192000
+astatine_218          218.0086950000
+astatine_219          219.0111618000
+astatine_220          220.0154330000
+astatine_221          221.0180170000
+astatine_222          222.0224940000
+astatine_223          223.0251510000
+astatine_224          224.0297490000
+astatine_225          225.0326300000
+astatine_226          226.0371600000
+astatine_227          227.0402400000
+astatine_228          228.0447500000
+astatine_229          229.0481200000
+astatine              astatine_210     # most stable
+
+# radon: Rn (86)  std atomic weight: [222]
+radon_193             193.0097080000
+radon_194             194.0061440000
+radon_195             195.0054220000
+radon_196             196.0021160000
+radon_197             197.0015850000
+radon_198             197.9986790000
+radon_199             198.9983900000
+radon_200             199.9956900000
+radon_201             200.9956280000
+radon_202             201.9932640000
+radon_203             202.9933880000
+radon_204             203.9914300000
+radon_205             204.9917190000
+radon_206             205.9902140000
+radon_207             206.9907303000
+radon_208             207.9896350000
+radon_209             208.9904150000
+radon_210             209.9896891000
+radon_211             210.9906011000
+radon_212             211.9907039000
+radon_213             212.9938831000
+radon_214             213.9953630000
+radon_215             214.9987459000
+radon_216             216.0002719000
+radon_217             217.0039280000
+radon_218             218.0056016000
+radon_219             219.0094804000
+radon_220             220.0113941000
+radon_221             221.0155371000
+radon_222             222.0175782000
+radon_223             223.0218893000
+radon_224             224.0240960000
+radon_225             225.0284860000
+radon_226             226.0308610000
+radon_227             227.0353040000
+radon_228             228.0378350000
+radon_229             229.0422570000
+radon_230             230.0451400000
+radon_231             231.0498700000
+radon                 radon_222        # most stable
+
+# francium: Fr (87)  std atomic weight: [223]
+francium_199          199.0072590000
+francium_200          200.0065860000
+francium_201          201.0038670000
+francium_202          202.0033200000
+francium_203          203.0009407000
+francium_204          204.0006520000
+francium_205          204.9985939000
+francium_206          205.9986660000
+francium_207          206.9969460000
+francium_208          207.9971380000
+francium_209          208.9959550000
+francium_210          209.9964220000
+francium_211          210.9955560000
+francium_212          211.9962257000
+francium_213          212.9961860000
+francium_214          213.9989713000
+francium_215          215.0003418000
+francium_216          216.0031899000
+francium_217          217.0046323000
+francium_218          218.0075787000
+francium_219          219.0092524000
+francium_220          220.0123277000
+francium_221          221.0142552000
+francium_222          222.0175520000
+francium_223          223.0197360000
+francium_224          224.0233980000
+francium_225          225.0255730000
+francium_226          226.0295660000
+francium_227          227.0318690000
+francium_228          228.0358230000
+francium_229          229.0382980000
+francium_230          230.0424160000
+francium_231          231.0451580000
+francium_232          232.0493700000
+francium_233          233.0526400000
+francium              francium_223     # most stable
+
+# radium: Ra (88)  std atomic weight: [226]
+radium_201            201.0127100000
+radium_202            202.0097600000
+radium_203            203.0093040000
+radium_204            204.0064920000
+radium_205            205.0062680000
+radium_206            206.0038280000
+radium_207            207.0037990000
+radium_208            208.0018410000
+radium_209            209.0019900000
+radium_210            210.0004940000
+radium_211            211.0008932000
+radium_212            211.9997870000
+radium_213            213.0003840000
+radium_214            214.0000997000
+radium_215            215.0027204000
+radium_216            216.0035334000
+radium_217            217.0063207000
+radium_218            218.0071410000
+radium_219            219.0100855000
+radium_220            220.0110259000
+radium_221            221.0139177000
+radium_222            222.0153748000
+radium_223            223.0185023000
+radium_224            224.0202120000
+radium_225            225.0236119000
+radium_226            226.0254103000
+radium_227            227.0291783000
+radium_228            228.0310707000
+radium_229            229.0349420000
+radium_230            230.0370550000
+radium_231            231.0410270000
+radium_232            232.0434753000
+radium_233            233.0475820000
+radium_234            234.0503420000
+radium_235            235.0549700000
+radium                radium_226       # most stable
+
+# actinium: Ac (89)  std atomic weight: [227]
+actinium_206          206.0144520000
+actinium_207          207.0119660000
+actinium_208          208.0115500000
+actinium_209          209.0094950000
+actinium_210          210.0094360000
+actinium_211          211.0077320000
+actinium_212          212.0078130000
+actinium_213          213.0066090000
+actinium_214          214.0069180000
+actinium_215          215.0064750000
+actinium_216          216.0087430000
+actinium_217          217.0093440000
+actinium_218          218.0116420000
+actinium_219          219.0124210000
+actinium_220          220.0147549000
+actinium_221          221.0155920000
+actinium_222          222.0178442000
+actinium_223          223.0191377000
+actinium_224          224.0217232000
+actinium_225          225.0232300000
+actinium_226          226.0260984000
+actinium_227          227.0277523000
+actinium_228          228.0310215000
+actinium_229          229.0329560000
+actinium_230          230.0363270000
+actinium_231          231.0383930000
+actinium_232          232.0420340000
+actinium_233          233.0443460000
+actinium_234          234.0481390000
+actinium_235          235.0508400000
+actinium_236          236.0549880000
+actinium_237          237.0582700000
+actinium              actinium_227     # most stable
+
+# thorium: Th (90)  std atomic weight: 232.0377(4)
+thorium_208           208.0179000000
+thorium_209           209.0177530000
+thorium_210           210.0150940000
+thorium_211           211.0149290000
+thorium_212           212.0129880000
+thorium_213           213.0130090000
+thorium_214           214.0115000000
+thorium_215           215.0117248000
+thorium_216           216.0110560000
+thorium_217           217.0131170000
+thorium_218           218.0132760000
+thorium_219           219.0155370000
+thorium_220           220.0157480000
+thorium_221           221.0181840000
+thorium_222           222.0184690000
+thorium_223           223.0208119000
+thorium_224           224.0214640000
+thorium_225           225.0239514000
+thorium_226           226.0249034000
+thorium_227           227.0277042000
+thorium_228           228.0287413000
+thorium_229           229.0317627000
+thorium_230           230.0331341000
+thorium_231           231.0363046000
+thorium_232           232.0380558000   # 1.00000000
+thorium_233           233.0415823000
+thorium_234           234.0436014000
+thorium_235           235.0472550000
+thorium_236           236.0496570000
+thorium_237           237.0536290000
+thorium_238           238.0565000000
+thorium_239           239.0607700000
+thorium               thorium_232
+
+# protactinium: Pa (91)  std atomic weight: 231.03588(2)
+protactinium_212      212.0232030000
+protactinium_213      213.0211090000
+protactinium_214      214.0209180000
+protactinium_215      215.0191830000
+protactinium_216      216.0191090000
+protactinium_217      217.0183250000
+protactinium_218      218.0200590000
+protactinium_219      219.0199040000
+protactinium_220      220.0217050000
+protactinium_221      221.0218750000
+protactinium_222      222.0237840000
+protactinium_223      223.0239630000
+protactinium_224      224.0256176000
+protactinium_225      225.0261310000
+protactinium_226      226.0279480000
+protactinium_227      227.0288054000
+protactinium_228      228.0310517000
+protactinium_229      229.0320972000
+protactinium_230      230.0345410000
+protactinium_231      231.0358842000   # 1.00000000
+protactinium_232      232.0385917000
+protactinium_233      233.0402472000
+protactinium_234      234.0433072000
+protactinium_235      235.0453990000
+protactinium_236      236.0486680000
+protactinium_237      237.0510230000
+protactinium_238      238.0546370000
+protactinium_239      239.0572600000
+protactinium_240      240.0609800000
+protactinium_241      241.0640800000
+protactinium          protactinium_231
+
+# uranium: U (92)  std atomic weight: 238.02891(3)
+uranium_217           217.0246600000
+uranium_218           218.0235230000
+uranium_219           219.0249990000
+uranium_220           220.0246200000
+uranium_221           221.0262800000
+uranium_222           222.0260000000
+uranium_223           223.0277390000
+uranium_224           224.0276050000
+uranium_225           225.0293910000
+uranium_226           226.0293390000
+uranium_227           227.0311570000
+uranium_228           228.0313710000
+uranium_229           229.0335063000
+uranium_230           230.0339401000
+uranium_231           231.0362939000
+uranium_232           232.0371563000
+uranium_233           233.0396355000
+uranium_234           234.0409523000   # 0.00005400
+uranium_235           235.0439301000   # 0.00720400
+uranium_236           236.0455682000
+uranium_237           237.0487304000
+uranium_238           238.0507884000   # 0.99274200
+uranium_239           239.0542935000
+uranium_240           240.0565934000
+uranium_241           241.0603300000
+uranium_242           242.0629300000
+uranium_243           243.0669900000
+uranium           0.00005400 uranium_234 \
+                + 0.00720400 uranium_235 \
+                + 0.99274200 uranium_238
+
+# neptunium: Np (93)  std atomic weight: [237]
+neptunium_219         219.0314300000
+neptunium_220         220.0325400000
+neptunium_221         221.0320400000
+neptunium_222         222.0333000000
+neptunium_223         223.0328500000
+neptunium_224         224.0342200000
+neptunium_225         225.0339110000
+neptunium_226         226.0351880000
+neptunium_227         227.0349570000
+neptunium_228         228.0360670000
+neptunium_229         229.0362640000
+neptunium_230         230.0378280000
+neptunium_231         231.0382450000
+neptunium_232         232.0401100000
+neptunium_233         233.0407410000
+neptunium_234         234.0428953000
+neptunium_235         235.0440635000
+neptunium_236         236.0465700000
+neptunium_237         237.0481736000
+neptunium_238         238.0509466000
+neptunium_239         239.0529392000
+neptunium_240         240.0561650000
+neptunium_241         241.0582530000
+neptunium_242         242.0616400000
+neptunium_243         243.0642800000
+neptunium_244         244.0678500000
+neptunium_245         245.0708000000
+neptunium             neptunium_237    # most stable
+
+# plutonium: Pu (94)  std atomic weight: [244]
+plutonium_228         228.0387320000
+plutonium_229         229.0401440000
+plutonium_230         230.0396500000
+plutonium_231         231.0411020000
+plutonium_232         232.0411850000
+plutonium_233         233.0429980000
+plutonium_234         234.0433174000
+plutonium_235         235.0452860000
+plutonium_236         236.0460581000
+plutonium_237         237.0484098000
+plutonium_238         238.0495601000
+plutonium_239         239.0521636000
+plutonium_240         240.0538138000
+plutonium_241         241.0568517000
+plutonium_242         242.0587428000
+plutonium_243         243.0620036000
+plutonium_244         244.0642053000
+plutonium_245         245.0678260000
+plutonium_246         246.0702050000
+plutonium_247         247.0741900000
+plutonium             plutonium_244    # most stable
+
+# americium: Am (95)
+americium_230         230.0460900000
+americium_231         231.0455600000
+americium_232         232.0464500000
+americium_233         233.0464400000
+americium_234         234.0477300000
+americium_235         235.0479080000
+americium_236         236.0494300000
+americium_237         237.0499960000
+americium_238         238.0519850000
+americium_239         239.0530247000
+americium_240         240.0553000000
+americium_241         241.0568293000
+americium_242         242.0595494000
+americium_243         243.0613813000
+americium_244         244.0642851000
+americium_245         245.0664548000
+americium_246         246.0697750000
+americium_247         247.0720900000
+americium_248         248.0757500000
+americium_249         249.0784800000
+
+# curium: Cm (96)
+curium_232            232.0498200000
+curium_233            233.0507700000
+curium_234            234.0501600000
+curium_235            235.0515400000
+curium_236            236.0513740000
+curium_237            237.0528690000
+curium_238            238.0530810000
+curium_239            239.0549100000
+curium_240            240.0555297000
+curium_241            241.0576532000
+curium_242            242.0588360000
+curium_243            243.0613893000
+curium_244            244.0627528000
+curium_245            245.0654915000
+curium_246            246.0672238000
+curium_247            247.0703541000
+curium_248            248.0723499000
+curium_249            249.0759548000
+curium_250            250.0783580000
+curium_251            251.0822860000
+curium_252            252.0848700000
+
+# berkelium: Bk (97)
+berkelium_234         234.0572700000
+berkelium_235         235.0565800000
+berkelium_236         236.0574800000
+berkelium_237         237.0571000000
+berkelium_238         238.0582000000
+berkelium_239         239.0582400000
+berkelium_240         240.0597600000
+berkelium_241         241.0601600000
+berkelium_242         242.0619800000
+berkelium_243         243.0630078000
+berkelium_244         244.0651810000
+berkelium_245         245.0663618000
+berkelium_246         246.0686730000
+berkelium_247         247.0703073000
+berkelium_248         248.0730880000
+berkelium_249         249.0749877000
+berkelium_250         250.0783167000
+berkelium_251         251.0807620000
+berkelium_252         252.0843100000
+berkelium_253         253.0868800000
+berkelium_254         254.0906000000
+
+# californium: Cf (98)
+californium_237       237.0621980000
+californium_238       238.0614900000
+californium_239       239.0625300000
+californium_240       240.0622560000
+californium_241       241.0636900000
+californium_242       242.0637540000
+californium_243       243.0654800000
+californium_244       244.0660008000
+californium_245       245.0680487000
+californium_246       246.0688055000
+californium_247       247.0709650000
+californium_248       248.0721851000
+californium_249       249.0748539000
+californium_250       250.0764062000
+californium_251       251.0795886000
+californium_252       252.0816272000
+californium_253       253.0851345000
+californium_254       254.0873240000
+californium_255       255.0910500000
+californium_256       256.0934400000
+
+# einsteinium: Es (99)
+einsteinium_239       239.0682300000
+einsteinium_240       240.0689200000
+einsteinium_241       241.0685600000
+einsteinium_242       242.0695700000
+einsteinium_243       243.0695100000
+einsteinium_244       244.0708800000
+einsteinium_245       245.0712500000
+einsteinium_246       246.0729000000
+einsteinium_247       247.0736220000
+einsteinium_248       248.0754710000
+einsteinium_249       249.0764110000
+einsteinium_250       250.0786100000
+einsteinium_251       251.0799936000
+einsteinium_252       252.0829800000
+einsteinium_253       253.0848257000
+einsteinium_254       254.0880222000
+einsteinium_255       255.0902750000
+einsteinium_256       256.0936000000
+einsteinium_257       257.0959800000
+einsteinium_258       258.0995200000
+
+# fermium: Fm (100)
+fermium_241           241.0742100000
+fermium_242           242.0734300000
+fermium_243           243.0744600000
+fermium_244           244.0740400000
+fermium_245           245.0753500000
+fermium_246           246.0753500000
+fermium_247           247.0769400000
+fermium_248           248.0771865000
+fermium_249           249.0789275000
+fermium_250           250.0795210000
+fermium_251           251.0815400000
+fermium_252           252.0824671000
+fermium_253           253.0851846000
+fermium_254           254.0868544000
+fermium_255           255.0899640000
+fermium_256           256.0917745000
+fermium_257           257.0951061000
+fermium_258           258.0970800000
+fermium_259           259.1006000000
+fermium_260           260.1028100000
+
+# mendelevium: Md (101)
+mendelevium_245       245.0808100000
+mendelevium_246       246.0817100000
+mendelevium_247       247.0815200000
+mendelevium_248       248.0828200000
+mendelevium_249       249.0829100000
+mendelevium_250       250.0844100000
+mendelevium_251       251.0847740000
+mendelevium_252       252.0864300000
+mendelevium_253       253.0871440000
+mendelevium_254       254.0895900000
+mendelevium_255       255.0910841000
+mendelevium_256       256.0938900000
+mendelevium_257       257.0955424000
+mendelevium_258       258.0984315000
+mendelevium_259       259.1005100000
+mendelevium_260       260.1036500000
+mendelevium_261       261.1058300000
+mendelevium_262       262.1091000000
+
+# nobelium: No (102)
+nobelium_248          248.0865500000
+nobelium_249          249.0878000000
+nobelium_250          250.0875600000
+nobelium_251          251.0889400000
+nobelium_252          252.0889670000
+nobelium_253          253.0905641000
+nobelium_254          254.0909560000
+nobelium_255          255.0931910000
+nobelium_256          256.0942829000
+nobelium_257          257.0968878000
+nobelium_258          258.0982100000
+nobelium_259          259.1010300000
+nobelium_260          260.1026400000
+nobelium_261          261.1057000000
+nobelium_262          262.1074600000
+nobelium_263          263.1107100000
+nobelium_264          264.1127300000
+
+# lawrencium: Lr (103)
+lawrencium_251        251.0941800000
+lawrencium_252        252.0952600000
+lawrencium_253        253.0950900000
+lawrencium_254        254.0964800000
+lawrencium_255        255.0965620000
+lawrencium_256        256.0984940000
+lawrencium_257        257.0994180000
+lawrencium_258        258.1017600000
+lawrencium_259        259.1029020000
+lawrencium_260        260.1055000000
+lawrencium_261        261.1068800000
+lawrencium_262        262.1096100000
+lawrencium_263        263.1113600000
+lawrencium_264        264.1142000000
+lawrencium_265        265.1161900000
+lawrencium_266        266.1198300000
+
+# rutherfordium: Rf (104)
+rutherfordium_253     253.1004400000
+rutherfordium_254     254.1000500000
+rutherfordium_255     255.1012700000
+rutherfordium_256     256.1011520000
+rutherfordium_257     257.1029180000
+rutherfordium_258     258.1034280000
+rutherfordium_259     259.1055960000
+rutherfordium_260     260.1064400000
+rutherfordium_261     261.1087730000
+rutherfordium_262     262.1099200000
+rutherfordium_263     263.1124900000
+rutherfordium_264     264.1138800000
+rutherfordium_265     265.1166800000
+rutherfordium_266     266.1181700000
+rutherfordium_267     267.1217900000
+rutherfordium_268     268.1239700000
+
+# dubnium: Db (105)
+dubnium_255           255.1070700000
+dubnium_256           256.1078900000
+dubnium_257           257.1075800000
+dubnium_258           258.1092800000
+dubnium_259           259.1094920000
+dubnium_260           260.1113000000
+dubnium_261           261.1119200000
+dubnium_262           262.1140700000
+dubnium_263           263.1149900000
+dubnium_264           264.1174100000
+dubnium_265           265.1186100000
+dubnium_266           266.1210300000
+dubnium_267           267.1224700000
+dubnium_268           268.1256700000
+dubnium_269           269.1279100000
+dubnium_270           270.1313600000
+
+# seaborgium: Sg (106)
+seaborgium_258        258.1129800000
+seaborgium_259        259.1144000000
+seaborgium_260        260.1143840000
+seaborgium_261        261.1159490000
+seaborgium_262        262.1163370000
+seaborgium_263        263.1182900000
+seaborgium_264        264.1189300000
+seaborgium_265        265.1210900000
+seaborgium_266        266.1219800000
+seaborgium_267        267.1243600000
+seaborgium_268        268.1253900000
+seaborgium_269        269.1286300000
+seaborgium_270        270.1304300000
+seaborgium_271        271.1339300000
+seaborgium_272        272.1358900000
+seaborgium_273        273.1395800000
+
+# bohrium: Bh (107)
+bohrium_260           260.1216600000
+bohrium_261           261.1214500000
+bohrium_262           262.1229700000
+bohrium_263           263.1229200000
+bohrium_264           264.1245900000
+bohrium_265           265.1249100000
+bohrium_266           266.1267900000
+bohrium_267           267.1275000000
+bohrium_268           268.1296900000
+bohrium_269           269.1304200000
+bohrium_270           270.1333600000
+bohrium_271           271.1352600000
+bohrium_272           272.1382600000
+bohrium_273           273.1402400000
+bohrium_274           274.1435500000
+bohrium_275           275.1456700000
+
+# hassium: Hs (108)
+hassium_263           263.1285200000
+hassium_264           264.1283570000
+hassium_265           265.1297930000
+hassium_266           266.1300460000
+hassium_267           267.1316700000
+hassium_268           268.1318600000
+hassium_269           269.1337500000
+hassium_270           270.1342900000
+hassium_271           271.1371700000
+hassium_272           272.1385000000
+hassium_273           273.1416800000
+hassium_274           274.1433000000
+hassium_275           275.1466700000
+hassium_276           276.1484600000
+hassium_277           277.1519000000
+
+# meitnerium: Mt (109)
+meitnerium_265        265.1360000000
+meitnerium_266        266.1373700000
+meitnerium_267        267.1371900000
+meitnerium_268        268.1386500000
+meitnerium_269        269.1388200000
+meitnerium_270        270.1403300000
+meitnerium_271        271.1407400000
+meitnerium_272        272.1434100000
+meitnerium_273        273.1444000000
+meitnerium_274        274.1472400000
+meitnerium_275        275.1488200000
+meitnerium_276        276.1515900000
+meitnerium_277        277.1532700000
+meitnerium_278        278.1563100000
+meitnerium_279        279.1580800000
+
+# darmstadtium: Ds (110)
+darmstadtium_267      267.1437700000
+darmstadtium_268      268.1434800000
+darmstadtium_269      269.1447520000
+darmstadtium_270      270.1445840000
+darmstadtium_271      271.1459500000
+darmstadtium_272      272.1460200000
+darmstadtium_273      273.1485600000
+darmstadtium_274      274.1494100000
+darmstadtium_275      275.1520300000
+darmstadtium_276      276.1530300000
+darmstadtium_277      277.1559100000
+darmstadtium_278      278.1570400000
+darmstadtium_279      279.1601000000
+darmstadtium_280      280.1613100000
+darmstadtium_281      281.1645100000
+
+# roentgenium: Rg (111)
+roentgenium_272       272.1532700000
+roentgenium_273       273.1531300000
+roentgenium_274       274.1552500000
+roentgenium_275       275.1559400000
+roentgenium_276       276.1583300000
+roentgenium_277       277.1590700000
+roentgenium_278       278.1614900000
+roentgenium_279       279.1627200000
+roentgenium_280       280.1651400000
+roentgenium_281       281.1663600000
+roentgenium_282       282.1691200000
+roentgenium_283       283.1705400000
+
+# copernicium: Cn (112)
+copernicium_276       276.1614100000
+copernicium_277       277.1636400000
+copernicium_278       278.1641600000
+copernicium_279       279.1665400000
+copernicium_280       280.1671500000
+copernicium_281       281.1697500000
+copernicium_282       282.1705000000
+copernicium_283       283.1732700000
+copernicium_284       284.1741600000
+copernicium_285       285.1771200000
+
+# nihonium: Nh (113)
+nihonium_278          278.1705800000
+nihonium_279          279.1709500000
+nihonium_280          280.1729300000
+nihonium_281          281.1734800000
+nihonium_282          282.1756700000
+nihonium_283          283.1765700000
+nihonium_284          284.1787300000
+nihonium_285          285.1797300000
+nihonium_286          286.1822100000
+nihonium_287          287.1833900000
+
+# flerovium: Fl (114)
+flerovium_285         285.1836400000
+flerovium_286         286.1842300000
+flerovium_287         287.1867800000
+flerovium_288         288.1875700000
+flerovium_289         289.1904200000
+
+# moscovium: Uup (115)
+moscovium_287         287.1907000000
+moscovium_288         288.1927400000
+moscovium_289         289.1936300000
+moscovium_290         290.1959800000
+moscovium_291         291.1970700000
+
+# livermorium: Lv (116)
+livermorium_289       289.1981600000
+livermorium_290       290.1986400000
+livermorium_291       291.2010800000
+livermorium_292       292.2017400000
+livermorium_293       293.2044900000
+
+# tennessine: Uus (117)
+tennessine_291        291.2055300000
+tennessine_292        292.2074600000
+tennessine_293        293.2082400000
+tennessine_294        294.2104600000
+
+# oganesson: Og (118)
+oganesson_293         293.2135600000
+oganesson_294         294.2139200000
+oganesson_295         295.2162400000
+
diff --git a/getopt.c b/getopt.c
new file mode 100644
index 0000000..6d2c7de
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,759 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.org
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94
+   	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 3, 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, 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.  */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) && !defined (_MSC_VER) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif	/* GNU C library.  */
+
+/* This is for other GNU distributions with internationalized messages.
+   The GNU C Library itself does not yet support such messages.  */
+#if HAVE_LIBINTL_H
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+static const char *
+_getopt_initialize (optstring)
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind = 1;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0)
+    optstring = _getopt_initialize (optstring);
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc
+		 && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return EOF;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return EOF;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+	  || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, nextchar, nameend - nextchar))
+	  {
+	    if (nameend - nextchar == strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, gettext ("%s: option `%s' is ambiguous\n"),
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = nameend + 1;
+	      else
+		{
+		  if (opterr)
+		   if (argv[optind - 1][1] == '-')
+		    /* --option */
+		    fprintf (stderr,
+		     gettext ("%s: option `--%s' doesn't allow an argument\n"),
+		     argv[0], pfound->name);
+		   else
+		    /* +option or -option */
+		    fprintf (stderr,
+		     gettext ("%s: option `%c%s' doesn't allow an argument\n"),
+		     argv[0], argv[optind - 1][0], pfound->name);
+
+		  nextchar += strlen (nextchar);
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr,
+			   gettext ("%s: option `%s' requires an argument\n"),
+			   argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, gettext ("%s: unrecognized option `--%s'\n"),
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, gettext ("%s: unrecognized option `%c%s'\n"),
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+	    if (posixly_correct)
+	      /* 1003.2 specifies the format of this message.  */
+	      fprintf (stderr, gettext ("%s: illegal option -- %c\n"),
+		       argv[0], c);
+	    else
+	      fprintf (stderr, gettext ("%s: invalid option -- %c\n"),
+		       argv[0], c);
+	  }
+	optopt = c;
+	return '?';
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = NULL;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr,
+			   gettext ("%s: option requires an argument -- %c\n"),
+			   argv[0], c);
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif	/* _LIBC or not __GNU_LIBRARY__.  */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/getopt.h b/getopt.h
new file mode 100644
index 0000000..efdc3fb
--- /dev/null
+++ b/getopt.h
@@ -0,0 +1,135 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 90, 91, 92, 93, 94 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, write to the Free Software
+   Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */
+
+#ifdef STRINGS_H
+#  include <strings.h>
+#else
+#  include <string.h>
+#endif 
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument		(or 0) if the option does not take an argument,
+   required_argument	(or 1) if the option requires an argument,
+   optional_argument 	(or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define	no_argument		0
+#define required_argument	1
+#define optional_argument	2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+		        const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind,
+			     int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/getopt1.c b/getopt1.c
new file mode 100644
index 0000000..677ab2b
--- /dev/null
+++ b/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994
+	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 3, 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, 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) && !defined (_MSC_VER) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif	/* _LIBC or not __GNU_LIBRARY__.  */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"add", 1, 0, 0},
+	{"append", 0, 0, 0},
+	{"delete", 1, 0, 0},
+	{"verbose", 0, 0, 0},
+	{"create", 0, 0, 0},
+	{"file", 1, 0, 0},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+		       long_options, &option_index);
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case 0:
+	  printf ("option %s", long_options[option_index].name);
+	  if (optarg)
+	    printf (" with arg %s", optarg);
+	  printf ("\n");
+	  break;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case 'd':
+	  printf ("option d with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/icon_ms.png b/icon_ms.png
new file mode 100644
index 0000000..342eb52
--- /dev/null
+++ b/icon_ms.png
Binary files differ
diff --git a/locale_map.txt b/locale_map.txt
new file mode 100644
index 0000000..edafae6
--- /dev/null
+++ b/locale_map.txt
@@ -0,0 +1,225 @@
+Afrikaans_South Africa	af_ZA
+Albanian_Albania	sq_AL
+Alsatian_France	gsw_FR
+Amharic_Ethiopia	am_ET
+Arabic_Algeria	ar_DZ
+Arabic_Bahrain	ar_BH
+Arabic_Egypt	ar_EG
+Arabic_Iraq	ar_IQ
+Arabic_Jordan	ar_JO
+Arabic_Kuwait	ar_KW
+Arabic_Lebanon	ar_LB
+Arabic_Libya	ar_LY
+Arabic_Morocco	ar_MA
+Arabic_Oman	ar_OM
+Arabic_Qatar	ar_QA
+Arabic_Saudi Arabia	ar_SA
+Arabic_Syria	ar_SY
+Arabic_Tunisia	ar_TN
+Arabic_U.A.E.	ar_AE
+Arabic_Yemen	ar_YE
+Armenian_Armenia	hy_AM
+Assamese_India	as_IN
+Azeri_Azerbaijan, Cyrillic	az_AZ
+Azeri_Azerbaijan, Latin	az_AZ
+Bashkir_Russia	ba_RU
+Basque_Basque	eu_ES
+Belarusian_Belarus	be_BY
+Bengali_Bangladesh	bn_??
+Bengali_India	bn_IN
+Bosnian_Neutral	bs_??
+Bosnian_Bosnia and Herzegovina, Cyrillic	bs_BA
+Bosnian_Bosnia and Herzegovina, Latin	bs_BA
+Breton_France	br_FR
+Bulgarian_Bulgaria	bg_BG
+Central Kurdish_Iraq	ku_IQ
+Cherokee_Cherokee	chr_Cher
+Catalan_Spain	ca_ES
+Chinese_Hong Kong SAR, PRC	zh_HK
+Chinese_Macao SAR	zh_MO
+Chinese_Singapore	zh_SG
+Chinese_Simplified	zh_Hans
+Chinese_Traditional	zh_Hant
+Corsican_France	co_FR
+Croatian_Neutral	hr_??
+Croatian_Bosnia and Herzegovina, Latin	hr_BA
+Croatian_Croatia	hr_HR
+Czech_Czech Republic	cs_CZ
+Danish_Denmark	da_DK
+Dari_Afghanistan	prs_AF
+Divehi_Maldives	dv_MV
+Dutch_Belgium	nl_BE
+Dutch_Netherlands	nl_NL
+English_Australia	en_AU
+English_Belize	en_BZ
+English_Canada	en_CA
+English_Caribbean	en_029
+English_India	en_IN
+English_Ireland	en_IE
+English_Ireland	en_IE
+English_Jamaica	en_JM
+English_Malaysia	en_MY
+English_New Zealand	en_NZ
+English_Philippines	en_PH
+English_Singapore	en_SG
+English_South Africa	en_ZA
+English_Trinidad and Tobago	en_TT
+English_United Kingdom	en_GB
+English_United States	en_US
+English_Zimbabwe	en_ZW
+Estonian_Estonia	et_EE
+Faroese_Faroe Islands	fo_FO
+Filipino_Philippines	fil_PH
+Finnish_Finland	fi_FI
+French_Belgium	fr_BE
+French_Canada	fr_CA
+French_France	fr_FR
+French_Luxembourg	fr_LU
+French_Monaco	fr_MC
+French_Switzerland	fr_CH
+Frisian_Netherlands	fy_NL
+Galician_Spain	gl_ES
+Georgian_Georgia	ka_GE
+German_Austria	de_AT
+German_Germany	de_DE
+German_Liechtenstein	de_LI
+German_Luxembourg	de_LU
+German_Switzerland	de_CH
+Greek_Greece	el_GR
+Greenlandic_Greenland	kl_GL
+Gujarati_India	gu_IN
+Hausa_Nigeria	ha_NG
+Hawiian_United States	haw_US
+Hebrew_Israel	he_IL
+Hindi_India	hi_IN
+Hungarian_Hungary	hu_HU
+Icelandic_Iceland	is_IS
+Igbo_Nigeria	ig_NG
+Indonesian_Indonesia	id_ID
+Inuktitut_Canada	iu_CA, Latin
+Inuktitut_Canada	iu_CA, Canadian Syllabics
+Irish_Ireland	ga_IE
+isiXhosa_South Africa	xh_ZA
+isiZulu_South Africa	zu_ZA
+Italian_Italy	it_IT
+Italian_Switzerland	it_CH
+Japanese_Japan	ja_JP
+Kannada_India	kn_IN
+Kazakh_Kazakhstan	kk_KZ
+Khmer_Cambodia	kh_KH
+K'iche_Guatemala	qut_GT
+Kinyarwanda_Rwanda	rw_RW
+Konkani_India	kok_IN
+Korean_Korea	ko_KR
+Kyrgyz_Kyrgyzstan	ky_KG
+Lao_Lao PDR	lo_LA
+Latvian_Latvia	lv_LV
+Lithuanian_Lithuanian	lt_LT
+Lower Sorbian_Germany	dsb_DE
+Luxembourgish_Luxembourg	lb_LU
+Macedonian_Macedonia, FYROM	mk_MK
+Malay_Brunei Darassalam	ms_BN
+Malay_Malaysia	ms_MY
+Malayalam_India	ml_IN
+Maltese_Malta	mt_MT
+Maori_New Zealand	mi_NZ
+Mapudungun_Chile	arn_CL
+Marathi_India	mr_IN
+Mohawk_Canada	moh_CA
+Mongolian_Mongolia, Cyrillic	mn_MN
+Mongolian_Mongolia, Mong	mn_MN
+Nepali_Nepal	ne_NP
+Nepali_India	ne_IN
+Norwegian_Bokmål, Norway	no_NO
+Norwegian_Nynorsk, Norway	no_NO
+Occitan_France	oc_FR
+Oriya_India	or_IN
+Pashto_Afghanistan	ps_AF
+Persian_Iran	fa_IR
+Polish_Poland	pl_PL
+Portuguese_Brazil	pt_BR
+Portuguese_Portugal	pt_PT
+Pular_Senegal	ff_SN
+Punjabi_India, Gurmukhi script	pa_IN
+Punjabi_Pakistan, Arabic script	pa_PK
+Quechua_Bolivia	quz_BO
+Quechua_Ecuador	quz_EC
+Quechua_Peru	quz_PE
+Romanian_Romania	ro_RO
+Romansh_Switzerland	rm_CH
+Russian_Russia	ru_RU
+Sakha_Russia	sah_RU
+Sami_Inari, Finland	smn_FI
+Sami_Lule, Norway	smj_NO
+Sami_Lule, Sweden	smj_SE
+Sami_Northern, Finland	se_FI
+Sami_Northern, Norway	se_NO
+Sami_Northern, Sweden	se_SE
+Sami_Skolt, Finland	sms_FI
+Sami_Southern, Norway	sma_NO
+Sami_Southern, Sweden	sma_SE
+Sanskrit_India	sa_IN
+Serbian_Neutral	sr_??
+Serbian_Bosnia and Herzegovina, Cyrillic	sr_BA
+Serbian_Bosnia and Herzegovina, Latin	sr_BA
+Serbian_Croatia	sr_HR
+Serbian_Serbia and Montenegro, Former, Cyrillic	sr_CS
+Serbian_Serbia and Montenegro, Former, Latin	sr_CS
+Sesotho sa Leboa_South Africa	nso_ZA
+Setswana / Tswana_Botswana	tn_BW
+Setswana / Tswana_South Africa	tn_ZA
+Sindhi_Pakistan	sd_PK
+Sinhala_Sri Lanka	si_LK
+Slovak_Slovakia	sk_SK
+Slovenian_Slovenia	sl_SI
+Spanish_Argentina	es_AR
+Spanish_Bolivia	es_BO
+Spanish_Chile	es_CL
+Spanish_Colombia	es_CO
+Spanish_Costa Rica	es_CR
+Spanish_Dominican Republic	es_DO
+Spanish_Ecuador	es_EC
+Spanish_El Salvador	es_SV
+Spanish_Guatemala	es_GT
+Spanish_Honduras	es_HN
+Spanish_Mexico	es_MX
+Spanish_Nicaragua	es_NI
+Spanish_Panama	es_PA
+Spanish_Paraguay	es_PY
+Spanish_Peru	es_PE
+Spanish_Puerto Rico	es_PR
+Spanish_Spain, Modern Sort	es_ES
+Spanish_Spain, Traditional Sort	es_ES
+Spanish_United States	es_US
+Spanish_Uruguay	es_UY
+Spanish_Venezuela	es_VE
+Swahili_Kenya	sw_KE
+Swedish_Finland	sv_FI
+Swedish_Sweden	sv_SE
+Swedish_Sweden	sv_SE
+Syriac_Syria	syr_SY
+Tajik_Tajikistan, Cyrillic	tg_TJ
+Tamazight_Algeria, Latin	tzm_DZ
+Tamil_India	ta_IN
+Tamil_Sri Lanka	ta_LK
+Tatar_Russia	tt_RU
+Telugu_India	te_IN
+Thai_Thailand	th_TH
+Tibetan_PRC	bo_CN
+Tigrinya_Eritrea	ti_ER
+Tigrinya_Ethiopia	ti_ET
+Turkish_Turkey	tr_TR
+Turkmen_Turkmenistan	tk_TM
+Ukrainian_Ukraine	uk_UA
+Upper Sorbian_Germany	hsb_DE
+Urdu_(reserved)	ur_??
+Urdu_Pakistan	ur_PK
+Uyghur_PRC	ug_CN
+Uzbek_Uzbekistan, Cyrillic	uz_UZ
+Uzbek_Uzbekistan, Latin	uz_UZ
+Valencian_Valencia	ca_ES-Valencia
+Vietnamese_Vietnam	vi_VN
+Welsh_United Kingdom	cy_GB
+Wolof_Senegal	wo_SN
+Yi_PRC	ii_CN
+Yoruba_Nigeria	yo_NG
diff --git a/module-pre.js b/module-pre.js
new file mode 100644
index 0000000..bb23b71
--- /dev/null
+++ b/module-pre.js
@@ -0,0 +1,5 @@
+globalThis.printBuffer = '';
+
+Module['print'] = function (out) {
+    globalThis.printBuffer += out + "\n";
+};
diff --git a/parse.tab.c b/parse.tab.c
new file mode 100644
index 0000000..b1f6853
--- /dev/null
+++ b/parse.tab.c
@@ -0,0 +1,2164 @@
+/* A Bison parser, made by GNU Bison 3.8.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output, and Bison version.  */
+#define YYBISON 30802
+
+/* Bison version string.  */
+#define YYBISON_VERSION "3.8.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 2
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+/* Substitute the type names.  */
+#define YYSTYPE         UNITSSTYPE
+/* Substitute the variable and function names.  */
+#define yyparse         unitsparse
+#define yylex           unitslex
+#define yyerror         unitserror
+#define yydebug         unitsdebug
+#define yynerrs         unitsnerrs
+
+/* First part of user prologue.  */
+#line 24 "parse.y"
+
+#include<stdio.h>
+#include<float.h>
+#include "units.h"
+
+struct commtype {
+   int location;
+   const char *data;
+   struct unittype *result;
+   int errorcode;
+};
+
+static int err;  /* value used by parser to store return values */
+
+/* 
+   The CHECK macro aborts parse if an error has occurred.  It optionally
+   destroys a variable.  Call with CHECK(0) if no variables need destruction 
+   on error. 
+*/
+ 
+#define CHECK(var) if (err) { comm->errorcode=err; \
+                              if (var) destroyunit(var); \
+                              YYABORT; }
+ 
+int yylex();
+void yyerror(struct commtype *comm, char *);
+
+#define MAXMEM 100
+int unitcount=0;    /* Counts the number of units allocated by the parser */
+
+struct function { 
+   char *name; 
+   double (*func)(double); 
+   int type;
+}; 
+
+#define DIMENSIONLESS 0
+#define ANGLEIN 1
+#define ANGLEOUT 2
+#define NATURAL 3
+
+struct unittype *
+getnewunit()
+{
+  struct unittype *unit;
+
+  if (unitcount>=MAXMEM)
+    return 0;
+  unit = (struct unittype *) 
+    mymalloc(sizeof(struct unittype),"(getnewunit)");
+  if (!unit)
+    return 0;
+  initializeunit(unit);
+  unitcount++;
+  return unit;
+}
+
+
+void
+destroyunit(struct unittype *unit)
+{
+  freeunit(unit);
+  free(unit);
+  unitcount--;
+}  
+ 
+
+struct unittype *
+makenumunit(double num,int *myerr)
+{
+  struct unittype *ret;
+  ret=getnewunit();
+  if (!ret){
+    *myerr = E_PARSEMEM;
+    return 0;  
+  }
+  ret->factor = num;
+  *myerr = 0;
+  return ret;
+}
+
+int
+logunit(struct unittype *theunit, int base)
+{  
+  if ((err=unit2num(theunit)))
+    return err;
+  if (base==2)
+    theunit->factor = log2(theunit->factor);
+  else if (base==10)
+    theunit->factor = log10(theunit->factor);
+  else
+    theunit->factor = log(theunit->factor)/log((double)base);
+  if (errno)
+    return E_FUNC;
+  return 0;
+}
+ 
+int
+funcunit(struct unittype *theunit, struct function const *fun)
+{
+  struct unittype angleunit;
+  if (fun->type==ANGLEIN){
+    err=unit2num(theunit);
+    if (err==E_NOTANUMBER){
+      initializeunit(&angleunit);
+      angleunit.denominator[0] = dupstr("radian");
+      angleunit.denominator[1] = 0;
+      err = multunit(theunit, &angleunit);
+      freeunit(&angleunit);
+      if (!err)
+        err = unit2num(theunit);
+    }
+    if (err)
+      return err;
+  } else if (fun->type==ANGLEOUT || fun->type == DIMENSIONLESS || fun->type == NATURAL) {
+    if ((err=unit2num(theunit)))
+      return err;
+    if (fun->type==NATURAL && (theunit->factor<0 || trunc(theunit->factor)!=theunit->factor))
+      return E_NOTINDOMAIN;
+  } else 
+     return E_BADFUNCTYPE;
+  errno = 0;
+  theunit->factor = (*(fun->func))(theunit->factor);
+  if (errno)
+    return E_FUNC;
+  if (fun->type==ANGLEOUT) {
+    theunit->numerator[0] = dupstr("radian");
+    theunit->numerator[1] = 0;
+  }
+  return 0;
+}
+
+
+
+#line 212 "parse.tab.c"
+
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+#  else
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+#  endif
+# endif
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
+
+
+/* Debug traces.  */
+#ifndef UNITSDEBUG
+# if defined YYDEBUG
+#if YYDEBUG
+#   define UNITSDEBUG 1
+#  else
+#   define UNITSDEBUG 0
+#  endif
+# else /* ! defined YYDEBUG */
+#  define UNITSDEBUG 0
+# endif /* ! defined YYDEBUG */
+#endif  /* ! defined UNITSDEBUG */
+#if UNITSDEBUG
+extern int unitsdebug;
+#endif
+
+/* Token kinds.  */
+#ifndef UNITSTOKENTYPE
+# define UNITSTOKENTYPE
+  enum unitstokentype
+  {
+    UNITSEMPTY = -2,
+    UNITSEOF = 0,                  /* "end of file"  */
+    UNITSerror = 256,              /* error  */
+    UNITSUNDEF = 257,              /* "invalid token"  */
+    REAL = 258,                    /* REAL  */
+    UNIT = 259,                    /* UNIT  */
+    REALFUNC = 260,                /* REALFUNC  */
+    LOG = 261,                     /* LOG  */
+    UNITFUNC = 262,                /* UNITFUNC  */
+    EXPONENT = 263,                /* EXPONENT  */
+    MULTIPLY = 264,                /* MULTIPLY  */
+    MULTSTAR = 265,                /* MULTSTAR  */
+    DIVIDE = 266,                  /* DIVIDE  */
+    NUMDIV = 267,                  /* NUMDIV  */
+    SQRT = 268,                    /* SQRT  */
+    CUBEROOT = 269,                /* CUBEROOT  */
+    MULTMINUS = 270,               /* MULTMINUS  */
+    EOL = 271,                     /* EOL  */
+    FUNCINV = 272,                 /* FUNCINV  */
+    MEMERROR = 273,                /* MEMERROR  */
+    BADNUMBER = 274,               /* BADNUMBER  */
+    NUMOVERFLOW = 275,             /* NUMOVERFLOW  */
+    NUMUNDERFLOW = 276,            /* NUMUNDERFLOW  */
+    UNITEND = 277,                 /* UNITEND  */
+    LASTUNSET = 278,               /* LASTUNSET  */
+    ADD = 279,                     /* ADD  */
+    MINUS = 280,                   /* MINUS  */
+    UNARY = 281                    /* UNARY  */
+  };
+  typedef enum unitstokentype unitstoken_kind_t;
+#endif
+
+/* Value type.  */
+#if ! defined UNITSSTYPE && ! defined UNITSSTYPE_IS_DECLARED
+union UNITSSTYPE
+{
+#line 164 "parse.y"
+
+  double number;
+  int integer;
+  struct unittype *unit;
+  struct function *realfunc;
+  struct func *unitfunc;
+
+#line 301 "parse.tab.c"
+
+};
+typedef union UNITSSTYPE UNITSSTYPE;
+# define UNITSSTYPE_IS_TRIVIAL 1
+# define UNITSSTYPE_IS_DECLARED 1
+#endif
+
+
+
+
+int unitsparse (struct commtype *comm);
+
+
+
+/* Symbol kind.  */
+enum yysymbol_kind_t
+{
+  YYSYMBOL_YYEMPTY = -2,
+  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
+  YYSYMBOL_YYerror = 1,                    /* error  */
+  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
+  YYSYMBOL_REAL = 3,                       /* REAL  */
+  YYSYMBOL_UNIT = 4,                       /* UNIT  */
+  YYSYMBOL_REALFUNC = 5,                   /* REALFUNC  */
+  YYSYMBOL_LOG = 6,                        /* LOG  */
+  YYSYMBOL_UNITFUNC = 7,                   /* UNITFUNC  */
+  YYSYMBOL_EXPONENT = 8,                   /* EXPONENT  */
+  YYSYMBOL_MULTIPLY = 9,                   /* MULTIPLY  */
+  YYSYMBOL_MULTSTAR = 10,                  /* MULTSTAR  */
+  YYSYMBOL_DIVIDE = 11,                    /* DIVIDE  */
+  YYSYMBOL_NUMDIV = 12,                    /* NUMDIV  */
+  YYSYMBOL_SQRT = 13,                      /* SQRT  */
+  YYSYMBOL_CUBEROOT = 14,                  /* CUBEROOT  */
+  YYSYMBOL_MULTMINUS = 15,                 /* MULTMINUS  */
+  YYSYMBOL_EOL = 16,                       /* EOL  */
+  YYSYMBOL_FUNCINV = 17,                   /* FUNCINV  */
+  YYSYMBOL_MEMERROR = 18,                  /* MEMERROR  */
+  YYSYMBOL_BADNUMBER = 19,                 /* BADNUMBER  */
+  YYSYMBOL_NUMOVERFLOW = 20,               /* NUMOVERFLOW  */
+  YYSYMBOL_NUMUNDERFLOW = 21,              /* NUMUNDERFLOW  */
+  YYSYMBOL_UNITEND = 22,                   /* UNITEND  */
+  YYSYMBOL_LASTUNSET = 23,                 /* LASTUNSET  */
+  YYSYMBOL_ADD = 24,                       /* ADD  */
+  YYSYMBOL_MINUS = 25,                     /* MINUS  */
+  YYSYMBOL_UNARY = 26,                     /* UNARY  */
+  YYSYMBOL_27_ = 27,                       /* '('  */
+  YYSYMBOL_28_ = 28,                       /* ')'  */
+  YYSYMBOL_YYACCEPT = 29,                  /* $accept  */
+  YYSYMBOL_input = 30,                     /* input  */
+  YYSYMBOL_unitexpr = 31,                  /* unitexpr  */
+  YYSYMBOL_divlist = 32,                   /* divlist  */
+  YYSYMBOL_expr = 33,                      /* expr  */
+  YYSYMBOL_numexpr = 34,                   /* numexpr  */
+  YYSYMBOL_pexpr = 35,                     /* pexpr  */
+  YYSYMBOL_list = 36                       /* list  */
+};
+typedef enum yysymbol_kind_t yysymbol_kind_t;
+
+
+
+
+#ifdef short
+# undef short
+#endif
+
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+   <limits.h> and (if available) <stdint.h> are included
+   so that the code can choose integer types of a good width.  */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+#  define YY_STDINT_H
+# endif
+#endif
+
+/* Narrow types that promote to a signed type and that can represent a
+   signed or unsigned integer of at least N bits.  In tables they can
+   save space and decrease cache pressure.  Promoting to a signed type
+   helps avoid bugs in integer arithmetic.  */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
+#else
+typedef short yytype_int16;
+#endif
+
+/* Work around bug in HP-UX 11.23, which defines these macros
+   incorrectly for preprocessor constants.  This workaround can likely
+   be removed in 2023, as HPE has promised support for HP-UX 11.23
+   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
+#endif
+
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
+#else
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+#  define YYPTRDIFF_T __PTRDIFF_TYPE__
+#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+#  ifndef ptrdiff_t
+#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  endif
+#  define YYPTRDIFF_T ptrdiff_t
+#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+#  define YYPTRDIFF_T long
+#  define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM                                  \
+  YY_CAST (YYPTRDIFF_T,                                 \
+           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
+            ? YYPTRDIFF_MAXIMUM                         \
+            : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_int8 yy_state_t;
+
+/* State numbers in computations.  */
+typedef int yy_state_fast_t;
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+#  define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+#  define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YY_USE(E) ((void) (E))
+#else
+# define YY_USE(E) /* empty */
+#endif
+
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# endif
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+
+#define YY_ASSERT(E) ((void) (0 && (E)))
+
+#if !defined yyoverflow
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* !defined yyoverflow */
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined UNITSSTYPE_IS_TRIVIAL && UNITSSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yy_state_t yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYPTRDIFF_T yynewbytes;                                         \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYPTRDIFF_T yyi;                      \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  39
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   202
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  29
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  8
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  40
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  65
+
+/* YYMAXUTOK -- Last valid token kind.  */
+#define YYMAXUTOK   281
+
+
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
+#define YYTRANSLATE(YYX)                                \
+  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
+   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
+   : YYSYMBOL_YYUNDEF)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex.  */
+static const yytype_int8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      27,    28,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26
+};
+
+#if UNITSDEBUG
+/* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_int16 yyrline[] =
+{
+       0,   213,   213,   215,   216,   219,   220,   223,   224,   228,
+     229,   230,   231,   233,   236,   238,   240,   244,   245,   248,
+     254,   255,   256,   258,   260,   262,   263,   264,   265,   266,
+     267,   268,   269,   272,   275,   276,   277,   278,   279,   280,
+     281
+};
+#endif
+
+/** Accessing symbol of state STATE.  */
+#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])
+
+#if UNITSDEBUG || 0
+/* The user-facing name of the symbol whose (internal) number is
+   YYSYMBOL.  No bounds checking.  */
+static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;
+
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "\"end of file\"", "error", "\"invalid token\"", "REAL", "UNIT",
+  "REALFUNC", "LOG", "UNITFUNC", "EXPONENT", "MULTIPLY", "MULTSTAR",
+  "DIVIDE", "NUMDIV", "SQRT", "CUBEROOT", "MULTMINUS", "EOL", "FUNCINV",
+  "MEMERROR", "BADNUMBER", "NUMOVERFLOW", "NUMUNDERFLOW", "UNITEND",
+  "LASTUNSET", "ADD", "MINUS", "UNARY", "'('", "')'", "$accept", "input",
+  "unitexpr", "divlist", "expr", "numexpr", "pexpr", "list", YY_NULLPTR
+};
+
+static const char *
+yysymbol_name (yysymbol_kind_t yysymbol)
+{
+  return yytname[yysymbol];
+}
+#endif
+
+#define YYPACT_NINF (-19)
+
+#define yypact_value_is_default(Yyn) \
+  ((Yyn) == YYPACT_NINF)
+
+#define YYTABLE_NINF (-1)
+
+#define yytable_value_is_error(Yyn) \
+  0
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+static const yytype_int16 yypact[] =
+{
+      50,   -19,   -19,   -19,   -18,   -18,   -18,   175,   -18,   -18,
+     175,   -19,     4,   -19,   -19,   -19,   -19,   -19,   -19,   175,
+      75,    15,     8,    14,    12,    22,   -19,   100,   -19,   -19,
+     -19,   100,   -19,   -19,   100,   -19,   -18,   100,     7,   -19,
+     -19,   -19,    75,    75,    75,    75,    75,    35,   125,   175,
+     150,   -19,   -19,   -19,    30,    30,     3,     3,   -19,   175,
+     175,    32,   150,    32,    32
+};
+
+/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE does not specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_int8 yydefact[] =
+{
+       0,     4,    17,    21,     0,     0,     0,     0,     0,     0,
+       0,     2,     0,    37,    34,    35,    36,    38,    39,     0,
+       0,     0,     0,     6,     5,    20,    25,     9,    28,    29,
+      30,     7,    26,    27,    10,    40,     0,    11,     0,     1,
+       3,     8,     0,     0,     0,     0,     0,     0,     0,     0,
+      24,    31,    19,    15,    16,    14,    12,    13,    18,     0,
+       0,    22,    23,    32,    33
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -19,   -19,   -19,    41,   -16,     5,    -3,     0
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+       0,    21,    22,    41,    24,    25,    26,    50
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule whose
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_int8 yytable[] =
+{
+      27,    28,    29,    30,    38,    32,    33,    31,    35,    20,
+      34,    36,    42,    43,    44,    39,    42,    43,    44,    37,
+      27,    42,    43,    44,    40,     7,    53,    54,    55,    56,
+      57,    45,    46,    51,    47,    52,    45,    46,     2,    42,
+      48,    23,    27,    27,    27,    27,    27,     0,    61,    62,
+       0,     1,    58,     2,     3,     4,     5,     6,     0,    63,
+      64,     7,     0,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,     0,    19,     0,    20,     2,     3,
+       4,     5,     6,     0,     0,     0,     0,     0,     8,     9,
+      10,     0,    12,    13,    14,    15,    16,    17,    18,     0,
+      19,     0,    20,     2,     3,     4,     5,     6,    48,     0,
+       0,     0,     0,     8,     9,    49,     0,    12,    13,    14,
+      15,    16,    17,    18,     0,     0,     0,    20,     2,     3,
+       4,     5,     6,     0,     0,     0,     0,     0,     8,     9,
+      59,     0,    12,    13,    14,    15,    16,    17,    18,     0,
+      60,     0,    20,     2,     3,     4,     5,     6,    48,     0,
+       0,     0,     0,     8,     9,     0,     0,    12,    13,    14,
+      15,    16,    17,    18,     0,     0,     0,    20,     2,     3,
+       4,     5,     6,     0,     0,     0,     0,     0,     8,     9,
+       0,     0,    12,    13,    14,    15,    16,    17,    18,     0,
+       0,     0,    20
+};
+
+static const yytype_int8 yycheck[] =
+{
+       0,     4,     5,     6,    20,     8,     9,     7,     4,    27,
+      10,     7,     9,    10,    11,     0,     9,    10,    11,    19,
+      20,     9,    10,    11,    16,    11,    42,    43,    44,    45,
+      46,    24,    25,    36,    12,    28,    24,    25,     3,     9,
+       8,     0,    42,    43,    44,    45,    46,    -1,    48,    49,
+      -1,     1,    47,     3,     4,     5,     6,     7,    -1,    59,
+      60,    11,    -1,    13,    14,    15,    16,    17,    18,    19,
+      20,    21,    22,    23,    -1,    25,    -1,    27,     3,     4,
+       5,     6,     7,    -1,    -1,    -1,    -1,    -1,    13,    14,
+      15,    -1,    17,    18,    19,    20,    21,    22,    23,    -1,
+      25,    -1,    27,     3,     4,     5,     6,     7,     8,    -1,
+      -1,    -1,    -1,    13,    14,    15,    -1,    17,    18,    19,
+      20,    21,    22,    23,    -1,    -1,    -1,    27,     3,     4,
+       5,     6,     7,    -1,    -1,    -1,    -1,    -1,    13,    14,
+      15,    -1,    17,    18,    19,    20,    21,    22,    23,    -1,
+      25,    -1,    27,     3,     4,     5,     6,     7,     8,    -1,
+      -1,    -1,    -1,    13,    14,    -1,    -1,    17,    18,    19,
+      20,    21,    22,    23,    -1,    -1,    -1,    27,     3,     4,
+       5,     6,     7,    -1,    -1,    -1,    -1,    -1,    13,    14,
+      -1,    -1,    17,    18,    19,    20,    21,    22,    23,    -1,
+      -1,    -1,    27
+};
+
+/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+   state STATE-NUM.  */
+static const yytype_int8 yystos[] =
+{
+       0,     1,     3,     4,     5,     6,     7,    11,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    25,
+      27,    30,    31,    32,    33,    34,    35,    36,    35,    35,
+      35,    36,    35,    35,    36,     4,     7,    36,    33,     0,
+      16,    32,     9,    10,    11,    24,    25,    12,     8,    15,
+      36,    35,    28,    33,    33,    33,    33,    33,    34,    15,
+      25,    36,    36,    36,    36
+};
+
+/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
+static const yytype_int8 yyr1[] =
+{
+       0,    29,    30,    30,    30,    31,    31,    32,    32,    33,
+      33,    33,    33,    33,    33,    33,    33,    34,    34,    35,
+      36,    36,    36,    36,    36,    36,    36,    36,    36,    36,
+      36,    36,    36,    36,    36,    36,    36,    36,    36,    36,
+      36
+};
+
+/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
+static const yytype_int8 yyr2[] =
+{
+       0,     2,     1,     2,     1,     1,     1,     2,     2,     1,
+       2,     2,     3,     3,     3,     3,     3,     1,     3,     3,
+       1,     1,     3,     3,     2,     1,     2,     2,     2,     2,
+       2,     3,     4,     4,     1,     1,     1,     1,     1,     1,
+       2
+};
+
+
+enum { YYENOMEM = -2 };
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = UNITSEMPTY)
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+#define YYNOMEM         goto yyexhaustedlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == UNITSEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (comm, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
+
+/* Backward compatibility with an undocumented macro.
+   Use UNITSerror or UNITSUNDEF. */
+#define YYERRCODE UNITSUNDEF
+
+
+/* Enable debugging if requested.  */
+#if UNITSDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+
+
+
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Kind, Value, comm); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyo,
+                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct commtype *comm)
+{
+  FILE *yyoutput = yyo;
+  YY_USE (yyoutput);
+  YY_USE (comm);
+  if (!yyvaluep)
+    return;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YY_USE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
+
+static void
+yy_symbol_print (FILE *yyo,
+                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct commtype *comm)
+{
+  YYFPRINTF (yyo, "%s %s (",
+             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
+
+  yy_symbol_value_print (yyo, yykind, yyvaluep, comm);
+  YYFPRINTF (yyo, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
+                 int yyrule, struct commtype *comm)
+{
+  int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
+                       &yyvsp[(yyi + 1) - (yynrhs)], comm);
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule, comm); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !UNITSDEBUG */
+# define YYDPRINTF(Args) ((void) 0)
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !UNITSDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg,
+            yysymbol_kind_t yykind, YYSTYPE *yyvaluep, struct commtype *comm)
+{
+  YY_USE (yyvaluep);
+  YY_USE (comm);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  switch (yykind)
+    {
+    case YYSYMBOL_UNIT: /* UNIT  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1120 "parse.tab.c"
+        break;
+
+    case YYSYMBOL_unitexpr: /* unitexpr  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1126 "parse.tab.c"
+        break;
+
+    case YYSYMBOL_divlist: /* divlist  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1132 "parse.tab.c"
+        break;
+
+    case YYSYMBOL_expr: /* expr  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1138 "parse.tab.c"
+        break;
+
+    case YYSYMBOL_pexpr: /* pexpr  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1144 "parse.tab.c"
+        break;
+
+    case YYSYMBOL_list: /* list  */
+#line 201 "parse.y"
+            { destroyunit(((*yyvaluep).unit));}
+#line 1150 "parse.tab.c"
+        break;
+
+      default:
+        break;
+    }
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (struct commtype *comm)
+{
+/* Lookahead token kind.  */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol.  */
+/* Default value used for initialization, for pacifying older GCCs
+   or non-GCC compilers.  */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+    /* Number of syntax errors so far.  */
+    int yynerrs = 0;
+
+    yy_state_fast_t yystate = 0;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus = 0;
+
+    /* Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* Their size.  */
+    YYPTRDIFF_T yystacksize = YYINITDEPTH;
+
+    /* The state stack: array, bottom, top.  */
+    yy_state_t yyssa[YYINITDEPTH];
+    yy_state_t *yyss = yyssa;
+    yy_state_t *yyssp = yyss;
+
+    /* The semantic value stack: array, bottom, top.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs = yyvsa;
+    YYSTYPE *yyvsp = yyvs;
+
+  int yyn;
+  /* The return value of yyparse.  */
+  int yyresult;
+  /* Lookahead symbol kind.  */
+  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yychar = UNITSEMPTY; /* Cause a token to be read.  */
+
+  goto yysetstate;
+
+
+/*------------------------------------------------------------.
+| yynewstate -- push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+
+/*--------------------------------------------------------------------.
+| yysetstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+  YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
+  YY_IGNORE_USELESS_CAST_BEGIN
+  *yyssp = YY_CAST (yy_state_t, yystate);
+  YY_IGNORE_USELESS_CAST_END
+  YY_STACK_PRINT (yyss, yyssp);
+
+  if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    YYNOMEM;
+#else
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYPTRDIFF_T yysize = yyssp - yyss + 1;
+
+# if defined yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        yy_state_t *yyss1 = yyss;
+        YYSTYPE *yyvs1 = yyvs;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * YYSIZEOF (*yyssp),
+                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
+                    &yystacksize);
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+# else /* defined YYSTACK_RELOCATE */
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        YYNOMEM;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yy_state_t *yyss1 = yyss;
+        union yyalloc *yyptr =
+          YY_CAST (union yyalloc *,
+                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
+        if (! yyptr)
+          YYNOMEM;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YY_IGNORE_USELESS_CAST_BEGIN
+      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+                  YY_CAST (long, yystacksize)));
+      YY_IGNORE_USELESS_CAST_END
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
+
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
+  if (yychar == UNITSEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token\n"));
+      yychar = yylex (&yylval, comm);
+    }
+
+  if (yychar <= UNITSEOF)
+    {
+      yychar = UNITSEOF;
+      yytoken = YYSYMBOL_YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else if (yychar == UNITSerror)
+    {
+      /* The scanner already issued an error message, process directly
+         to error recovery.  But do not keep the error token as
+         lookahead, it is too special and may lead us to an endless
+         loop in error recovery. */
+      yychar = UNITSUNDEF;
+      yytoken = YYSYMBOL_YYerror;
+      goto yyerrlab1;
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  /* Discard the shifted token.  */
+  yychar = UNITSEMPTY;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+  case 2: /* input: EOL  */
+#line 213 "parse.y"
+                      { comm->result = makenumunit(1,&err); CHECK(0);
+                       comm->errorcode = 0; YYACCEPT; }
+#line 1427 "parse.tab.c"
+    break;
+
+  case 3: /* input: unitexpr EOL  */
+#line 215 "parse.y"
+                     { comm->result = (yyvsp[-1].unit); comm->errorcode = 0; YYACCEPT; }
+#line 1433 "parse.tab.c"
+    break;
+
+  case 4: /* input: error  */
+#line 216 "parse.y"
+                     { YYABORT; }
+#line 1439 "parse.tab.c"
+    break;
+
+  case 5: /* unitexpr: expr  */
+#line 219 "parse.y"
+                                    { (yyval.unit) = (yyvsp[0].unit);}
+#line 1445 "parse.tab.c"
+    break;
+
+  case 6: /* unitexpr: divlist  */
+#line 220 "parse.y"
+                                    { (yyval.unit) = (yyvsp[0].unit);}
+#line 1451 "parse.tab.c"
+    break;
+
+  case 7: /* divlist: DIVIDE list  */
+#line 223 "parse.y"
+                                    { invertunit((yyvsp[0].unit)); (yyval.unit)=(yyvsp[0].unit);}
+#line 1457 "parse.tab.c"
+    break;
+
+  case 8: /* divlist: divlist divlist  */
+#line 224 "parse.y"
+                                         {err = multunit((yyvsp[-1].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                          CHECK((yyvsp[-1].unit));(yyval.unit)=(yyvsp[-1].unit);}
+#line 1464 "parse.tab.c"
+    break;
+
+  case 9: /* expr: list  */
+#line 228 "parse.y"
+                                    { (yyval.unit) = (yyvsp[0].unit); }
+#line 1470 "parse.tab.c"
+    break;
+
+  case 10: /* expr: MULTMINUS list  */
+#line 229 "parse.y"
+                                    { (yyval.unit) = (yyvsp[0].unit); (yyval.unit)->factor *= -1; }
+#line 1476 "parse.tab.c"
+    break;
+
+  case 11: /* expr: MINUS list  */
+#line 230 "parse.y"
+                                    { (yyval.unit) = (yyvsp[0].unit); (yyval.unit)->factor *= -1; }
+#line 1482 "parse.tab.c"
+    break;
+
+  case 12: /* expr: expr ADD expr  */
+#line 231 "parse.y"
+                                    { err = addunit((yyvsp[-2].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                      CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1489 "parse.tab.c"
+    break;
+
+  case 13: /* expr: expr MINUS expr  */
+#line 233 "parse.y"
+                                    { (yyvsp[0].unit)->factor *= -1;
+                                      err = addunit((yyvsp[-2].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                      CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1497 "parse.tab.c"
+    break;
+
+  case 14: /* expr: expr DIVIDE expr  */
+#line 236 "parse.y"
+                                    { err = divunit((yyvsp[-2].unit), (yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                      CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1504 "parse.tab.c"
+    break;
+
+  case 15: /* expr: expr MULTIPLY expr  */
+#line 238 "parse.y"
+                                    { err = multunit((yyvsp[-2].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                      CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1511 "parse.tab.c"
+    break;
+
+  case 16: /* expr: expr MULTSTAR expr  */
+#line 240 "parse.y"
+                                    { err = multunit((yyvsp[-2].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                      CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1518 "parse.tab.c"
+    break;
+
+  case 17: /* numexpr: REAL  */
+#line 244 "parse.y"
+                                    { (yyval.number) = (yyvsp[0].number);         }
+#line 1524 "parse.tab.c"
+    break;
+
+  case 18: /* numexpr: numexpr NUMDIV numexpr  */
+#line 245 "parse.y"
+                                    { (yyval.number) = (yyvsp[-2].number) / (yyvsp[0].number);    }
+#line 1530 "parse.tab.c"
+    break;
+
+  case 19: /* pexpr: '(' expr ')'  */
+#line 248 "parse.y"
+                                    { (yyval.unit) = (yyvsp[-1].unit);  }
+#line 1536 "parse.tab.c"
+    break;
+
+  case 20: /* list: numexpr  */
+#line 254 "parse.y"
+                                   { (yyval.unit) = makenumunit((yyvsp[0].number),&err); CHECK(0);}
+#line 1542 "parse.tab.c"
+    break;
+
+  case 21: /* list: UNIT  */
+#line 255 "parse.y"
+                                   { (yyval.unit) = (yyvsp[0].unit); }
+#line 1548 "parse.tab.c"
+    break;
+
+  case 22: /* list: list EXPONENT list  */
+#line 256 "parse.y"
+                                   { err = unitpower((yyvsp[-2].unit),(yyvsp[0].unit));destroyunit((yyvsp[0].unit));
+                                     CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1555 "parse.tab.c"
+    break;
+
+  case 23: /* list: list MULTMINUS list  */
+#line 258 "parse.y"
+                                   { err = multunit((yyvsp[-2].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                     CHECK((yyvsp[-2].unit));(yyval.unit)=(yyvsp[-2].unit);}
+#line 1562 "parse.tab.c"
+    break;
+
+  case 24: /* list: list list  */
+#line 260 "parse.y"
+                                   { err = multunit((yyvsp[-1].unit),(yyvsp[0].unit)); destroyunit((yyvsp[0].unit));
+                                     CHECK((yyvsp[-1].unit));(yyval.unit)=(yyvsp[-1].unit);}
+#line 1569 "parse.tab.c"
+    break;
+
+  case 25: /* list: pexpr  */
+#line 262 "parse.y"
+                                   { (yyval.unit)=(yyvsp[0].unit); }
+#line 1575 "parse.tab.c"
+    break;
+
+  case 26: /* list: SQRT pexpr  */
+#line 263 "parse.y"
+                                   { err = rootunit((yyvsp[0].unit),2); CHECK((yyvsp[0].unit)); (yyval.unit)=(yyvsp[0].unit);}
+#line 1581 "parse.tab.c"
+    break;
+
+  case 27: /* list: CUBEROOT pexpr  */
+#line 264 "parse.y"
+                                   { err = rootunit((yyvsp[0].unit),3); CHECK((yyvsp[0].unit)); (yyval.unit)=(yyvsp[0].unit);}
+#line 1587 "parse.tab.c"
+    break;
+
+  case 28: /* list: REALFUNC pexpr  */
+#line 265 "parse.y"
+                                   { err = funcunit((yyvsp[0].unit),(yyvsp[-1].realfunc));CHECK((yyvsp[0].unit)); (yyval.unit)=(yyvsp[0].unit);}
+#line 1593 "parse.tab.c"
+    break;
+
+  case 29: /* list: LOG pexpr  */
+#line 266 "parse.y"
+                                   { err = logunit((yyvsp[0].unit),(yyvsp[-1].integer)); CHECK((yyvsp[0].unit)); (yyval.unit)=(yyvsp[0].unit);}
+#line 1599 "parse.tab.c"
+    break;
+
+  case 30: /* list: UNITFUNC pexpr  */
+#line 267 "parse.y"
+                                   { err = evalfunc((yyvsp[0].unit),(yyvsp[-1].unitfunc),0,0); CHECK((yyvsp[0].unit));(yyval.unit)=(yyvsp[0].unit);}
+#line 1605 "parse.tab.c"
+    break;
+
+  case 31: /* list: FUNCINV UNITFUNC pexpr  */
+#line 268 "parse.y"
+                                   { err = evalfunc((yyvsp[0].unit),(yyvsp[-1].unitfunc),1,0); CHECK((yyvsp[0].unit));(yyval.unit)=(yyvsp[0].unit);}
+#line 1611 "parse.tab.c"
+    break;
+
+  case 32: /* list: list EXPONENT MULTMINUS list  */
+#line 270 "parse.y"
+                                   { (yyvsp[0].unit)->factor *= -1; err = unitpower((yyvsp[-3].unit),(yyvsp[0].unit));
+                                     destroyunit((yyvsp[0].unit));CHECK((yyvsp[-3].unit));(yyval.unit)=(yyvsp[-3].unit);}
+#line 1618 "parse.tab.c"
+    break;
+
+  case 33: /* list: list EXPONENT MINUS list  */
+#line 273 "parse.y"
+                                   { (yyvsp[0].unit)->factor *= -1; err = unitpower((yyvsp[-3].unit),(yyvsp[0].unit));
+                                     destroyunit((yyvsp[0].unit));CHECK((yyvsp[-3].unit));(yyval.unit)=(yyvsp[-3].unit);}
+#line 1625 "parse.tab.c"
+    break;
+
+  case 34: /* list: BADNUMBER  */
+#line 275 "parse.y"
+                                   { err = E_BADNUM;   CHECK(0); }
+#line 1631 "parse.tab.c"
+    break;
+
+  case 35: /* list: NUMOVERFLOW  */
+#line 276 "parse.y"
+                                   { err = E_OVERFLOW; CHECK(0); }
+#line 1637 "parse.tab.c"
+    break;
+
+  case 36: /* list: NUMUNDERFLOW  */
+#line 277 "parse.y"
+                                   { err = E_UNDERFLOW;CHECK(0); }
+#line 1643 "parse.tab.c"
+    break;
+
+  case 37: /* list: MEMERROR  */
+#line 278 "parse.y"
+                                   { err = E_PARSEMEM; CHECK(0); }
+#line 1649 "parse.tab.c"
+    break;
+
+  case 38: /* list: UNITEND  */
+#line 279 "parse.y"
+                                   { err = E_UNITEND;  CHECK(0); }
+#line 1655 "parse.tab.c"
+    break;
+
+  case 39: /* list: LASTUNSET  */
+#line 280 "parse.y"
+                                   { err = E_LASTUNSET;CHECK(0); }
+#line 1661 "parse.tab.c"
+    break;
+
+  case 40: /* list: FUNCINV UNIT  */
+#line 281 "parse.y"
+                                   { err = E_NOTAFUNC; CHECK((yyvsp[0].unit));}
+#line 1667 "parse.tab.c"
+    break;
+
+
+#line 1671 "parse.tab.c"
+
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == UNITSEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+      yyerror (comm, YY_("syntax error"));
+    }
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= UNITSEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == UNITSEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval, comm);
+          yychar = UNITSEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
+  ++yynerrs;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  /* Pop stack until we find a state that shifts the error token.  */
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYSYMBOL_YYerror;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  YY_ACCESSING_SYMBOL (yystate), yyvsp, comm);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturnlab;
+
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturnlab;
+
+
+/*-----------------------------------------------------------.
+| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here.  |
+`-----------------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (comm, YY_("memory exhausted"));
+  yyresult = 2;
+  goto yyreturnlab;
+
+
+/*----------------------------------------------------------.
+| yyreturnlab -- parsing is finished, clean up and return.  |
+`----------------------------------------------------------*/
+yyreturnlab:
+  if (yychar != UNITSEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval, comm);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, comm);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+
+  return yyresult;
+}
+
+#line 284 "parse.y"
+
+
+double
+factorial(double x)
+{
+  return tgamma(x+1);
+}  
+
+struct function 
+  realfunctions[] = { {"sin", sin,    ANGLEIN},
+                      {"cos", cos,    ANGLEIN},
+                      {"tan", tan,    ANGLEIN},
+                      {"ln", log,     DIMENSIONLESS},
+                      {"log", log10,  DIMENSIONLESS},
+                      {"exp", exp,    DIMENSIONLESS},
+                      {"acos", acos,  ANGLEOUT},
+                      {"atan", atan,  ANGLEOUT},
+                      {"asin", asin,  ANGLEOUT},
+		      {"sinh", sinh, DIMENSIONLESS},
+		      {"cosh", cosh, DIMENSIONLESS},		      
+		      {"tanh", tanh, DIMENSIONLESS},
+		      {"asinh", asinh, DIMENSIONLESS},
+		      {"acosh", acosh, DIMENSIONLESS},		      
+		      {"atanh", atanh, DIMENSIONLESS},
+                      {"round", round, DIMENSIONLESS},
+                      {"floor", floor, DIMENSIONLESS},
+                      {"ceil", ceil, DIMENSIONLESS},
+                      {"erf", erf, DIMENSIONLESS},
+                      {"erfc", erfc, DIMENSIONLESS},
+                      {"Gamma", tgamma, DIMENSIONLESS},
+                      {"lnGamma", lgamma, DIMENSIONLESS},
+                      {"factorial", factorial, NATURAL},
+                      {0, 0, 0}};
+
+struct {
+  char op;
+  int value;
+} optable[] = { {'*', MULTIPLY},
+                {'/', DIVIDE},
+                {'|', NUMDIV},
+                {'+', ADD},
+                {'(', '('},
+                {')', ')'},
+                {'^', EXPONENT},
+                {'~', FUNCINV},
+                {0, 0}};
+
+struct {
+  char *name;
+  int value;
+} strtable[] = { {"sqrt", SQRT},
+                 {"cuberoot", CUBEROOT},
+                 {"per" , DIVIDE},
+                 {0, 0}};
+
+#define LASTUNIT '_'     /* Last unit symbol */
+
+
+int yylex(YYSTYPE *lvalp, struct commtype *comm)
+{
+  int length, count;
+  struct unittype *output;
+  const char *inptr;
+  char *name;
+
+  char *nonunitchars = "~;+-*/|\t\n^ ()"; /* Chars not allowed in unit name --- also defined in units.c */
+  char *nonunitstart = ".,";              /* Can't start a unit */
+  char *nonunitend = ".,_";              /* Can't end a unit */
+  char *number_start = ".,0123456789";    /* Can be first char of a number */
+  
+  if (comm->location==-1) return 0;
+  inptr = comm->data + comm->location;   /* Point to start of data */
+
+  /* Skip spaces */
+  while(*inptr==' ') inptr++, comm->location++;
+
+  if (*inptr==0) {
+    comm->location = -1;
+    return EOL;  /* Return failure if string has ended */
+  }  
+
+  /* Check for **, an exponent operator.  */
+
+  if (0==strncmp("**",inptr,2)){
+    comm->location += 2;
+    return EXPONENT;
+  }
+
+  /* Check for '-' and '*' which get special handling */
+
+  if (*inptr=='-'){
+    comm->location++;
+    if (parserflags.minusminus)
+      return MINUS;
+    return MULTMINUS;
+  }      
+
+  if (*inptr=='*'){
+    comm->location++;
+    if (parserflags.oldstar)
+      return MULTIPLY;
+    return MULTSTAR;
+  }      
+
+  /* Look for single character ops */
+
+  for(count=0; optable[count].op; count++){
+    if (*inptr==optable[count].op) {
+       comm->location++;
+       return optable[count].value;
+    }
+  }
+
+  /* Look for numbers */
+
+  if (strchr(number_start,*inptr)){  /* prevent "nan" from being recognized */
+    char *endloc;
+    errno=0;
+    lvalp->number = strtod(inptr, &endloc);
+    if (inptr != endloc) { 
+      comm->location += (endloc-inptr);
+      if (*endloc && strchr(number_start,*endloc))
+        return BADNUMBER;
+      else if (errno){
+        errno=0;
+        if (fabs(lvalp->number)==HUGE_VAL) return NUMOVERFLOW;
+        else return NUMUNDERFLOW;
+      }
+      else
+        return REAL;
+    }
+  }
+
+  /* Look for a word (function name or unit name) */
+
+  length = strcspn(inptr,nonunitchars);   
+
+  if (!length){  /* Next char is not a valid unit char */
+     comm->location++;
+     return 0;
+  }
+
+  /* Check for the "last unit" symbol, with possible exponent */ 
+
+  if (*inptr == LASTUNIT &&
+      (length==1 || length==2 && strchr("23456789",inptr[1]))){
+    comm->location++;
+    if (!lastunitset) 
+      return LASTUNSET;
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, &lastunit);
+    if (length==2){
+      expunit(output, inptr[1]-'0');
+      comm->location++;
+    }
+    lvalp->unit = output;
+    return UNIT;
+  } 
+
+  /* Check that unit name doesn't start or end with forbidden chars */
+  if (strchr(nonunitstart,*inptr)){
+    comm->location++;
+    return 0;
+  }
+  if (strchr(nonunitend, inptr[length-1])){
+    comm->location+=length;
+    return 0;
+  }
+
+  name = dupnstr(inptr, length);
+
+  /* Look for string operators */
+
+  for(count=0;strtable[count].name;count++){
+    if (!strcmp(name,strtable[count].name)){
+      free(name);
+      comm->location += length;
+      return strtable[count].value;
+    }
+  }
+  
+  /* Look for real function names */
+
+  for(count=0;realfunctions[count].name;count++){
+    if (!strcmp(name,realfunctions[count].name)){
+      lvalp->realfunc = realfunctions+count;
+      comm->location += length;
+      free(name);
+      return REALFUNC;
+    }
+  }
+
+  /* Check for arbitrary base log */
+  
+  if (!strncmp(name, "log",3)){
+    count = strspn(name+3,"1234567890");
+    if (count+3 == strlen(name)){
+      lvalp->integer=atoi(name+3);
+      if (lvalp->integer>1){      /* Log base must be larger than 1 */
+	comm->location += length;
+	free(name);
+	return LOG;
+      }
+    }
+  }
+      
+  /* Look for function parameter */
+
+  if (function_parameter && !strcmp(name,function_parameter)){
+    free(name);
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, parameter_value);
+    lvalp->unit = output;
+    comm->location += length;
+    return UNIT;
+  } 
+
+  /* Look for user defined function */
+
+  lvalp->unitfunc = fnlookup(name);
+  if (lvalp->unitfunc){
+    comm->location += length;
+    free(name);
+    return UNITFUNC;
+  }
+
+  /* Didn't find a special string, so treat it as unit name */
+
+  comm->location+=length;
+  if (strchr("23456789",inptr[length-1]) && !hassubscript(name)) {
+    /* ends with digit but not a subscript, so do exponent handling like m3 */
+    count = name[length-1] - '0';
+    length--;
+    if (strchr(number_start, name[length-1])){
+      free(name);
+      return UNITEND;
+    }
+  } else count=1;
+
+  free(name);
+    
+  output = getnewunit();
+  if (!output)
+    return MEMERROR;
+  output->numerator[count--]=0;
+  for(;count>=0;count--)
+    output->numerator[count] = dupnstr(inptr, length);
+  lvalp->unit=output;
+  return UNIT;
+}
+
+
+void yyerror(struct commtype *comm, char *s){}
+
+
+int
+parseunit(struct unittype *output, char const *input,char **errstr,int *errloc)
+{
+  struct commtype comm;
+  int saveunitcount;
+
+  saveunitcount = unitcount;
+  initializeunit(output);
+  comm.result = 0;
+  comm.location = 0;
+  comm.data = input;
+  comm.errorcode = E_PARSE;    /* Assume parse error */
+  errno=0;
+  /* errno should only be set in the case of invalid function arguments */
+  if (yyparse(&comm) || errno){
+    if (comm.location==-1) 
+      comm.location = strlen(input);
+    if (errstr){
+      if (comm.errorcode==E_FUNC || errno)
+        *errstr = strerror(errno);
+      else
+        *errstr=errormsg[comm.errorcode];
+    }
+    if (errloc)
+      *errloc = comm.location;
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory with error: %d in %d out\n",
+             saveunitcount, unitcount);
+    return comm.errorcode;
+  } else {
+    if (errstr)
+      *errstr = 0;
+    multunit(output,comm.result);
+    destroyunit(comm.result);
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory without error: %d in %d out\n",
+	      saveunitcount, unitcount);
+    return 0;
+  }
+}
+
+
diff --git a/parse.y b/parse.y
new file mode 100644
index 0000000..10e401d
--- /dev/null
+++ b/parse.y
@@ -0,0 +1,584 @@
+/*
+ *  parse.y: the parser for GNU units, a program for units conversion
+ *  Copyright (C) 1999-2002, 2007, 2009, 2014, 2017-2018, 2020, 2024 
+ *  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 3 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *     
+ *  This program was written by Adrian Mariano (adrianm@gnu.org)
+ */
+
+
+%{
+#include<stdio.h>
+#include<float.h>
+#include "units.h"
+
+struct commtype {
+   int location;
+   const char *data;
+   struct unittype *result;
+   int errorcode;
+};
+
+static int err;  /* value used by parser to store return values */
+
+/* 
+   The CHECK macro aborts parse if an error has occurred.  It optionally
+   destroys a variable.  Call with CHECK(0) if no variables need destruction 
+   on error. 
+*/
+ 
+#define CHECK(var) if (err) { comm->errorcode=err; \
+                              if (var) destroyunit(var); \
+                              YYABORT; }
+ 
+int yylex();
+void yyerror(struct commtype *comm, char *);
+
+#define MAXMEM 100
+int unitcount=0;    /* Counts the number of units allocated by the parser */
+
+struct function { 
+   char *name; 
+   double (*func)(double); 
+   int type;
+}; 
+
+#define DIMENSIONLESS 0
+#define ANGLEIN 1
+#define ANGLEOUT 2
+#define NATURAL 3
+
+struct unittype *
+getnewunit()
+{
+  struct unittype *unit;
+
+  if (unitcount>=MAXMEM)
+    return 0;
+  unit = (struct unittype *) 
+    mymalloc(sizeof(struct unittype),"(getnewunit)");
+  if (!unit)
+    return 0;
+  initializeunit(unit);
+  unitcount++;
+  return unit;
+}
+
+
+void
+destroyunit(struct unittype *unit)
+{
+  freeunit(unit);
+  free(unit);
+  unitcount--;
+}  
+ 
+
+struct unittype *
+makenumunit(double num,int *myerr)
+{
+  struct unittype *ret;
+  ret=getnewunit();
+  if (!ret){
+    *myerr = E_PARSEMEM;
+    return 0;  
+  }
+  ret->factor = num;
+  *myerr = 0;
+  return ret;
+}
+
+int
+logunit(struct unittype *theunit, int base)
+{  
+  if ((err=unit2num(theunit)))
+    return err;
+  if (base==2)
+    theunit->factor = log2(theunit->factor);
+  else if (base==10)
+    theunit->factor = log10(theunit->factor);
+  else
+    theunit->factor = log(theunit->factor)/log((double)base);
+  if (errno)
+    return E_FUNC;
+  return 0;
+}
+ 
+int
+funcunit(struct unittype *theunit, struct function const *fun)
+{
+  struct unittype angleunit;
+  if (fun->type==ANGLEIN){
+    err=unit2num(theunit);
+    if (err==E_NOTANUMBER){
+      initializeunit(&angleunit);
+      angleunit.denominator[0] = dupstr("radian");
+      angleunit.denominator[1] = 0;
+      err = multunit(theunit, &angleunit);
+      freeunit(&angleunit);
+      if (!err)
+        err = unit2num(theunit);
+    }
+    if (err)
+      return err;
+  } else if (fun->type==ANGLEOUT || fun->type == DIMENSIONLESS || fun->type == NATURAL) {
+    if ((err=unit2num(theunit)))
+      return err;
+    if (fun->type==NATURAL && (theunit->factor<0 || trunc(theunit->factor)!=theunit->factor))
+      return E_NOTINDOMAIN;
+  } else 
+     return E_BADFUNCTYPE;
+  errno = 0;
+  theunit->factor = (*(fun->func))(theunit->factor);
+  if (errno)
+    return E_FUNC;
+  if (fun->type==ANGLEOUT) {
+    theunit->numerator[0] = dupstr("radian");
+    theunit->numerator[1] = 0;
+  }
+  return 0;
+}
+
+
+%}
+
+%parse-param {struct commtype *comm}
+%lex-param {struct commtype *comm}
+%define api.pure full
+%define api.prefix {units}
+
+%union {
+  double number;
+  int integer;
+  struct unittype *unit;
+  struct function *realfunc;
+  struct func *unitfunc;
+}
+
+%token <number> REAL
+%token <unit> UNIT
+%token <realfunc> REALFUNC
+%token <integer> LOG
+%token <unitfunc> UNITFUNC
+%token <integer> EXPONENT
+%token <integer> MULTIPLY
+%token <integer> MULTSTAR
+%token <integer> DIVIDE
+%token <integer> NUMDIV
+%token <integer> SQRT
+%token <integer> CUBEROOT
+%token <integer> MULTMINUS
+%token <integer> EOL
+%token <integer> FUNCINV
+%token <integer> MEMERROR
+%token <integer> BADNUMBER
+%token <integer> NUMOVERFLOW
+%token <integer> NUMUNDERFLOW
+%token <integer> UNITEND
+%token <integer> LASTUNSET
+
+%type <number> numexpr
+%type <unit> expr
+%type <unit> list
+%type <unit> pexpr
+%type <unit> unitexpr
+%type <unit> divlist
+
+%destructor { destroyunit($$);} <unit>
+
+%left ADD MINUS
+%left UNARY
+%left DIVIDE MULTSTAR
+%left MULTIPLY MULTMINUS
+%nonassoc '(' SQRT CUBEROOT REALFUNC LOG UNIT REAL UNITFUNC FUNCINV MEMERROR BADNUMBER NUMOVERFLOW NUMUNDERFLOW UNITEND LASTUNSET
+%right EXPONENT
+%left NUMDIV
+
+
+%%
+ input: EOL           { comm->result = makenumunit(1,&err); CHECK(0);
+                       comm->errorcode = 0; YYACCEPT; }
+      | unitexpr EOL { comm->result = $1; comm->errorcode = 0; YYACCEPT; }
+      | error        { YYABORT; }
+      ;
+
+ unitexpr:  expr                    { $$ = $1;}
+         |  divlist                 { $$ = $1;}
+         ;
+
+ divlist: DIVIDE list               { invertunit($2); $$=$2;}
+        | divlist divlist %prec MULTIPLY {err = multunit($1,$2); destroyunit($2);
+                                          CHECK($1);$$=$1;}
+        ;
+
+ expr: list                         { $$ = $1; }
+     | MULTMINUS list %prec UNARY   { $$ = $2; $$->factor *= -1; }
+     | MINUS list %prec UNARY       { $$ = $2; $$->factor *= -1; }
+     | expr ADD expr                { err = addunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MINUS expr              { $3->factor *= -1;
+                                      err = addunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr DIVIDE expr             { err = divunit($1, $3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MULTIPLY expr           { err = multunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MULTSTAR expr           { err = multunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     ; 
+
+numexpr:  REAL                      { $$ = $1;         }
+         | numexpr NUMDIV numexpr   { $$ = $1 / $3;    }
+     ;
+
+ pexpr: '(' expr ')'                { $$ = $2;  }
+       ;
+
+ /* list is a list of units, possibly raised to powers, to be multiplied
+    together. */
+
+list:  numexpr                     { $$ = makenumunit($1,&err); CHECK(0);}
+      | UNIT                       { $$ = $1; }
+      | list EXPONENT list         { err = unitpower($1,$3);destroyunit($3);
+                                     CHECK($1);$$=$1;}
+      | list MULTMINUS list        { err = multunit($1,$3); destroyunit($3);
+                                     CHECK($1);$$=$1;}
+      | list list %prec MULTIPLY   { err = multunit($1,$2); destroyunit($2);
+                                     CHECK($1);$$=$1;}
+      | pexpr                      { $$=$1; }
+      | SQRT pexpr                 { err = rootunit($2,2); CHECK($2); $$=$2;}
+      | CUBEROOT pexpr             { err = rootunit($2,3); CHECK($2); $$=$2;}
+      | REALFUNC pexpr             { err = funcunit($2,$1);CHECK($2); $$=$2;}
+      | LOG pexpr                  { err = logunit($2,$1); CHECK($2); $$=$2;}
+      | UNITFUNC pexpr             { err = evalfunc($2,$1,0,0); CHECK($2);$$=$2;}
+      | FUNCINV UNITFUNC pexpr     { err = evalfunc($3,$2,1,0); CHECK($3);$$=$3;}
+      | list EXPONENT MULTMINUS list %prec EXPONENT  
+                                   { $4->factor *= -1; err = unitpower($1,$4);
+                                     destroyunit($4);CHECK($1);$$=$1;}
+      | list EXPONENT MINUS list %prec EXPONENT  
+                                   { $4->factor *= -1; err = unitpower($1,$4);
+                                     destroyunit($4);CHECK($1);$$=$1;}
+      | BADNUMBER                  { err = E_BADNUM;   CHECK(0); }
+      | NUMOVERFLOW                { err = E_OVERFLOW; CHECK(0); }
+      | NUMUNDERFLOW               { err = E_UNDERFLOW;CHECK(0); }
+      | MEMERROR                   { err = E_PARSEMEM; CHECK(0); }        
+      | UNITEND                    { err = E_UNITEND;  CHECK(0); }
+      | LASTUNSET                  { err = E_LASTUNSET;CHECK(0); }
+      | FUNCINV UNIT               { err = E_NOTAFUNC; CHECK($2);}
+   ;
+
+%%
+
+double
+factorial(double x)
+{
+  return tgamma(x+1);
+}  
+
+struct function 
+  realfunctions[] = { {"sin", sin,    ANGLEIN},
+                      {"cos", cos,    ANGLEIN},
+                      {"tan", tan,    ANGLEIN},
+                      {"ln", log,     DIMENSIONLESS},
+                      {"log", log10,  DIMENSIONLESS},
+                      {"exp", exp,    DIMENSIONLESS},
+                      {"acos", acos,  ANGLEOUT},
+                      {"atan", atan,  ANGLEOUT},
+                      {"asin", asin,  ANGLEOUT},
+		      {"sinh", sinh, DIMENSIONLESS},
+		      {"cosh", cosh, DIMENSIONLESS},		      
+		      {"tanh", tanh, DIMENSIONLESS},
+		      {"asinh", asinh, DIMENSIONLESS},
+		      {"acosh", acosh, DIMENSIONLESS},		      
+		      {"atanh", atanh, DIMENSIONLESS},
+                      {"round", round, DIMENSIONLESS},
+                      {"floor", floor, DIMENSIONLESS},
+                      {"ceil", ceil, DIMENSIONLESS},
+                      {"erf", erf, DIMENSIONLESS},
+                      {"erfc", erfc, DIMENSIONLESS},
+                      {"Gamma", tgamma, DIMENSIONLESS},
+                      {"lnGamma", lgamma, DIMENSIONLESS},
+                      {"factorial", factorial, NATURAL},
+                      {0, 0, 0}};
+
+struct {
+  char op;
+  int value;
+} optable[] = { {'*', MULTIPLY},
+                {'/', DIVIDE},
+                {'|', NUMDIV},
+                {'+', ADD},
+                {'(', '('},
+                {')', ')'},
+                {'^', EXPONENT},
+                {'~', FUNCINV},
+                {0, 0}};
+
+struct {
+  char *name;
+  int value;
+} strtable[] = { {"sqrt", SQRT},
+                 {"cuberoot", CUBEROOT},
+                 {"per" , DIVIDE},
+                 {0, 0}};
+
+#define LASTUNIT '_'     /* Last unit symbol */
+
+
+int yylex(YYSTYPE *lvalp, struct commtype *comm)
+{
+  int length, count;
+  struct unittype *output;
+  const char *inptr;
+  char *name;
+
+  char *nonunitchars = "~;+-*/|\t\n^ ()"; /* Chars not allowed in unit name --- also defined in units.c */
+  char *nonunitstart = ".,";              /* Can't start a unit */
+  char *nonunitend = ".,_";              /* Can't end a unit */
+  char *number_start = ".,0123456789";    /* Can be first char of a number */
+  
+  if (comm->location==-1) return 0;
+  inptr = comm->data + comm->location;   /* Point to start of data */
+
+  /* Skip spaces */
+  while(*inptr==' ') inptr++, comm->location++;
+
+  if (*inptr==0) {
+    comm->location = -1;
+    return EOL;  /* Return failure if string has ended */
+  }  
+
+  /* Check for **, an exponent operator.  */
+
+  if (0==strncmp("**",inptr,2)){
+    comm->location += 2;
+    return EXPONENT;
+  }
+
+  /* Check for '-' and '*' which get special handling */
+
+  if (*inptr=='-'){
+    comm->location++;
+    if (parserflags.minusminus)
+      return MINUS;
+    return MULTMINUS;
+  }      
+
+  if (*inptr=='*'){
+    comm->location++;
+    if (parserflags.oldstar)
+      return MULTIPLY;
+    return MULTSTAR;
+  }      
+
+  /* Look for single character ops */
+
+  for(count=0; optable[count].op; count++){
+    if (*inptr==optable[count].op) {
+       comm->location++;
+       return optable[count].value;
+    }
+  }
+
+  /* Look for numbers */
+
+  if (strchr(number_start,*inptr)){  /* prevent "nan" from being recognized */
+    char *endloc;
+    errno=0;
+    lvalp->number = strtod(inptr, &endloc);
+    if (inptr != endloc) { 
+      comm->location += (endloc-inptr);
+      if (*endloc && strchr(number_start,*endloc))
+        return BADNUMBER;
+      else if (errno){
+        errno=0;
+        if (fabs(lvalp->number)==HUGE_VAL) return NUMOVERFLOW;
+        else return NUMUNDERFLOW;
+      }
+      else
+        return REAL;
+    }
+  }
+
+  /* Look for a word (function name or unit name) */
+
+  length = strcspn(inptr,nonunitchars);   
+
+  if (!length){  /* Next char is not a valid unit char */
+     comm->location++;
+     return 0;
+  }
+
+  /* Check for the "last unit" symbol, with possible exponent */ 
+
+  if (*inptr == LASTUNIT &&
+      (length==1 || length==2 && strchr("23456789",inptr[1]))){
+    comm->location++;
+    if (!lastunitset) 
+      return LASTUNSET;
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, &lastunit);
+    if (length==2){
+      expunit(output, inptr[1]-'0');
+      comm->location++;
+    }
+    lvalp->unit = output;
+    return UNIT;
+  } 
+
+  /* Check that unit name doesn't start or end with forbidden chars */
+  if (strchr(nonunitstart,*inptr)){
+    comm->location++;
+    return 0;
+  }
+  if (strchr(nonunitend, inptr[length-1])){
+    comm->location+=length;
+    return 0;
+  }
+
+  name = dupnstr(inptr, length);
+
+  /* Look for string operators */
+
+  for(count=0;strtable[count].name;count++){
+    if (!strcmp(name,strtable[count].name)){
+      free(name);
+      comm->location += length;
+      return strtable[count].value;
+    }
+  }
+  
+  /* Look for real function names */
+
+  for(count=0;realfunctions[count].name;count++){
+    if (!strcmp(name,realfunctions[count].name)){
+      lvalp->realfunc = realfunctions+count;
+      comm->location += length;
+      free(name);
+      return REALFUNC;
+    }
+  }
+
+  /* Check for arbitrary base log */
+  
+  if (!strncmp(name, "log",3)){
+    count = strspn(name+3,"1234567890");
+    if (count+3 == strlen(name)){
+      lvalp->integer=atoi(name+3);
+      if (lvalp->integer>1){      /* Log base must be larger than 1 */
+	comm->location += length;
+	free(name);
+	return LOG;
+      }
+    }
+  }
+      
+  /* Look for function parameter */
+
+  if (function_parameter && !strcmp(name,function_parameter)){
+    free(name);
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, parameter_value);
+    lvalp->unit = output;
+    comm->location += length;
+    return UNIT;
+  } 
+
+  /* Look for user defined function */
+
+  lvalp->unitfunc = fnlookup(name);
+  if (lvalp->unitfunc){
+    comm->location += length;
+    free(name);
+    return UNITFUNC;
+  }
+
+  /* Didn't find a special string, so treat it as unit name */
+
+  comm->location+=length;
+  if (strchr("23456789",inptr[length-1]) && !hassubscript(name)) {
+    /* ends with digit but not a subscript, so do exponent handling like m3 */
+    count = name[length-1] - '0';
+    length--;
+    if (strchr(number_start, name[length-1])){
+      free(name);
+      return UNITEND;
+    }
+  } else count=1;
+
+  free(name);
+    
+  output = getnewunit();
+  if (!output)
+    return MEMERROR;
+  output->numerator[count--]=0;
+  for(;count>=0;count--)
+    output->numerator[count] = dupnstr(inptr, length);
+  lvalp->unit=output;
+  return UNIT;
+}
+
+
+void yyerror(struct commtype *comm, char *s){}
+
+
+int
+parseunit(struct unittype *output, char const *input,char **errstr,int *errloc)
+{
+  struct commtype comm;
+  int saveunitcount;
+
+  saveunitcount = unitcount;
+  initializeunit(output);
+  comm.result = 0;
+  comm.location = 0;
+  comm.data = input;
+  comm.errorcode = E_PARSE;    /* Assume parse error */
+  errno=0;
+  /* errno should only be set in the case of invalid function arguments */
+  if (yyparse(&comm) || errno){
+    if (comm.location==-1) 
+      comm.location = strlen(input);
+    if (errstr){
+      if (comm.errorcode==E_FUNC || errno)
+        *errstr = strerror(errno);
+      else
+        *errstr=errormsg[comm.errorcode];
+    }
+    if (errloc)
+      *errloc = comm.location;
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory with error: %d in %d out\n",
+             saveunitcount, unitcount);
+    return comm.errorcode;
+  } else {
+    if (errstr)
+      *errstr = 0;
+    multunit(output,comm.result);
+    destroyunit(comm.result);
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory without error: %d in %d out\n",
+	      saveunitcount, unitcount);
+    return 0;
+  }
+}
+
+
diff --git a/strfunc.c b/strfunc.c
new file mode 100644
index 0000000..e2aab4b
--- /dev/null
+++ b/strfunc.c
@@ -0,0 +1,109 @@
+/*
+ *  Copyright (C) 1996 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 3 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *     
+ */
+
+#define NULL 0
+
+#define size_t int
+
+#ifdef NO_STRTOK
+
+/* Find the first ocurrence in S of any character in ACCEPT.  */
+char *
+strpbrk(char *s, char *accept)
+{
+  while (*s != '\0')
+    if (strchr(accept, *s) == NULL)
+      ++s;
+    else
+      return (char *) s;
+
+  return NULL;
+}
+
+
+static char *olds = NULL;
+
+char *
+strtok(char *s, char *delim)
+{
+  char *token;
+
+  if (s == NULL)
+    {
+      if (olds == NULL)
+	{
+	  /*errno = EINVAL;  Wonder where errno is defined....*/
+	  return NULL;
+	}
+      else
+	s = olds;
+    }
+
+  /* Scan leading delimiters.  */
+  s += strspn(s, delim);
+  if (*s == '\0')
+    {
+      olds = NULL;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk(token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    olds = NULL;
+  else
+    {
+      /* Terminate the token and make OLDS point past it.  */
+      *s = '\0';
+      olds = s + 1;
+    }
+  return token;
+}
+
+
+#endif /* NO_STRTOK */
+
+#ifdef NO_STRSPN
+
+/* Return the length of the maximum initial segment
+   of S which contains only characters in ACCEPT.  */
+size_t
+strspn(char *s, char *accept)
+{
+  register char *p;
+  register char *a;
+  register size_t count = 0;
+
+  for (p = s; *p != '\0'; ++p)
+    {
+      for (a = accept; *a != '\0'; ++a)
+	if (*p == *a)
+	  break;
+      if (*a == '\0')
+	return count;
+      else
+	++count;
+    }
+
+  return count;
+}
+
+#endif NO_STRSPN
diff --git a/units-test.js b/units-test.js
new file mode 100644
index 0000000..b94fb92
--- /dev/null
+++ b/units-test.js
@@ -0,0 +1,31 @@
+const units = require('./units.lib.js');
+
+const readline = require("readline");
+const rl = readline.createInterface({
+    input: process.stdin,
+    output: process.stdout
+});
+
+
+units().then(Module => {
+    let test = Module.cwrap('test_int', 'number', ['number', 'number']);
+    let _do_a_conversion = Module.cwrap('do_a_conversion', 'number', ['number', 'number']);
+
+    function do_a_conversion(from, to) {
+        let from_c = Module.stringToNewUTF8(from);
+        let to_c = Module.stringToNewUTF8(to);
+        let lenBefore = globalThis.printBuffer.length;
+        _do_a_conversion(from_c, to === "" ? 0 : to_c);
+        return globalThis.printBuffer.slice(lenBefore).trim();
+    }
+
+    let ask = () => rl.question("From ", from => {
+        rl.question("To ", to => {
+            console.log(do_a_conversion(from, to));
+            ask();
+        });
+    });
+
+    ask();
+
+});
diff --git a/units.c b/units.c
new file mode 100644
index 0000000..124b622
--- /dev/null
+++ b/units.c
@@ -0,0 +1,6760 @@
+#define VERSION "2.23"
+/*
+ *  units, a program for units conversion
+ *  Copyright (C) 1996, 1997, 1999, 2000-2007, 2009, 2011-2020, 2022, 2024
+ *  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 3 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA 02110-1301 USA
+ *     
+ *  This program was written by Adrian Mariano (adrianm@gnu.org)
+ */
+
+#define LICENSE "\
+Copyright (C) 2024 Free Software Foundation, Inc.\n\
+GNU Units comes with ABSOLUTELY NO WARRANTY.\n\
+You may redistribute copies of GNU Units\n\
+under the terms of the GNU General Public License."
+
+#define _XOPEN_SOURCE 600
+
+#if defined (_WIN32) && defined (_MSC_VER)
+# include <windows.h>
+# include <winbase.h>
+#endif
+#if defined (_WIN32) && defined (HAVE_MKS_TOOLKIT)
+# include <sys/types.h>
+#endif
+# include <sys/stat.h>
+
+#include <ctype.h>
+#include <float.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+
+#if defined (_WIN32) && defined (_MSC_VER)
+# include <io.h>
+# define fileno _fileno
+# define isatty _isatty
+# define stat   _stat
+#endif
+
+#ifdef HAVE_IOCTL
+#  include <sys/ioctl.h>
+#  include <fcntl.h>
+#endif
+
+#ifndef NO_SETLOCALE
+#  include<locale.h>
+#endif
+
+#ifdef SUPPORT_UTF8
+/* Apparently this define is needed to get wcswidth() prototype */
+#  include <wchar.h>
+#  include <langinfo.h>
+#  define UTF8VERSTR "with utf8"
+#else
+#  define UTF8VERSTR "without utf8"
+#endif
+
+#ifdef READLINE
+#  define RVERSTR "with readline"
+#  include <readline/readline.h>
+#  include <readline/history.h>
+#  define HISTORY_FILE ".units_history"
+#else
+#  define RVERSTR "without readline"
+#endif
+
+#include "getopt.h"
+#include "units.h"
+
+#ifndef UNITSFILE
+#  define UNITSFILE "definitions.units"
+#endif
+
+#ifndef LOCALEMAP
+#  define LOCALEMAP "locale_map.txt"
+#endif
+
+#ifndef DATADIR
+#  ifdef _WIN32
+#    define DATADIR   "..\\share\\units"
+#  else
+#    define DATADIR   "../share/units"
+#  endif
+#endif
+
+#if defined (_WIN32) && defined (_MSC_VER)
+#  include <direct.h>
+#  define getcwd _getcwd
+#else
+#  include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#  define EXE_EXT ".exe"
+#  define PATHSEP ';'
+#  define DIRSEP '\\'
+#  define DIRSEPSTR "\\"        /* for building pathnames */
+#else
+#  define EXE_EXT ""
+#  define PATHSEP ':'
+#  define DIRSEP '/'
+#  define DIRSEPSTR "/"         /* for building pathnames */
+#endif
+
+#define PRIMITIVECHAR '!'       /* Character that marks irreducible units */
+#define COMMENTCHAR '#'         /* Comments marked by this character */
+#define COMMANDCHAR '!'         /* Unit database commands marked with this */
+#define UNITSEPCHAR ';'         /* Separator for unit lists.  Include this */
+                                /*    char in rl_basic_word_break_characters */
+                                /*    and in nonunitchars defined in parse.y */
+#define FUNCSEPCHAR ';'         /* Separates forward and inverse definitions */
+#define REDEFCHAR '+'           /* Mark unit as redefinition to suppress warning message */
+#ifdef _WIN32
+#  define DEFAULTPAGER "more"             /* Default pager for Windows */
+#else
+#  define DEFAULTPAGER "/usr/bin/pager"   /* Default pager for Unix */
+#endif
+#define DEFAULTLOCALE "en_US"   /* Default locale */
+#define MAXINCLUDE 5            /* Max depth of include files */
+#define MAXFILES 25             /* Max number of units files on command line */
+#define NODIM "!dimensionless"  /* Marks dimensionless primitive units, such */
+                                /*    as the radian, which are ignored when */
+                                /*    doing unit comparisons */
+#define NOPOINT -1              /* suppress display of pointer in processunit*/
+#define NOERRMSG -2             /* no error messages in checkunitlist() */
+#define ERRMSG -3
+#define SHOWFILES -4
+
+#define MAXHISTORYFILE 5000     /* max length of history file for readline */
+
+#define MAXPRODUCTREDUCTIONS 1000  /* If we make this many reductions, declare */
+                                   /*    a circular reference */
+
+/* values for output number format */
+#define BASE_FORMATS "gGeEf"    /* printf() format types recognized pre-C99 */
+#define DEFAULTPRECISION 8      /* default significant digits for printf() */
+#define DEFAULTTYPE 'g'         /* default number format type for printf() */
+#define MAXPRECISION DBL_DIG    /* maximum number precision for printf() */
+
+#define HOME_UNITS_ENV "MYUNITSFILE"  /* Personal units file environment var */
+
+#define NOERROR_KEYWORD "noerror "   /* The trailing space is important */
+#define CO_NOARG -1
+
+#define HELPCOMMAND "help"      /* Command to request help at prompt */
+#define SEARCHCOMMAND "search"  /* Command to request text search of units */
+#define UNITMATCH "?"           /* Command to request conformable units */
+char *exit_commands[]={"quit","exit",0};
+char *all_commands[]={"quit","exit",HELPCOMMAND,SEARCHCOMMAND,UNITMATCH,0};
+
+/* Key words for function definitions */
+struct {
+  char *word;
+  char delimit;
+  int checkopen;  /* allow open intervals with parentheses */
+} fnkeywords[]={ {"units=",  FUNCSEPCHAR, 0}, 
+                 {"domain=", ',', 1}, 
+                 {"range=", ',',1}, 
+                 {NOERROR_KEYWORD, ' ',CO_NOARG}, 
+                 {0,0}};
+#define FN_UNITS 0
+#define FN_DOMAIN 1
+#define FN_RANGE 2
+#define FN_NOERROR 3
+
+char *builtins[] = {"sin", "cos", "tan","ln", "log", "exp", 
+                    "acos", "atan", "asin", "sqrt", "cuberoot", "per",
+                    "sinh", "cosh", "tanh", "asinh", "atanh", "acosh", 0};
+
+struct {
+  char *format;         /* printf() format specification for numeric output */
+  int width;            /* printf() width from format */
+  int precision;        /* printf() precision from format */
+  char type;            /* printf() type from format */
+} num_format;
+
+
+struct {    /* Program command line option flags */
+ int
+   interactive,
+   unitlists,     /* Perform unit list output if set */
+   oneline,       /* Suppresses the second line of output */
+   quiet,         /* Supress prompting (-q option) */
+   round,         /* Round the last of unit list output to nearest integer */
+   showconformable, /*   */
+   showfactor,    /*   */
+   strictconvert, /* Strict conversion (disables reciprocals) */
+   unitcheck,     /* Enable unit checking: 1 for regular check, 2 for verbose*/
+   verbose,       /* Flag for output verbosity */
+   readline;      /* Using readline library? */
+} flags;
+
+
+#define UTF8MARKER "\xEF\xBB\xBF"   /* UTF-8 marker inserted by some Windows */
+                                    /* programs at start of a UTF-8 file */
+
+struct parseflag parserflags;   /* parser options */
+
+
+char *homeunitsfile = ".units"; /* Units filename in home directory */
+char *homedir = NULL;           /* User's home direcotry */
+char *homedir_error = NULL;     /* Error message for home directory search */
+char *pager;                    /* Actual pager (from PAGER environment var) */
+char *mylocale;                 /* Locale in effect (from LC_CTYPE or LANG) */
+int utf8mode;                   /* Activate UTF8 support */
+char *powerstring = "^";        /* Exponent character used in output */
+char *unitsfiles[MAXFILES+1];   /* Null terminated list of units file names */
+char *logfilename=NULL;         /* Filename for logging */
+FILE *logfile=NULL;             /* File for logging */
+char *promptprefix=NULL;        /* Prefix added to prompt */
+char *progname;                 /* Used in error messages */
+char *fullprogname;             /* Full path of program; printversion() uses */
+char *progdir;                  /* Used to find supporting files */
+char *datadir;                  /* Used to find supporting files */
+char *deftext="";/* Output text when printing definition */
+char *digits = "0123456789.,";
+
+
+#define QUERYHAVE  "You have: "  /* Prompt text for units to convert from */
+#define QUERYWANT  "You want: "  /* Prompt text for units to convert to */
+
+#define LOGFROM    "From: "     /* tag for log file */
+#define LOGTO      "To:   "     /* tag for log file */
+
+
+#define  HASHSIZE 101           /* Values from K&R 2nd ed., Sect. 6.6 */
+#define  HASHNUMBER 31
+
+#define  SIMPLEHASHSIZE 128
+#define  simplehash(str) (*(str) & 127)    /* "hash" value for prefixes */
+
+#define POINTER  "^"		/* pointer to location of a parse error */
+
+#define ERRNUMFMT "%.8g"        /* Numerical format for certain error messages */
+
+char *errormsg[]={
+          /*  0 */ "Successful completion", 
+          /*  1 */ "Parse error",           
+          /*  2 */ "Product overflow",      
+          /*  3 */ "Unit reduction error (bad unit definition)",
+	  /*  4 */ "Circular unit definition",
+          /*  5 */ "Invalid sum or difference of non-conformable units",
+          /*  6 */ "Unit not dimensionless",
+          /*  7 */ "Unit not a root",
+          /*  8 */ "Unknown unit",
+          /*  9 */ "Bad argument",
+          /* 10 */ "Weird nonlinear unit type (bug in program)",
+          /* 11 */ "Function argument has wrong dimension",
+          /* 12 */ "Argument of function outside domain",
+          /* 13 */ "Nonlinear unit definition has unit error",
+          /* 14 */ "No inverse defined",
+          /* 15 */ "Parser memory overflow (recursive function definition?)",
+          /* 16 */ "Argument wrong dimension or bad nonlinear unit definition",
+          /* 17 */ "Cannot open units file",
+          /* 18 */ "Units file contains errors",
+          /* 19 */ "Memory allocation error",
+          /* 20 */ "Malformed number",
+          /* 21 */ "Unit name ends with a digit other than 0 or 1 without preceding '_'",
+          /* 22 */ "No previous result; '_' not set",
+          /* 23 */ "Base unit not dimensionless; rational exponent required",
+          /* 24 */ "Base unit not a root",
+          /* 25 */ "Exponent not dimensionless",
+          /* 26 */ "Unknown function name",
+          /* 27 */ "Overflow: number too large",
+          /* 28 */ "Underflow: number too small"
+};
+
+char *invalid_utf8 = "invalid/nonprinting UTF-8";
+
+char *irreducible=0;            /* Name of last irreducible unit */
+
+
+/* Hash table for unit definitions. */
+
+struct unitlist {
+   char *name;                  /* unit name */
+   char *value;                 /* unit value */
+   int linenumber;              /* line in units data file where defined */
+   char *file;                  /* file where defined */ 
+   struct unitlist *next;       /* next item in list */
+} *utab[HASHSIZE];
+
+
+/* Table for prefix definitions. */
+
+struct prefixlist {
+   int len;                     /* length of name string */
+   char *name;                  /* prefix name */
+   char *value;                 /* prefix value */
+   int linenumber;              /* line in units data file where defined */
+   char *file;                  /* file where defined */ 
+   struct prefixlist *next;     /* next item in list */
+} *ptab[SIMPLEHASHSIZE];
+
+
+struct wantalias {
+  char *name;
+  char *definition;
+  struct wantalias *next;
+  int linenumber;
+  char *file;
+};
+
+struct wantalias *firstalias = 0;
+struct wantalias **aliaslistend = &firstalias; /* Next list entry goes here */
+
+/* Table for function definitions */
+
+struct func *ftab[SIMPLEHASHSIZE];
+
+/* 
+   Used for passing parameters to the parser when we are in the process
+   of parsing a unit function.  If function_parameter is non-nil, then 
+   whenever the text in function_parameter appears in a unit expression
+   it is replaced by the unit value stored in parameter_value.
+*/
+
+char *function_parameter = 0; 
+struct unittype *parameter_value = 0;
+
+/* Stores the last result value for replacement with '_' */
+
+int lastunitset = 0;
+struct unittype lastunit;
+
+char *NULLUNIT = "";  /* Used for units that are canceled during reduction */
+
+#define startswith(string, prefix) (!strncmp(string, prefix, strlen(prefix)))
+#define lastchar(string) (*((string)+strlen(string)-1))
+#define emptystr(string) (*(string)==0)
+#define nonempty(list)  ((list) && *(list))
+
+
+#ifdef READLINE
+
+char *historyfile;           /* Filename for readline history */
+int init_history_length;      /* Length of history read from the history file*/
+int init_history_base;
+
+void
+save_history(void)
+{
+  int newentries;
+  int err;
+
+  newentries = history_length-init_history_length;
+  if (history_max_entries > 0){
+    newentries += history_base - init_history_base;
+    if (newentries > history_max_entries)
+      newentries = history_max_entries;
+  }
+  
+  err = append_history(newentries,historyfile);
+  if (err){
+    if (err == ENOENT)
+      err = write_history(historyfile);
+    if (err) {
+      printf("Unable to write history to '%s': %s\n",historyfile,strerror(err));
+      return;
+    }
+  } 
+  history_truncate_file(historyfile,MAXHISTORYFILE);
+}
+#endif
+
+
+/* Increases the buffer by BUFGROW bytes and leaves the new pointer in buf
+   and the new buffer size in bufsize. */
+
+#define BUFGROW 100
+
+void
+growbuffer(char **buf, int *bufsize)
+{
+  int usemalloc;
+
+  usemalloc = !*buf || !*bufsize;
+  *bufsize += BUFGROW;
+  if (usemalloc)
+    *buf = malloc(*bufsize);
+  else
+    *buf = realloc(*buf,*bufsize);
+  if (!*buf){
+    fprintf(stderr, "%s: memory allocation error (growbuffer)\n",progname);  
+    exit(EXIT_FAILURE); 
+  }
+}
+
+
+FILE *
+openfile(char *file,char *mode)
+{
+  FILE *fileptr;
+
+  struct stat statbuf;
+  if (stat(file, &statbuf)==0 && statbuf.st_mode & S_IFDIR){
+    errno=EISDIR;
+    return NULL;
+  }
+  fileptr = fopen(file,mode);
+  return fileptr;
+}  
+
+
+
+void
+logprintf(const char *format, ...)
+{
+  va_list args;
+
+  va_start(args, format);
+  vprintf(format, args);
+  va_end(args);
+  if (logfile) {
+    va_start(args, format);
+    vfprintf(logfile, format, args);
+    va_end(args);
+  }
+}
+
+void
+logputchar(char c)
+{
+  putchar(c);
+  if (logfile) fputc(c, logfile);
+}
+
+void
+logputs(const char *s)
+{
+  fputs(s, stdout);
+  if (logfile) fputs(s, logfile);
+}
+    
+
+/* Look for a subscript in the input string.  A subscript starts with
+   '_' and is followed by a sequence of only digits (matching the
+   regexp "_[0-9]+").  The function returns 1 if it finds a subscript
+   and zero otherwise.  Note that it returns 1 for an input that is 
+   entirely subscript, with the '_' appearing in the first position. */
+
+int
+hassubscript(const char *str)
+{
+  const char *ptr = &lastchar(str);
+  while (ptr>str){
+    if (!strchr(digits, *ptr))
+      return 0;
+    ptr--;
+    if (*ptr=='_')
+      return 1;
+  }
+  return 0;
+}
+
+
+/* replace various Unicode operator symbols with stanard ASCII equivalents */
+void
+replace_operators(char *input)
+{
+  struct{
+    char *unicode;
+    char replacement;
+  } repl_table[]={
+                   {"\xE2\x80\x92", '-'}, /* U+2012: figure dash */
+                   {"\xE2\x80\x93", '-'}, /* U+2013: en dash */
+                   {"\xE2\x88\x92", '-'}, /* U+2212: minus */
+                   {"\xC3\x97", '*'},     /* U+00D7: times */
+                   {"\xE2\xA8\x89" ,'*'}, /* U+2A09: N-ary times operator */
+                   {"\xC2\xB7", '*'},     /* U+00B7: middle dot */
+                   {"\xE2\x8B\x85", '*'}, /* U+22C5: dot operator */
+                   {"\xC3\xB7", '/'},     /* U+00F7: division sign */
+                   {"\xE2\x88\x95", '/'}, /* U+2215: division slash */
+                   {"\xE2\x81\x84", '|'}, /* U+2044: fraction slash */
+                   {0,0}
+  };
+  char *inptr, *outptr, *ptr;
+
+  for (int i=0; repl_table[i].unicode; i++) {
+    inptr = outptr = input;
+    do {
+      ptr = strstr(inptr, repl_table[i].unicode);    /* find next unicode symbol */
+      if (ptr) {
+        while (inptr < ptr)                          /* copy the input up to the unicode */
+          *outptr++ = *inptr++;
+        inptr = ptr + strlen(repl_table[i].unicode); /* skip over unicode */
+        *outptr++ = repl_table[i].replacement;       /* Output replacement */
+      }
+    } while (ptr);
+    
+    /* If replacement were made, copy remaining input to end of string */
+
+    if (inptr > input) {
+      while (*inptr)
+        *outptr++ = *inptr++;
+      *outptr = '\0';
+    }
+  }
+}
+
+
+
+/* Replace all control chars with a space */
+
+void
+replacectrlchars(char *string)
+{
+  for(;*string;string++)
+    if (iscntrl(*string))
+      *string = ' ';
+}
+
+/* 
+   Fetch a line of data with backslash for continuation.  The
+   parameter count is incremented to report the number of newlines
+   that are read so that line numbers can be accurately reported. 
+*/
+
+char *
+fgetscont(char *buf, int size, FILE *file, int *count)
+{
+  if (!fgets(buf,size,file))
+    return 0;
+  (*count)++;
+  while(strlen(buf)>=2 && 0==strcmp(buf+strlen(buf)-2,"\\\n")){
+    (*count)++;
+    buf[strlen(buf)-2] = 0; /* delete trailing \n and \ char */
+    if (strlen(buf)>=size-1) /* return if the buffer is full */
+      return buf;
+    if (!fgets(buf+strlen(buf), size - strlen(buf), file))
+      return buf;  /* already read some data so return success */
+  }
+  if (lastchar(buf) == '\\') {   /* If last char of buffer is \ then   */
+    ungetc('\\', file);           /* we don't know if it is followed by */
+    lastchar(buf) = 0;           /* a \n, so put it back and try again */
+  }
+  return buf;
+}
+
+
+/* 
+   Gets arbitrarily long input data into a buffer using growbuffer().
+   Returns 0 if no data is read.  Increments count by the number of
+   newlines read unless it points to NULL. 
+ 
+   Replaces tabs and newlines with spaces before returning the result.
+*/
+
+char *
+fgetslong(char **buf, int *bufsize, FILE *file, int *count)
+{
+  int dummy;
+  if (!count)
+    count = &dummy;
+  if (!*bufsize) growbuffer(buf,bufsize);
+  if (!fgetscont(*buf, *bufsize, file, count))
+    return 0;
+  while (lastchar(*buf) != '\n' && !feof(file)){
+    growbuffer(buf, bufsize);
+    fgetscont(*buf+strlen(*buf), *bufsize-strlen(*buf), file, count);
+    (*count)--;
+  }  
+  /* These nonprinting characters must be removed so that the test
+     for UTF-8 validity will work. */ 
+  replacectrlchars(*buf);
+  return *buf;
+}
+
+/* Allocates memory and aborts if malloc fails. */
+
+void *
+mymalloc(int bytes,const char *mesg)
+{
+   void *pointer;
+
+   pointer = malloc(bytes);
+   if (!pointer){
+     fprintf(stderr, "%s: memory allocation error %s\n", progname, mesg);
+     exit(EXIT_FAILURE);
+   }
+   return pointer;
+}
+
+
+/* Duplicates a string */
+
+char *
+dupstr(const char *str)
+{
+   char *ret;
+
+   ret = mymalloc(strlen(str) + 1,"(dupstr)");
+   strcpy(ret, str);
+   return ret;
+}
+
+/* Duplicates a string that is not null-terminated, 
+   adding the null to the copy */
+
+char *
+dupnstr(const char *string, int length)
+{  
+  char *newstr;
+  newstr = mymalloc(length+1,"(dupnstr)");
+  strncpy(newstr, string, length);
+  newstr[length]=0;
+  return newstr;
+}
+  
+
+#ifdef SUPPORT_UTF8
+
+/* 
+   The strwidth function gives the printed width of a UTF-8 byte sequence.
+   It will return -1 if the sequence is an invalid UTF-8 sequence or
+   if the sequence contains "nonprinting" characters.  Note that \n and \t are 
+   "nonprinting" characters. 
+*/
+
+int 
+strwidth(const char *str)
+{
+  wchar_t *widestr;
+  int len;
+
+  if (!utf8mode)
+    return strlen(str);
+  len = strlen(str)+1;
+  widestr = mymalloc(sizeof(wchar_t)*len, "(strwidth)");
+  len = mbsrtowcs(widestr, &str, len, NULL);
+
+  if (len==-1){
+    free(widestr);
+    return -1; /* invalid multibyte sequence */
+  }
+
+  len=wcswidth(widestr, len);
+  free(widestr);
+  return len;
+}
+#else
+#  define strwidth strlen
+#endif 
+
+
+
+/* hashing algorithm for units */
+
+unsigned
+uhash(const char *str)
+{
+   unsigned hashval;
+
+   for (hashval = 0; *str; str++)
+      hashval = *str + HASHNUMBER * hashval;
+   return (hashval % HASHSIZE);
+}
+
+
+/* Lookup a unit in the units table.  Returns the definition, or NULL
+   if the unit isn't found in the table. */
+
+struct unitlist *
+ulookup(const char *str)
+{
+   struct unitlist *uptr;
+
+   for (uptr = utab[uhash(str)]; uptr; uptr = uptr->next)
+      if (strcmp(str, uptr->name) == 0)
+         return uptr;
+   return NULL;
+}
+
+/* Lookup a prefix in the prefix table.  Finds the longest prefix that
+   matches the beginning of the input string.  Returns NULL if no
+   prefixes match. */
+
+struct prefixlist *
+plookup(const char *str)
+{
+   struct prefixlist *prefix;
+   struct prefixlist *bestprefix=NULL;
+   int bestlength=0;
+
+   for (prefix = ptab[simplehash(str)]; prefix; prefix = prefix->next) {
+     if (prefix->len > bestlength && !strncmp(str, prefix->name, prefix->len)){
+       bestlength = prefix->len;
+       bestprefix = prefix;
+     }
+   }
+   return bestprefix;
+}
+
+/* Look up function in the function linked list */
+
+struct func *
+fnlookup(const char *str)
+{ 
+  struct func *funcptr;
+
+  for(funcptr=ftab[simplehash(str)];funcptr;funcptr = funcptr->next)
+    if (!strcmp(funcptr->name, str))
+      return funcptr;
+  return 0;
+}
+
+struct wantalias *
+aliaslookup(const char *str)
+{
+  struct wantalias *aliasptr;
+  for(aliasptr = firstalias; aliasptr; aliasptr=aliasptr->next)
+    if (!strcmp(aliasptr->name, str))
+      return aliasptr;
+  return 0;
+}
+
+
+/* Insert a new function into the linked list of functions */
+
+void
+addfunction(struct func *newfunc)
+{
+  int val;
+
+  val = simplehash(newfunc->name);
+  newfunc->next = ftab[val];
+  ftab[val] = newfunc;
+}
+
+
+/* Free the fields in the function except for the name so that it
+   can be redefined.  It remains in position in the linked list. */
+void
+freefunction(struct func *funcentry)
+{
+  if (funcentry->table){
+    free(funcentry->table);
+    free(funcentry->tableunit);
+  } else {
+    free(funcentry->forward.param);
+    free(funcentry->forward.def);
+    if (funcentry->forward.domain_min) free(funcentry->forward.domain_min);
+    if (funcentry->forward.domain_max) free(funcentry->forward.domain_max);
+    if (funcentry->inverse.domain_min) free(funcentry->inverse.domain_min);
+    if (funcentry->inverse.domain_max) free(funcentry->inverse.domain_max);
+    if (funcentry->forward.dimen) free(funcentry->forward.dimen);
+    if (funcentry->inverse.dimen) free(funcentry->inverse.dimen);
+    if (funcentry->inverse.def) free(funcentry->inverse.def);    
+    if (funcentry->inverse.param) free(funcentry->inverse.param);
+  }
+}
+
+/* Remove leading and trailing spaces from the input */
+
+void
+removespaces(char *in)
+{
+  char *ptr;
+  if (*in) {
+    for(ptr = in + strlen(in) - 1; *ptr==' '; ptr--); /* Last non-space */
+    *(ptr+1)=0;
+    if (*in==' '){
+      ptr = in + strspn(in," ");
+      memmove(in, ptr, strlen(ptr)+1);
+    }
+  }
+}
+
+
+/* 
+   Looks up an inverse function given as a ~ character followed by
+   spaces and then the function name.  The spaces will be deleted as a
+   side effect.  If an inverse function is found returns the function
+   pointer, otherwise returns null.
+*/
+
+struct func *
+invfnlookup(char *str)
+{
+  if (*str != '~')
+    return 0;
+  removespaces(str+1);
+  return fnlookup(str+1);
+}
+
+
+char *
+strip_comment(char *line)
+{
+  char *comment = 0;
+  
+  if ((line = strchr(line,COMMENTCHAR))) {
+    comment = line+1;
+    *line = 0;
+  }
+  return comment;
+}
+
+
+/* Print string but replace two consecutive spaces with one space. */
+
+void
+tightprint(FILE *outfile, char *string)
+{
+  while(*string){
+    fputc(*string, outfile);
+    if (*string != ' ') string++;
+    else while(*string==' ') string++;
+  }
+}
+
+/*
+    Copy string to buf, replacing two or more consecutive spaces with
+    one space.
+*/
+void
+tightbufprint(char *buf, char *string)
+{
+  while(*string) {
+    *buf++ = *string;
+    if (*string != ' ')
+	string++;
+    else {
+	while(*string==' ')
+	    string++;
+    }
+  }
+  *buf = '\0';
+}
+
+
+#define readerror (goterr=1) && errfile && fprintf
+
+#define VAGUE_ERR "%s: error in units file '%s' line %d\n", \
+                       progname, file, linenum
+
+/* Print out error message encountered while reading the units file. */
+
+
+/*
+  Splits the line into two parts.  The first part is space delimited.
+  The second part is everything else.  Removes trailing spaces from
+  the second part.  Returned items are null if no parameter was found.
+*/
+  
+void
+splitline(char *line, char **first, char **second)
+{
+  *second = 0;
+  *first = strtok(line, " ");
+  if (*first){
+    *second = strtok(0, "\n");
+    if (*second){
+      removespaces(*second);
+      if (emptystr(*second))
+        *second = 0;
+    }
+  }
+}
+
+
+/* see if character is part of a valid decimal number */
+
+int
+isdecimal(char c)
+{
+  return strchr(digits, c) != NULL;
+}
+
+
+/* 
+   Check for some invalid unit names.  Print error message.  Returns 1 if 
+   unit name is bad, zero otherwise.  
+*/
+int
+checkunitname(char *name, int linenum, char *file, FILE *errfile)
+{
+  char nonunitchars[] = "~;+-*/|^)";  /* Also defined in parse.y with a few more characters */
+  char **ptr;
+  char *cptr;
+
+  if ((cptr=strpbrk(name, nonunitchars))){
+    if (errfile) fprintf(errfile,
+         "%s: unit '%s' in units file '%s' on line %d ignored.  It contains invalid character '%c'\n",
+              progname, name, file, linenum, *cptr);
+    return 1;
+  }
+  if (strchr(digits, name[0])){
+    if (errfile) fprintf(errfile,
+         "%s: unit '%s' in units file '%s' on line %d ignored.  It starts with a digit\n", 
+              progname, name, file, linenum);
+    return 1;
+  }
+  for(ptr=builtins;*ptr;ptr++)
+    if (!strcmp(name, *ptr)){
+      if (errfile) fprintf(errfile,
+           "%s: redefinition of built-in function '%s' in file '%s' on line %d ignored.\n",
+                           progname, name, file, linenum);
+      return 1;
+    }
+  for(ptr=all_commands;*ptr;ptr++)
+    if (!strcmp(name, *ptr)){
+      if (errfile) fprintf(errfile,
+           "%s: unit name '%s' in file '%s' on line %d may be hidden by command with the same name.\n",
+                           progname, name, file, linenum);
+    }
+  return 0;
+}
+
+int
+newunit(char *unitname, char *unitdef, int *count, int linenum, 
+        char *file,FILE *errfile, int redefine, int userunit)
+{
+  struct unitlist *uptr;
+  unsigned hashval; 
+
+  /* units ending with '_' create ambiguity for exponents */
+
+  if ((unitname[0]=='_' && !userunit) || lastchar(unitname)=='_'){
+    if (errfile) fprintf(errfile,
+       "%s: unit '%s' on line %d of '%s' ignored.  It starts or ends with '_'\n",
+              progname, unitname, linenum, file);
+    return E_BADFILE;
+  }
+
+  /* Units that end in [2-9] can never be accessed */
+  if (strchr(".,23456789", lastchar(unitname)) && !hassubscript(unitname)){
+    if (errfile) fprintf(errfile,
+       "%s: unit '%s' on line %d of '%s' ignored.  %s\n",
+        progname, unitname, linenum, file,errormsg[E_UNITEND]);
+    return E_BADFILE;
+  }
+
+  if (checkunitname(unitname, linenum, file, errfile))
+    return E_BADFILE;
+
+  if ((uptr=ulookup(unitname))) {    /* Is it a redefinition? */
+    if (flags.unitcheck && errfile && !redefine)
+      fprintf(errfile,
+      "%s: unit '%s' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+              progname, unitname, uptr->linenumber,uptr->file,
+              linenum, file);
+    free(uptr->value);
+  } else {       
+    /* make new units table entry */
+
+    uptr = (struct unitlist *) mymalloc(sizeof(*uptr),"(newunit)");
+    uptr->name = dupstr(unitname);
+
+    /* install unit name/value pair in list */
+
+    hashval = uhash(uptr->name);
+    uptr->next = utab[hashval];
+    utab[hashval] = uptr;
+    (*count)++;
+  }
+  uptr->value = dupstr(unitdef);
+  uptr->linenumber = linenum;
+  uptr->file = file;
+  return 0;
+}
+  
+
+
+int 
+newprefix(char *unitname, char *unitdef, int *count, int linenum, 
+          char *file,FILE *errfile, int redefine)
+{
+  struct prefixlist *pfxptr;
+  unsigned pval;
+
+  lastchar(unitname) = 0;
+  if (checkunitname(unitname,linenum,file,errfile))
+    return E_BADFILE;
+  if ((pfxptr = plookup(unitname))     /* already there: redefinition */
+      && !strcmp(pfxptr->name, unitname)){
+    if (flags.unitcheck && errfile && !redefine)
+      fprintf(errfile,
+             "%s: prefix '%s-' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+              progname, unitname, pfxptr->linenumber,pfxptr->file,
+              linenum, file);
+    free(pfxptr->value);
+  } else {  
+    pfxptr = (struct prefixlist *) mymalloc(sizeof(*pfxptr),"(newprefix)");  
+    pfxptr->name = dupstr(unitname);
+    pfxptr->len = strlen(unitname);
+    pval = simplehash(unitname);
+    pfxptr->next = ptab[pval];
+    ptab[pval] = pfxptr;
+    (*count)++;
+  }
+  pfxptr->value = dupstr(unitdef);
+  pfxptr->linenumber = linenum;
+  pfxptr->file = file;
+  return 0;
+}
+
+
+/* 
+   parsepair() looks for data of the form [text1,text2] where the ',' is a 
+   specified delimiter.  The second argument, text2, is optional and if it's 
+   missing then second is set to NULL.  The parameters are allowed to be 
+   empty strings.  The function returns the first character after the 
+   closing bracket if no errors occur or the NULL pointer on error.
+*/
+
+char *
+parsepair(char *input, char **first, char **second, 
+          int *firstopen, int *secondopen, char delimiter, int checkopen,
+          char *unitname, int linenum, char *file,FILE *errfile)
+{
+  char *start, *end, *middle;
+
+  start = strpbrk(input, checkopen?"[(":"[");
+  if (!start){
+    if (errfile) fprintf(errfile,
+             "%s: expecting '[' %s in definition of '%s' in '%s' line %d\n",
+             progname, checkopen ? "or '('":"", unitname, file, linenum);
+    return 0;
+  }
+  if (*start=='(') *firstopen=1;
+  else *firstopen=0;
+  *start++=0;
+  removespaces(input);
+  if (!emptystr(input)){
+    if (errfile) fprintf(errfile,
+        "%s: unexpected characters before '%c' in definition of '%s' in '%s' line %d\n",
+        progname, *firstopen?'(':'[',unitname, file, linenum);
+    return 0;
+  }
+  end = strpbrk(start, checkopen?"])":"]");
+  if (!end){
+    if (errfile) fprintf(errfile,
+             "%s: expecting ']' %s in definition of '%s' in '%s' line %d\n",
+             progname, checkopen?"or ')'":"",unitname, file, linenum);
+    return 0;
+  }
+  if (*end==')') *secondopen=1;
+  else *secondopen=0;
+  *end++=0;
+
+  middle = strchr(start,delimiter);
+  
+  if (middle){
+    *middle++=0;
+    removespaces(middle);
+    *second = middle;
+  } else
+    *second = 0;
+
+  removespaces(start);
+  *first = start;
+  return end;
+}
+
+
+/* 
+   Extract numbers from two text strings and place them into pointers. 
+   Has two error codes for decreasing interval or bad numbers in the 
+   text strings.  Returns 0 on success.  
+*/
+
+#define EI_ERR_DEC 1    /* Decreasing interval */
+#define EI_ERR_MALF 2   /* Malformed number */
+
+int
+extract_interval(char *first, char *second, 
+                 double **firstout, double **secondout)
+{
+  double val;
+  char *end;
+
+  if (!emptystr(first)){
+    val = strtod(first, &end);
+    if (*end)
+      return EI_ERR_MALF;
+    else {
+      *firstout=(double *)mymalloc(sizeof(double), "(extract_interval)");
+      **firstout = val;
+    }
+  }
+  if (nonempty(second)) {
+    val = strtod(second, &end);
+    if (*end)
+      return EI_ERR_MALF;
+    else if (*firstout && **firstout>=val)
+      return EI_ERR_DEC;
+    else {
+      *secondout=(double *)mymalloc(sizeof(double), "(extract_interval)");
+      **secondout = val;
+    }
+  }
+  return 0;
+}
+
+
+ 
+void 
+copyfunctype(struct functype *dest, struct functype *src)
+{
+  dest->domain_min_open = src->domain_min_open;
+  dest->domain_max_open = src->domain_max_open;
+  dest->param = dest->def = dest->dimen = NULL;
+  dest->domain_min = dest->domain_max = NULL;
+  if (src->param) dest->param = dupstr(src->param); 
+  if (src->def) dest->def = dupstr(src->def);
+  if (src->dimen) dest->dimen = dupstr(src->dimen);
+  if (src->domain_min){
+    dest->domain_min = (double *) mymalloc(sizeof(double), "(copyfunctype)");
+    *dest->domain_min = *src->domain_min;
+  }
+  if (src->domain_max){
+    dest->domain_max = (double *) mymalloc(sizeof(double), "(copyfunctype)");
+    *dest->domain_max = *src->domain_max;
+  }
+}
+
+
+int
+copyfunction(char *unitname, char *funcname, int *count, int linenum, 
+             char *file, FILE *errfile)
+{
+  struct func *source, *funcentry;
+  int i;
+  if (checkunitname(unitname, linenum, file, errfile))
+    return E_BADFILE;
+  removespaces(funcname);
+  i = strlen(funcname)-2;                /* strip trailing () if present */
+  if (i>0 && !strcmp(funcname+i,"()"))  
+    funcname[i]=0;
+  source = fnlookup(funcname);
+  if (!source) {
+    if (errfile){ 
+      if (!strpbrk(funcname," ;][()+*/-^")) 
+        fprintf(errfile,"%s: bad definition for '%s' in '%s' line %d, function '%s' not defined\n",
+                progname, unitname, file, linenum, funcname);
+      else
+        fprintf(errfile,"%s: bad function definition of '%s' in '%s' line %d\n",
+                progname,unitname,file,linenum);
+    }
+    return E_BADFILE;
+  }
+  if ((funcentry=fnlookup(unitname))){
+    if (flags.unitcheck && errfile)          
+      fprintf(errfile,
+             "%s: function '%s' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+              progname, unitname, funcentry->linenumber,funcentry->file,
+              linenum, file);
+    freefunction(funcentry);
+  } else {
+    funcentry = (struct func*)mymalloc(sizeof(struct func),"(newfunction)");    
+    funcentry->name = dupstr(unitname);
+    addfunction(funcentry);
+    (*count)++;
+  }
+  funcentry->linenumber = linenum;
+  funcentry->file = file;
+  funcentry->skip_error_check = source->skip_error_check;
+  if (source->table){
+    funcentry->tablelen = source->tablelen;
+    funcentry->tableunit = dupstr(source->tableunit);
+    funcentry->table = (struct pair *)
+        mymalloc(sizeof(struct pair)*funcentry->tablelen, "(copyfunction)");
+    for(i=0;i<funcentry->tablelen;i++){
+      funcentry->table[i].location = source->table[i].location;
+      funcentry->table[i].value = source->table[i].value;
+    }
+  } else {
+    funcentry->table = 0;
+      copyfunctype(&funcentry->forward, &source->forward);
+      copyfunctype(&funcentry->inverse, &source->inverse);
+  }
+  return 0;
+}
+
+
+#define FREE_STUFF {if (forward_dim) free(forward_dim);\
+                    if (inverse_dim) free(inverse_dim);\
+                    if (domain_min) free(domain_min);\
+                    if (domain_max) free(domain_max);\
+                    if (range_min) free(range_min);\
+                    if (range_max) free(range_max);}
+
+#define REPEAT_ERR \
+  if (errfile) fprintf(errfile, \
+     "%s: keyword '%s' repeated in definition of '%s' on line %d of '%s'.\n",\
+                      progname,fnkeywords[i].word,unitname, linenum, file)
+
+int
+newfunction(char *unitname, char *unitdef, int *count, 
+            int linenum, char *file,FILE *errfile, int redefine)
+{
+  char *start, *end, *inv, *forward_dim, *inverse_dim, *first, *second;
+  double *domain_min, *domain_max, *range_min, *range_max;
+  struct func *funcentry;
+  int looking_for_keywords,i, firstopen, secondopen;
+  int domain_min_open, domain_max_open, range_min_open, range_max_open;
+  int noerror = 0;
+
+  if (*unitname=='('){
+    if (errfile) fprintf(errfile,
+         "%s: unit '%s' on line %d of '%s' ignored.  It starts with a '('\n", 
+         progname, unitname, linenum, file);
+    return E_BADFILE;
+  }
+                                   /* coverity[returned_null] */
+  start = strchr(unitname,'(');
+  end = strchr(unitname,')');
+  *start++ = 0;
+    
+  if (checkunitname(unitname,linenum,file,errfile))
+    return E_BADFILE;
+  
+  if (start==end)  /* no argument: function() so make a function copy */
+    return copyfunction(unitname, unitdef, count, linenum, file, errfile);
+
+  if (!end || strlen(end)>1){
+    if (errfile) fprintf(errfile,
+              "%s: bad function definition of '%s' in '%s' line %d\n",
+              progname,unitname,file,linenum);
+    return E_BADFILE;
+  }
+  *end=0;
+  forward_dim = NULL;
+  inverse_dim = NULL;
+  domain_min = NULL;
+  domain_max = NULL;
+  range_min = NULL;
+  range_max = NULL;
+  domain_min_open = 0;
+  domain_max_open = 0;
+  range_min_open = 0;
+  range_max_open = 0;
+  looking_for_keywords=1;
+  while (looking_for_keywords) {
+    looking_for_keywords = 0;
+    for(i=0;fnkeywords[i].word;i++){
+      if (startswith(unitdef, fnkeywords[i].word)){
+        looking_for_keywords = 1;  /* found keyword so keep looking */
+        unitdef+=strlen(fnkeywords[i].word);
+        if (fnkeywords[i].checkopen!=CO_NOARG){
+          unitdef = parsepair(unitdef,&first, &second, &firstopen, &secondopen, 
+                            fnkeywords[i].delimit, fnkeywords[i].checkopen,
+                            unitname, linenum, file,errfile);
+          if (!unitdef){
+            FREE_STUFF;
+            return E_BADFILE;
+          }
+          removespaces(unitdef);
+        }
+        if (i==FN_NOERROR)
+          noerror = 1;
+        if (i==FN_UNITS){
+          if (forward_dim || inverse_dim){
+            REPEAT_ERR;
+            return E_BADFILE;
+          }
+          forward_dim = dupstr(first);
+          if (second)
+            inverse_dim = dupstr(second);
+        }
+        if (i==FN_DOMAIN){
+          int err=0;
+          if (domain_min || domain_max){
+            REPEAT_ERR;
+            return E_BADFILE;
+          }
+          err = extract_interval(first,second,&domain_min, &domain_max);
+          domain_min_open = firstopen;
+          domain_max_open = secondopen;
+          if (err)
+            FREE_STUFF;
+          if (err==EI_ERR_DEC){
+            if (errfile) fprintf(errfile,
+               "%s: second endpoint for domain must be greater than the first\n       in definition of '%s' in '%s' line %d\n",
+                progname, unitname, file, linenum);
+            return E_BADFILE;
+          }
+          if (err==EI_ERR_MALF){
+            if (errfile) fprintf(errfile,
+              "%s: malformed domain in definition of '%s' in '%s' line %d\n",
+              progname, unitname, file, linenum);
+            return E_BADFILE;
+          }
+        } 
+        if (i==FN_RANGE){
+          int err=0;
+          if (range_min || range_max){
+            REPEAT_ERR;
+            FREE_STUFF;
+            return E_BADFILE;
+          }
+          err = extract_interval(first,second,&range_min, &range_max);
+          range_min_open = firstopen;
+          range_max_open = secondopen;
+          if (err)
+            FREE_STUFF;
+          if (err==EI_ERR_DEC){
+            if (errfile) fprintf(errfile,
+               "%s: second endpoint for range must be greater than the first\n       in definition of '%s' in '%s' line %d\n",
+                progname, unitname, file, linenum);
+            return E_BADFILE;
+          }
+          if (err==EI_ERR_MALF){
+            if (errfile) fprintf(errfile,
+              "%s: malformed range in definition of '%s' in '%s' line %d\n",
+              progname, unitname, file, linenum);
+            return E_BADFILE;
+          }
+        }
+      }
+    }
+  }
+
+  if (emptystr(unitdef)){
+    if (errfile) fprintf(errfile,
+                 "%s: function '%s' lacks a definition at line %d of '%s'\n",
+                 progname, unitname, linenum, file);
+    FREE_STUFF;
+    return E_BADFILE;
+  }
+
+  if (*unitdef=='['){
+    if (errfile) fprintf(errfile,
+         "%s: function '%s' missing keyword before '[' on line %d of '%s'\n", 
+         progname, unitname, linenum, file);
+    FREE_STUFF;
+    return E_BADFILE;
+  }
+
+  /* Check that if domain and range are specified and nonzero then the 
+     units are given.  Otherwise these are meaningless.  */
+  if (!forward_dim &&
+      ((domain_min && *domain_min) || (domain_max && *domain_max))){ 
+    if (errfile)
+      fprintf(errfile,"%s: function '%s' defined on line %d of '%s' has domain with no units.\n", 
+              progname, unitname, linenum, file);
+    FREE_STUFF;
+    return E_BADFILE;
+  }
+  if (!inverse_dim &&
+      ((range_min && *range_min) || (range_max && *range_max))){ 
+    if (errfile)
+      fprintf(errfile,"%s: function '%s' defined on line %d of '%s' has range with no units.\n", 
+              progname, unitname, linenum, file);
+    FREE_STUFF;
+    return E_BADFILE;
+  }
+  if ((funcentry=fnlookup(unitname))){
+    if (flags.unitcheck && errfile && !redefine)
+      fprintf(errfile,
+             "%s: function '%s' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+              progname, unitname, funcentry->linenumber,funcentry->file,
+              linenum, file);
+    freefunction(funcentry);
+  } else {
+    funcentry = (struct func*)mymalloc(sizeof(struct func),"(newfunction)");    
+    funcentry->name = dupstr(unitname);
+    addfunction(funcentry);
+    (*count)++;
+  }
+  funcentry->table = 0;
+  funcentry->skip_error_check = noerror;
+  funcentry->forward.dimen = forward_dim;
+  funcentry->inverse.dimen = inverse_dim;
+  funcentry->forward.domain_min = domain_min;
+  funcentry->forward.domain_max = domain_max;
+  funcentry->inverse.domain_min = range_min;
+  funcentry->inverse.domain_max = range_max;
+  funcentry->forward.domain_min_open = domain_min_open;
+  funcentry->forward.domain_max_open = domain_max_open;
+  funcentry->inverse.domain_min_open = range_min_open;
+  funcentry->inverse.domain_max_open = range_max_open;
+  inv = strchr(unitdef,FUNCSEPCHAR);
+  if (inv)
+    *inv++ = 0;
+  funcentry->forward.param = dupstr(start);
+  removespaces(unitdef);
+  funcentry->forward.def = dupstr(unitdef);
+  if (inv){
+    removespaces(inv);
+    funcentry->inverse.def = dupstr(inv);
+    funcentry->inverse.param = dupstr(unitname);
+  } 
+  else {
+    funcentry->inverse.def = 0;
+    funcentry->inverse.param = 0;
+  }
+  funcentry->linenumber = linenum;
+  funcentry->file = file;
+  return 0;
+}
+
+
+int 
+newtable(char *unitname,char *unitdef, int *count,
+         int linenum, char *file,FILE *errfile, int redefine)
+{
+  char *start, *end;
+  char *tableunit;
+  int tablealloc, tabpt;
+  struct pair *tab;
+  struct func *funcentry;
+  int noerror = 0;
+
+                                      /* coverity[returned_null] */
+  tableunit = strchr(unitname,'[');
+  end = strchr(unitname,']');
+  *tableunit++=0;
+  if (checkunitname(unitname, linenum, file, errfile))
+    return E_BADFILE;
+  if (!end){
+    if (errfile) fprintf(errfile,"%s: missing ']' in units file '%s' line %d\n",
+              progname,file,linenum);
+    return E_BADFILE;
+  } 
+  if (strlen(end)>1){
+    if (errfile) fprintf(errfile,
+              "%s: unexpected characters after ']' in units file '%s' line %d\n",
+              progname,file,linenum);
+    return E_BADFILE;
+  }
+  *end=0;
+  tab = (struct pair *)mymalloc(sizeof(struct pair)*20, "(newtable)");
+  tablealloc=20;
+  tabpt = 0;
+  start = unitdef;
+  if (startswith(start, NOERROR_KEYWORD)) {
+    noerror = 1;
+    start += strlen(NOERROR_KEYWORD);
+    removespaces(start);
+  }
+  while (1) {
+    if (tabpt>=tablealloc){
+      tablealloc+=20;
+      tab = (struct pair *)realloc(tab,sizeof(struct pair)*tablealloc);
+      if (!tab){
+        if (errfile) fprintf(errfile, "%s: memory allocation error (newtable)\n",
+                  progname);  
+        return E_MEMORY;
+      }
+    }
+    tab[tabpt].location = strtod(start,&end);
+    if (start==end || (!emptystr(end) && *end !=' ')){ 
+      if (!emptystr(start)) {  
+        if (strlen(start)>15) start[15]=0;  /* Truncate for error msg display */
+        if (errfile) fprintf(errfile, 
+             "%s: cannot parse table definition %s at '%s' on line %d of '%s'\n", 
+             progname, unitname, start, linenum, file);
+        free(tab);
+        return E_BADFILE;
+      }
+      break;
+    }
+    if (tabpt>0 && tab[tabpt].location<=tab[tabpt-1].location){
+      if (errfile)
+        fprintf(errfile,"%s: points don't increase (" ERRNUMFMT " to " ERRNUMFMT
+                        ") in units file '%s' line %d\n",
+                progname, tab[tabpt-1].location, tab[tabpt].location,
+                file, linenum);
+      free(tab);
+      return E_BADFILE;
+    }
+    start=end+strspn(end," ");
+    tab[tabpt].value = strtod(start,&end);
+    if (start==end){
+      if (errfile)
+        fprintf(errfile,"%s: missing value after " ERRNUMFMT " in units file '%s' line %d\n",
+                progname, tab[tabpt].location, file, linenum);
+      free(tab);
+      return E_BADFILE;
+    }
+    tabpt++;
+    start=end+strspn(end," ,");
+  }
+  if ((funcentry=fnlookup(unitname))){
+    if (flags.unitcheck && errfile && !redefine)
+      fprintf(errfile,
+      "%s: unit '%s' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+                    progname, unitname, funcentry->linenumber,funcentry->file,
+                                         linenum, file);
+    freefunction(funcentry);
+  } else {
+    funcentry = (struct func *)mymalloc(sizeof(struct func),"(newtable)");
+    funcentry->name = dupstr(unitname);
+    addfunction(funcentry);
+    (*count)++;
+  }
+  funcentry->tableunit = dupstr(tableunit);
+  funcentry->tablelen = tabpt;
+  funcentry->table = tab;
+  funcentry->skip_error_check = noerror;
+  funcentry->linenumber = linenum;
+  funcentry->file = file;
+  return 0;
+}
+
+
+int
+newalias(char *unitname, char *unitdef,int linenum, char *file,FILE *errfile)
+{
+  struct wantalias *aliasentry;
+
+  if (!strchr(unitdef, UNITSEPCHAR)){
+    if (errfile) fprintf(errfile,
+            "%s: unit list missing '%c' on line %d of '%s'\n",
+            progname, UNITSEPCHAR, linenum, file);
+    return E_BADFILE;
+  }
+  if ((aliasentry=aliaslookup(unitname))){   /* duplicate alias */
+    if (flags.unitcheck && errfile)
+      fprintf(errfile,
+              "%s: unit list '%s' defined on line %d of '%s' is redefined on line %d of '%s'.\n",
+              progname, unitname, aliasentry->linenumber,
+              aliasentry->file, linenum, file);
+    free(aliasentry->definition);
+  } else { 
+    aliasentry = (struct wantalias *)
+      mymalloc(sizeof(struct wantalias),"(newalias)");
+    aliasentry->name = dupstr(unitname);
+    aliasentry->next = 0;
+    *aliaslistend = aliasentry;
+    aliaslistend = &aliasentry->next;
+  }
+  aliasentry->definition = dupstr(unitdef);
+  aliasentry->linenumber = linenum;
+  aliasentry->file = file;
+  return 0;
+}
+
+
+/* 
+   Check environment variable name to see if its value appears on the
+   space delimited text string pointed to by list.  Returns 2 if the
+   environment variable is not set, return 1 if its value appears on
+   the list and zero otherwise.
+*/
+
+int
+checkvar(char *name, char *list)
+{
+  char *listitem;
+  name = getenv(name);
+  if (!name)
+    return 2;
+  listitem = strtok(list," ");
+  while (listitem){
+    if (!strcmp(name, listitem))
+      return 1;
+    listitem = strtok(0," ");
+  }
+  return 0;
+}
+
+
+#ifdef NO_SETENV
+int
+setenv(const char *name, const char *val, int overwrite)
+{
+  char *environment;
+
+  if (!overwrite && getenv(name) != NULL)
+    return 0;
+  environment = (char *) malloc(strlen(name) + strlen(val) + 2);
+  if (!environment)
+    return 1;
+  strcpy(environment, name);
+  strcat(environment, "=");
+  strcat(environment, val);
+
+  /* putenv() doesn't copy its argument, so don't free environment */
+
+#if defined (_WIN32) && defined (_MSC_VER)
+  return _putenv(environment);
+#else
+  return putenv(environment);
+#endif
+}
+#endif
+
+#ifdef _WIN32
+#  define  isdirsep(c)  ((c) == '/' || (c) == '\\')
+#  define  hasdirsep(s) strpbrk((s),"/\\")
+#else
+#  define  isdirsep(c)  ((c) == '/')
+#  define  hasdirsep(s) strchr((s),'/')
+#endif
+#define  isexe(s) ((strlen(s) == 4) && (tolower(s[1]) == 'e') \
+                 && (tolower(s[2]) == 'x') && (tolower(s[3]) == 'e'))
+
+/* Returns a pointer to the end of the pathname part of the 
+   specified filename */
+
+char *
+pathend(char *filename)
+{
+  char *pointer;
+
+  for(pointer=filename+strlen(filename);pointer>filename;pointer--){
+    if (isdirsep(*pointer)) {
+      pointer++;
+      break;
+    }
+  }
+  return pointer;
+}
+
+int
+isfullpath(char *path)
+{
+#ifdef _WIN32
+  /* handle Windows drive specifier */
+  if (isalpha(*path) && *(path + 1) == ':')
+      path += 2;
+#endif
+  return isdirsep(*path);
+}
+
+/* 
+   Read in units data.  
+
+   file - Filename to load
+   errfile - File to receive messages about errors in the units database.  
+             Set it to 0 to suppress errors. 
+   unitcount, prefixcount, funccount - Return statistics to the caller.  
+                                       Must initialize to zero before calling.
+   depth - Used to prevent recursive includes.  Call with it set to zero.
+
+   The global variable progname is used in error messages.  
+*/
+
+int
+readunits(char *file, FILE *errfile, 
+          int *unitcount, int *prefixcount, int *funccount, int depth)
+{
+   FILE *unitfile;
+   char *line = 0, *lineptr, *unitdef, *unitname, *permfile;
+   int linenum, linebufsize, goterr, retcode;
+   int locunitcount, locprefixcount, locfunccount, redefinition;
+   int wronglocale = 0;   /* If set then we are currently reading data */
+   int inlocale = 0;      /* for the wrong locale so we should skip it */
+   int in_utf8 = 0;       /* If set we are reading utf8 data */
+   int invar = 0;         /* If set we are in data for an env variable.*/
+   int wrongvar = 0;      /* If set then we are not processing */
+
+   locunitcount = 0;
+   locprefixcount = 0;
+   locfunccount  = 0;
+   linenum = 0;
+   linebufsize = 0;
+   goterr = 0;
+
+   unitfile = openfile(file, "rt");
+   
+   if (!unitfile){
+     if (errfile)
+       fprintf(errfile, "%s: Unable to read units file '%s': %s\n", progname, file, strerror(errno));
+     return E_FILE;
+   }
+   growbuffer(&line,&linebufsize);
+                                            /* coverity[alloc_fn] */
+   permfile = dupstr(file);    /* This is a permanent copy to reference in */
+                               /* the database. It is never freed. */
+   while (!feof(unitfile)) {
+      if (!fgetslong(&line, &linebufsize, unitfile, &linenum)) 
+        break;
+      if (linenum==1 && startswith(line, UTF8MARKER)){ 
+        int i;
+        for(lineptr=line,i=0;i<strlen(UTF8MARKER);i++, lineptr++)
+          *lineptr=' ';
+      }
+      strip_comment(line);
+      if (-1 == strwidth(line)){
+        readerror(errfile, "%s: %s on line %d of '%s'\n",
+                      progname, invalid_utf8, linenum, file);
+        continue;
+      }
+      replace_operators(line);
+
+      if (*line == COMMANDCHAR) {         /* Process units file commands */
+        unitname = strtok(line+1, " ");
+        if (!unitname){
+          readerror(errfile, VAGUE_ERR);
+          continue;
+        }
+
+        /* Check for locale and utf8 declarations.  Any other commands
+           most likely belong after these tests. */
+        if (!strcmp(unitname,"var") || !strcmp(unitname,"varnot")){
+          int not = 0;
+          if (unitname[3]=='n')
+            not = 1;
+          unitname=strtok(0," ");
+          unitdef=strtok(0,"");  /* Get rest of the line */
+          if (!unitname)
+            readerror(errfile, 
+                      "%s: no variable name specified on line %d of '%s'\n",
+                      progname, linenum, file);
+          else if (!unitdef)
+            readerror(errfile, 
+                      "%s: no value specified on line %d of '%s'\n",
+                      progname, linenum, file);
+          else if (invar)
+            readerror(errfile,
+                 "%s: nested var statements not allowed, line %d of '%s'\n",
+                      progname, linenum, file);
+          else {
+            int check;
+            invar = 1;
+            check = checkvar(unitname, unitdef);
+            if (check==2){
+              readerror(errfile,
+                 "%s: environment variable %s not set at line %d of '%s'\n",
+                         progname, unitname, linenum, file);
+              wrongvar = 1;
+            }
+            else if (!(not^check))
+              wrongvar = 1;
+          }         
+          continue;
+        }
+        else if (!strcmp(unitname, "endvar")){
+          if (!invar)
+            readerror(errfile, 
+                      "%s: unmatched !endvar on line %d of '%s'\n",
+                      progname, linenum, file);
+          wrongvar = 0;
+          invar = 0;
+          continue;
+        }
+        else if (!strcmp(unitname,"locale")){ 
+          unitname = strtok(0, " "); 
+          if (!unitname)
+            readerror(errfile, 
+                      "%s: no locale specified on line %d of '%s'\n",
+                      progname, linenum, file);
+          else if (inlocale)
+            readerror(errfile,
+                      "%s: nested locales not allowed, line %d of '%s'\n",
+                      progname, linenum, file);
+          else {
+            inlocale = 1;
+            if (strcmp(unitname,mylocale))  /* locales don't match           */
+              wronglocale = 1;
+          }
+          continue;
+        } 
+        else if (!strcmp(unitname, "endlocale")){
+          if (!inlocale)
+            readerror(errfile, 
+                      "%s: unmatched !endlocale on line %d of '%s'\n",
+                      progname, linenum, file);
+          wronglocale = 0;
+          inlocale = 0;
+          continue;
+        }
+        else if (!strcmp(unitname, "utf8")){
+          if (in_utf8)
+            readerror(errfile,"%s: nested utf8 not allowed, line %d of '%s'\n",
+                              progname, linenum, file);
+          else in_utf8 = 1;
+          continue;
+        } 
+        else if (!strcmp(unitname, "endutf8")){
+          if (!in_utf8)
+            readerror(errfile,"%s: unmatched !endutf8 on line %d of '%s'\n",
+                      progname, linenum, file);
+          in_utf8 = 0;
+          continue;
+        }
+        if (in_utf8 && !utf8mode) continue;
+        if (wronglocale || wrongvar) continue;
+
+        if (!strcmp(unitname,"prompt")){
+          unitname = strtok(0,"");     /* Rest of the line */
+          if (promptprefix)
+            free(promptprefix);
+          if (!unitname)
+            promptprefix=0;
+          else
+            promptprefix = dupstr(unitname);
+          continue;
+        }
+        if (!strcmp(unitname,"message")){
+          unitname = strtok(0,"");     /* Rest of the line */
+          if (!flags.quiet){
+            if (unitname) logputs(unitname);
+            logputchar('\n');
+          }
+          continue; 
+        }
+        else if (!strcmp(unitname,"set")) {
+          unitname = strtok(0," ");
+          unitdef = strtok(0," ");
+          if (!unitname)
+            readerror(errfile, 
+                      "%s: no variable name specified on line %d of '%s'\n",
+                      progname, linenum, file);
+          else if (!unitdef)
+            readerror(errfile, 
+                      "%s: no value specified on line %d of '%s'\n",
+                      progname, linenum, file);
+          else 
+            setenv(unitname, unitdef, 0);
+          continue;
+        }
+        else if (!strcmp(unitname,"unitlist")){
+          splitline(0,&unitname, &unitdef); /* 0 continues strtok call */
+          if (!unitname || !unitdef)
+            readerror(errfile,VAGUE_ERR);
+          else {
+            if (newalias(unitname, unitdef, linenum, permfile, errfile))
+              goterr = 1;
+          }
+          continue;
+        }
+        else if (!strcmp(unitname, "include")){
+          if (depth>MAXINCLUDE){
+            readerror(errfile, 
+                "%s: max include depth of %d exceeded in file '%s' line %d\n",
+                progname, MAXINCLUDE, file, linenum);
+          } else {
+            int readerr;
+            char *includefile;
+
+            unitname = strtok(0, " "); 
+            if (!unitname){
+              readerror(errfile,
+                        "%s: missing include filename on line %d of '%s'\n",
+                        progname, linenum, file);
+              continue;
+            }
+            includefile = mymalloc(strlen(file)+strlen(unitname)+1, "(readunits)");
+            if (isfullpath(unitname))
+              strcpy(includefile,unitname);
+            else {
+              strcpy(includefile,file);
+              strcpy(pathend(includefile), unitname);
+            }
+
+            readerr = readunits(includefile, errfile, unitcount, prefixcount, 
+                                funccount, depth+1);
+            if (readerr == E_MEMORY){
+              fclose(unitfile);
+              free(line);
+              free(includefile);
+              return readerr;
+            }
+            if (readerr == E_FILE) {
+              readerror(errfile, "%s:   file was included at line %d of file '%s'\n", progname,linenum, file);
+            }
+            
+            if (readerr)
+              goterr = 1;
+            free(includefile);
+          }
+        } else                             /* not a valid command */
+          readerror(errfile,VAGUE_ERR);
+        continue;
+      } 
+      if (in_utf8 && !utf8mode) continue;
+      if (wronglocale || wrongvar) continue;
+      splitline(line, &unitname, &unitdef);
+      if (!unitname) continue;
+
+      if (*unitname == REDEFCHAR){
+        unitname++;
+        redefinition=1;
+        if (strlen(unitname)==0){
+          readerror(errfile,
+               "%s: expecting name of unit to redefine after '+' at line %d of '%s'\n",
+               progname, linenum,file);
+          continue;
+        }
+      } else
+        redefinition=0;
+      if (!strcmp(unitname,"-")) {
+          readerror(errfile,
+               "%s: expecting prefix name before '-' at line %d of '%s'\n",
+               progname, linenum,file);
+          continue;
+      }
+      if (!unitdef){
+        readerror(errfile,
+              "%s: unit '%s' lacks a definition at line %d of '%s'\n",
+                  progname, unitname, linenum, file);
+        continue;
+      }
+
+      if (lastchar(unitname) == '-'){      /* it's a prefix definition */
+        if (newprefix(unitname,unitdef,&locprefixcount,linenum,
+                      permfile,errfile,redefinition))
+          goterr=1;
+      }
+      else if (strchr(unitname,'[')){     /* table definition  */
+        retcode=newtable(unitname,unitdef,&locfunccount,linenum,
+                         permfile,errfile,redefinition);
+        if (retcode){
+          if (retcode != E_BADFILE){
+            fclose(unitfile);
+            free(line);
+            return retcode;
+          }
+          goterr=1;
+        }
+      }
+      else if (strchr(unitname,'(')){     /* function definition */
+        if (newfunction(unitname,unitdef,&locfunccount,linenum,
+                        permfile,errfile,redefinition))
+          goterr = 1;
+      }
+      else {                              /* ordinary unit definition */
+        if (newunit(unitname,unitdef,&locunitcount,linenum,permfile,errfile,redefinition,0))
+          goterr = 1;
+      }
+   }
+   fclose(unitfile);
+   free(line);
+   if (unitcount)
+     *unitcount+=locunitcount;
+   if (prefixcount)
+     *prefixcount+=locprefixcount;
+   if (funccount)
+     *funccount+=locfunccount;
+   if (goterr)
+     return E_BADFILE;
+   else return 0;
+}
+
+/* Initialize a unit to be equal to 1. */
+
+void
+initializeunit(struct unittype *theunit)
+{
+   theunit->factor = 1.0;
+   theunit->numerator[0] = theunit->denominator[0] = NULL;
+}
+
+
+/* Free a unit: frees all the strings used in the unit structure.
+   Does not free the unit structure itself.  */
+
+void
+freeunit(struct unittype *theunit)
+{
+   char **ptr;
+
+   for(ptr = theunit->numerator; *ptr; ptr++)
+     if (*ptr != NULLUNIT) free(*ptr);
+   for(ptr = theunit->denominator; *ptr; ptr++)
+     if (*ptr != NULLUNIT) free(*ptr);
+
+   /* protect against repeated calls to freeunit() */
+
+   theunit->numerator[0] = 0;  
+   theunit->denominator[0] = 0;
+}
+
+
+/* Print out a unit  */
+
+void
+showunit(struct unittype *theunit)
+{
+   char **ptr;
+   int printedslash;
+   int counter = 1;
+
+   logprintf(num_format.format, theunit->factor);
+
+   for (ptr = theunit->numerator; *ptr; ptr++) {
+      if (ptr > theunit->numerator && **ptr &&
+          !strcmp(*ptr, *(ptr - 1)))
+         counter++;
+      else {
+         if (counter > 1)
+           logprintf("%s%d", powerstring, counter);
+         if (**ptr)
+           logprintf(" %s", *ptr);
+         counter = 1;
+      }
+   }
+   if (counter > 1)
+     logprintf("%s%d", powerstring, counter);
+   counter = 1;
+   printedslash = 0;
+   for (ptr = theunit->denominator; *ptr; ptr++) {
+      if (ptr > theunit->denominator && **ptr &&
+          !strcmp(*ptr, *(ptr - 1)))
+         counter++;
+      else {
+         if (counter > 1)
+           logprintf("%s%d", powerstring, counter);
+         if (**ptr) {
+            if (!printedslash)
+              logprintf(" /");
+            printedslash = 1;
+            logprintf(" %s", *ptr);
+         }
+         counter = 1;
+      }
+   }
+   if (counter > 1)
+     logprintf("%s%d", powerstring, counter);
+}
+
+
+/* qsort comparison function */
+
+int
+compare(const void *item1, const void *item2)
+{
+   return strcmp(*(char **) item1, *(char **) item2);
+}
+
+/* Sort numerator and denominator of a unit so we can compare different
+   units */
+
+void
+sortunit(struct unittype *theunit)
+{
+   char **ptr;
+   int count;
+
+   for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
+   qsort(theunit->numerator, count, sizeof(char *), compare);
+   for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
+   qsort(theunit->denominator, count, sizeof(char *), compare);
+}
+
+
+/* Cancels duplicate units that appear in the numerator and
+   denominator.  The input unit must be sorted. */
+
+void
+cancelunit(struct unittype *theunit)
+{
+   char **den, **num;
+   int comp;
+
+   den = theunit->denominator;
+   num = theunit->numerator;
+
+   while (*num && *den) { 
+      comp = strcmp(*den, *num);
+      if (!comp) { /* units match, so cancel them */
+         if (*den!=NULLUNIT) free(*den);
+         if (*num!=NULLUNIT) free(*num);  
+         *den++ = NULLUNIT;
+         *num++ = NULLUNIT;
+      } else if (comp < 0) /* Move up whichever pointer is alphabetically */
+         den++;            /* behind to look for future matches */
+      else
+         num++;
+   }
+}
+
+
+/*
+   Looks up the definition for the specified unit including prefix processing
+   and plural removal.
+
+   Returns a pointer to the definition or a null pointer
+   if the specified unit does not appear in the units table.
+
+   Sometimes the returned pointer will be a pointer to the special
+   buffer created to hold the data.  This buffer grows as needed during 
+   program execution.  
+
+   Note that if you pass the output of lookupunit() back into the function
+   again you will get correct results, but the data you passed in may get
+   clobbered if it happened to be the internal buffer.  
+*/
+
+static int bufsize=0;
+static char *buffer;  /* buffer for lookupunit answers with prefixes */
+
+
+/* 
+  Plural rules for english: add -s
+  after x, sh, ch, ss   add -es
+  -y becomes -ies except after a vowel when you just add -s as usual
+*/
+  
+
+char *
+lookupunit(char *unit,int prefixok)
+{
+   char *copy;
+   struct prefixlist *pfxptr;
+   struct unitlist *uptr;
+
+   if ((uptr = ulookup(unit)))
+      return uptr->value;
+
+   if (strwidth(unit)>2 && lastchar(unit) == 's') {
+      copy = dupstr(unit);
+      lastchar(copy) = 0;
+      if (lookupunit(copy,prefixok)){
+         while(strlen(copy)+1 > bufsize) {
+            growbuffer(&buffer, &bufsize);
+         }
+         strcpy(buffer, copy);  /* Note: returning looked up result seems   */
+         free(copy);            /*   better but it causes problems when it  */
+         return buffer;         /*   contains PRIMITIVECHAR.                */
+      }
+      if (strlen(copy)>2 && lastchar(copy) == 'e') {
+         lastchar(copy) = 0;
+         if (lookupunit(copy,prefixok)){
+            while (strlen(copy)+1 > bufsize) {
+               growbuffer(&buffer,&bufsize);
+            }
+            strcpy(buffer,copy);
+            free(copy);
+            return buffer;
+         }
+      }
+      if (strlen(copy)>2 && lastchar(copy) == 'i') {
+         lastchar(copy) = 'y';
+         if (lookupunit(copy,prefixok)){
+            while (strlen(copy)+1 > bufsize) {
+               growbuffer(&buffer,&bufsize);
+            }
+            strcpy(buffer,copy);
+            free(copy);
+            return buffer;
+         }
+      }
+      free(copy);
+   }
+   if (prefixok && (pfxptr = plookup(unit))) {
+      copy = unit + pfxptr->len;
+      if (emptystr(copy) || lookupunit(copy,0)) {
+         char *tempbuf;
+         while (strlen(pfxptr->value)+strlen(copy)+2 > bufsize){
+            growbuffer(&buffer, &bufsize);
+         }
+         tempbuf = dupstr(copy);   /* copy might point into buffer */
+         strcpy(buffer, pfxptr->value);
+         strcat(buffer, " ");
+         strcat(buffer, tempbuf);
+         free(tempbuf);
+         return buffer;
+      }
+   }
+   return 0;
+}
+
+
+/* Points entries of product[] to the strings stored in tomove[].  
+   Leaves tomove pointing to a list of NULLUNITS.  */
+
+int
+moveproduct(char *product[], char *tomove[])
+{
+   char **dest, **src;
+
+   dest=product;
+   for(src = tomove; *src; src++){
+     if (*src == NULLUNIT) continue;
+     for(; *dest && *dest != NULLUNIT; dest++);
+     if (dest - product >= MAXSUBUNITS - 1) {
+       return E_PRODOVERFLOW;
+     }
+     if (!*dest)
+        *(dest + 1) = 0;
+     *dest = *src;
+     *src=NULLUNIT;
+   }
+   return 0;
+}
+
+/* 
+   Make a copy of a product list.  Note that no error checking is done
+   for overflowing the product list because it is assumed that the
+   source list doesn't overflow, so the destination list shouldn't
+   overflow either.  (This assumption could be false if the
+   destination is not actually at the start of a product buffer.) 
+*/
+
+void
+copyproduct(char **dest, char **source)
+{
+   for(;*source;source++,dest++) {
+     if (*source==NULLUNIT)
+       *dest = NULLUNIT;
+     else
+       *dest=dupstr(*source);
+   }
+   *dest=0;
+}
+
+/* Make a copy of a unit */ 
+
+void
+unitcopy(struct unittype *dest, struct unittype *source)
+{
+  dest->factor = source->factor;
+  copyproduct(dest->numerator, source->numerator);
+  copyproduct(dest->denominator, source->denominator);
+}
+
+
+/* Multiply left by right.  In the process, all of the units are
+   deleted from right (but it is not freed) */
+
+int 
+multunit(struct unittype *left, struct unittype *right)
+{
+  int myerr;
+  left->factor *= right->factor;
+  myerr = moveproduct(left->numerator, right->numerator);
+  if (!myerr)
+    myerr = moveproduct(left->denominator, right->denominator);
+  return myerr;
+}
+
+int 
+divunit(struct unittype *left, struct unittype *right)
+{
+  int myerr;
+  left->factor /= right->factor;
+  myerr = moveproduct(left->numerator, right->denominator);
+  if (!myerr)
+    myerr = moveproduct(left->denominator, right->numerator);
+  return myerr;
+}
+
+
+/*
+   reduces a product of symbolic units to primitive units.
+   The three low bits are used to return flags:
+
+   bit 0 set if reductions were performed without error.
+   bit 1 set if no reductions are performed.
+   bit 2 set if an unknown unit is discovered.
+
+   Return values from multiple calls will be ORed together later.
+ */
+
+#define DIDREDUCTION    (1<<0)
+#define NOREDUCTION     (1<<1)
+#define REDUCTIONERROR  (1<<2)
+#define CIRCULARDEF     (1<<3)
+
+int
+reduceproduct(struct unittype *theunit, int flip)
+{
+
+   char *toadd;
+   char **product;
+   int didsomething = NOREDUCTION;
+   struct unittype newunit;
+   int ret;
+   int itcount=0;    /* Count iterations to catch infinite loops */
+   
+   if (flip)
+      product = theunit->denominator;
+   else
+      product = theunit->numerator;
+
+   for (; *product; product++) {
+      for (;;) {
+         if (!strlen(*product))
+            break;
+
+         /* check for infinite loop */
+         itcount++;
+         if (itcount>MAXPRODUCTREDUCTIONS) 
+           return CIRCULARDEF;
+         
+         toadd = lookupunit(*product,1);
+         if (!toadd) {
+            if (!irreducible)
+              irreducible = dupstr(*product);
+            return REDUCTIONERROR;
+         }
+         if (strchr(toadd, PRIMITIVECHAR))
+            break;
+         didsomething = DIDREDUCTION;
+         if (*product != NULLUNIT) {
+            free(*product);
+            *product = NULLUNIT;
+         }
+         if (parseunit(&newunit, toadd, 0, 0))
+            return REDUCTIONERROR;
+         if (flip) ret=divunit(theunit,&newunit);
+         else ret=multunit(theunit,&newunit);
+         freeunit(&newunit);
+         if (ret) 
+            return REDUCTIONERROR;
+      }
+   }
+   return didsomething;
+}
+
+
+#if 0
+void showunitdetail(struct unittype *foo)
+{
+  char **ptr;
+
+  printf("%.17g ", foo->factor);
+
+  for(ptr=foo->numerator;*ptr;ptr++)
+    if (*ptr==NULLUNIT) printf("NULL ");
+    else printf("`%s' ", *ptr);
+  printf(" / ");
+  for(ptr=foo->denominator;*ptr;ptr++)
+    if (*ptr==NULLUNIT) printf("NULL ");
+    else printf("`%s' ", *ptr);
+  putchar('\n');
+}
+#endif
+
+
+/*
+   Reduces numerator and denominator of the specified unit.
+   Returns 0 on success, or 1 on unknown unit error.
+ */
+
+int
+reduceunit(struct unittype *theunit)
+{
+   int ret;
+
+   if (irreducible)
+     free(irreducible);
+   irreducible=0;
+   ret = DIDREDUCTION;
+
+   /* Keep calling reduceproduct until it doesn't do anything */
+
+   while (ret & DIDREDUCTION) {
+      ret = reduceproduct(theunit, 0);
+      if (!(ret & REDUCTIONERROR))
+        ret |= reduceproduct(theunit, 1);
+      if (ret & REDUCTIONERROR){
+         if (irreducible) 
+           return E_UNKNOWNUNIT;
+         else
+           return E_REDUCE;
+      }
+      else if (ret & CIRCULARDEF)
+	return E_CIRCULARDEF;
+   }
+   return 0;
+}
+
+/* 
+   Returns one if the argument unit is defined in the data file as a   
+  dimensionless unit.  This is determined by comparing its definition to
+  the string NODIM.  
+*/
+
+int
+ignore_dimless(char *name)
+{
+  struct unitlist *ul;
+  if (!name) 
+    return 0;
+  ul = ulookup(name);
+  if (ul && !strcmp(ul->value, NODIM))
+    return 1;
+  return 0;
+}
+
+int 
+ignore_nothing(char *name)
+{
+  return 0;
+}
+
+
+int
+ignore_primitive(char *name)
+{
+  struct unitlist *ul;
+  if (!name) 
+    return 0;
+  ul = ulookup(name);
+  if (ul && strchr(ul->value, PRIMITIVECHAR))
+    return 1;
+  return 0;
+}
+
+
+/* 
+   Compare two product lists, return zero if they match and one if
+   they do not match.  They may contain embedded NULLUNITs which are
+   ignored in the comparison. Units defined as NODIM are also ignored
+   in the comparison.
+*/
+
+int
+compareproducts(char **one, char **two, int (*isdimless)(char *name))
+{
+   int oneblank, twoblank;
+   while (*one || *two) {
+      oneblank = (*one==NULLUNIT) || isdimless(*one);    
+      twoblank = (*two==NULLUNIT) || isdimless(*two);    
+      if (!*one && !twoblank)
+         return 1;
+      if (!*two && !oneblank)
+         return 1;
+      if (oneblank)
+         one++;
+      else if (twoblank)
+         two++;
+      else if (strcmp(*one, *two))
+         return 1;
+      else
+         one++, two++;
+   }
+   return 0;
+}
+
+
+/* Return zero if units are compatible, nonzero otherwise.  The units
+   must be reduced, sorted and canceled for this to work.  */
+
+int
+compareunits(struct unittype *first, struct unittype *second, 
+             int (*isdimless)(char *name))
+{
+   return
+      compareproducts(first->numerator, second->numerator, isdimless) ||
+      compareproducts(first->denominator, second->denominator, isdimless);
+}
+
+
+/* Reduce a unit as much as possible */
+
+int
+completereduce(struct unittype *unit)
+{
+   int err;
+
+   if ((err=reduceunit(unit)))
+     return err;
+   
+   sortunit(unit);
+   cancelunit(unit);
+   return 0;
+}
+
+
+/* Raise theunit to the specified power.  This function does not fill
+   in NULLUNIT gaps, which could be considered a deficiency. */
+
+int
+expunit(struct unittype *theunit, int  power)
+{
+  char **numptr, **denptr;
+  double thefactor;
+  int i, uind, denlen, numlen;
+
+  if (power==0){
+    freeunit(theunit);
+    initializeunit(theunit);
+    return 0;
+  }
+  numlen=0;
+  for(numptr=theunit->numerator;*numptr;numptr++) numlen++;
+  denlen=0;
+  for(denptr=theunit->denominator;*denptr;denptr++) denlen++;
+  thefactor=theunit->factor;
+  for(i=1;i<power;i++){
+    theunit->factor *= thefactor;
+    for(uind=0;uind<numlen;uind++){
+      if (theunit->numerator[uind]!=NULLUNIT){
+        if (numptr-theunit->numerator>=MAXSUBUNITS-1) {
+           *numptr=*denptr=0;
+           return E_PRODOVERFLOW;
+        }
+        *numptr++=dupstr(theunit->numerator[uind]);
+      }
+    }
+    for(uind=0;uind<denlen;uind++){
+      if (theunit->denominator[uind]!=NULLUNIT){
+        *denptr++=dupstr(theunit->denominator[uind]);
+        if (denptr-theunit->denominator>=MAXSUBUNITS-1) {
+          *numptr=*denptr=0;
+          return E_PRODOVERFLOW;
+        }
+      }   
+    }
+  }
+  *numptr=0;
+  *denptr=0;
+  return 0;
+}
+
+
+int
+unit2num(struct unittype *input)
+{
+  struct unittype one;
+  int err;
+
+  initializeunit(&one);
+  if ((err=completereduce(input)))
+    return err;
+  if (compareunits(input,&one,ignore_nothing))
+    return E_NOTANUMBER;
+  freeunit(input);
+  return 0;
+}
+
+
+int
+unitdimless(struct unittype *input)
+{
+  struct unittype one;
+  initializeunit(&one);
+  if (compareunits(input, &one, ignore_dimless))
+    return 0;
+  freeunit(input);  /* Eliminate dimensionless units from list */
+  return 1;
+}
+                       
+
+/* 
+   The unitroot function takes the nth root of an input unit which has
+   been completely reduced.  Returns 1 if the unit is not a power of n. 
+   Input data can contain NULLUNITs.  
+*/
+
+int 
+subunitroot(int n,char *current[], char *out[])
+{
+  char **ptr;
+  int count=0;
+
+  while(*current==NULLUNIT) current++;  /* skip past NULLUNIT entries */
+  ptr=current;
+  while(*ptr){
+    while(*ptr){
+      if (*ptr!=NULLUNIT){
+        if (strcmp(*current,*ptr)) break;
+        count++;
+      }
+      ptr++;
+    }
+    if (count % n != 0){  /* If not dimensionless generate error, otherwise */
+      if (!ignore_dimless(*current))    /* just skip over it */
+        return E_NOTROOT;
+    } else {
+      for(count /= n;count>0;count--) *(out++) = dupstr(*current);
+    }
+    current=ptr;
+  }
+  *out = 0;
+  return 0;
+}
+  
+
+int 
+rootunit(struct unittype *inunit,int n)
+{
+   struct unittype outunit;
+   int err;
+
+   initializeunit(&outunit);
+   if ((err=completereduce(inunit)))
+     return err;
+   /* Roots of negative numbers fail in pow(), even odd roots */
+   if (inunit->factor < 0)
+     return E_NOTROOT;
+   outunit.factor = pow(inunit->factor,1.0/(double)n);
+   if ((err = subunitroot(n, inunit->numerator, outunit.numerator)))
+     return err;
+   if ((err = subunitroot(n, inunit->denominator, outunit.denominator)))
+     return err;
+   freeunit(inunit);
+   initializeunit(inunit);
+   return multunit(inunit,&outunit);
+}
+
+
+/* Compute the inverse of a unit (1/theunit) */
+
+void
+invertunit(struct unittype *theunit)
+{
+  char **ptr, *swap;
+  int numlen, length, ind;
+
+  theunit->factor = 1.0/theunit->factor;  
+  length=numlen=0;
+  for(ptr=theunit->denominator;*ptr;ptr++,length++);
+  for(ptr=theunit->numerator;*ptr;ptr++,numlen++);
+  if (numlen>length)
+    length=numlen;
+  for(ind=0;ind<=length;ind++){
+    swap = theunit->numerator[ind];
+    theunit->numerator[ind] = theunit->denominator[ind];
+    theunit->denominator[ind] = swap;
+  }
+}
+
+
+int
+float2rat(double y, int *p, int *q)
+{
+  int coef[20];           /* How long does this buffer need to be? */
+  int i,termcount,saveq;
+  double fracpart,x;
+
+  /* Compute continued fraction approximation */
+
+  x=y;
+  termcount=0;
+  while(1){
+    coef[termcount] = (int) floor(x);
+    fracpart = x-coef[termcount];
+    if (fracpart < .001 || termcount==19) break;
+    x = 1/fracpart;
+    termcount++;
+  }
+
+  /* Compress continued fraction into rational p/q */
+ 
+  *p=0;
+  *q=1;
+  for(i=termcount;i>=1;i--) {
+    saveq=*q;
+    *q = coef[i] * *q + *p;
+    *p = saveq;
+  }
+  *p+=*q*coef[0];
+  return *q<MAXSUBUNITS && fabs((double)*p / (double)*q - y) < DBL_EPSILON;
+}
+
+
+/* Raise a unit to a power */
+
+int
+unitpower(struct unittype *base, struct unittype *exponent)
+{
+  int errcode, p, q;
+
+  errcode = unit2num(exponent);
+  if (errcode == E_NOTANUMBER)
+    return E_DIMEXPONENT;
+  if (errcode) 
+    return errcode;
+  errcode = unit2num(base);
+  if (!errcode){                     /* Exponent base is dimensionless */
+    base->factor = pow(base->factor,exponent->factor);
+    if (errno)
+      return E_FUNC;
+  }
+  else if (errcode==E_NOTANUMBER) {          /* Base not dimensionless */
+    if (!float2rat(exponent->factor,&p,&q)){ /* Exponent must be rational */
+      if (unitdimless(base)){
+        base->factor = pow(base->factor,exponent->factor);
+        if (errno)
+          return E_FUNC;
+      }
+      else
+        return E_IRRATIONAL_EXPONENT;
+    } else {
+      if (q!=1) {
+        errcode = rootunit(base, q);
+        if (errcode == E_NOTROOT) 
+          return E_BASE_NOTROOT;
+        if (errcode)
+          return errcode;
+      }
+      errcode = expunit(base, abs(p));
+      if (errcode)
+        return errcode;
+      if (p<0)
+        invertunit(base);
+    }
+  } 
+  else return errcode;
+  return 0;
+}
+
+
+/* Old units program would give message about what each operand
+   reduced to, showing that they weren't conformable.  Can this
+   be achieved here?  */
+
+int
+addunit(struct unittype *unita, struct unittype *unitb)
+{
+  int err;
+
+  if ((err=completereduce(unita)))
+    return err;
+  if ((err=completereduce(unitb)))
+    return err;
+  if (compareunits(unita,unitb,ignore_nothing))
+    return E_BADSUM;
+  unita->factor += unitb->factor;
+  freeunit(unitb);
+  return 0;
+}
+
+
+double
+linearinterp(double  a, double b, double aval, double bval, double c)
+{
+  double lambda;
+
+  lambda = (b-c)/(b-a);
+  return lambda*aval + (1-lambda)*bval;
+}
+
+
+/* evaluate a user function */
+
+#define INVERSE 1
+#define FUNCTION 0
+#define ALLERR 1
+#define NORMALERR 0
+
+int
+evalfunc(struct unittype *theunit, struct func *infunc, int inverse, 
+         int allerrors)
+{
+   struct unittype result;
+   struct functype *thefunc;
+   int err;
+   double value;
+   int foundit, count;
+   struct unittype *save_value;
+   char *save_function;
+
+   if (infunc->table) {  /* Tables are short, so use dumb search algorithm */
+     err = parseunit(&result, infunc->tableunit, 0, 0);
+     if (err)
+       return E_BADFUNCDIMEN;
+     if (inverse){
+       err = divunit(theunit, &result);
+       if (err)
+         return err;
+       err = unit2num(theunit);
+       if (err==E_NOTANUMBER)
+         return E_BADFUNCARG;
+       if (err)
+         return err;
+       value = theunit->factor;
+       foundit=0;
+       for(count=0;count<infunc->tablelen-1;count++)
+         if ((infunc->table[count].value<=value &&
+              value<=infunc->table[count+1].value) ||
+             (infunc->table[count+1].value<=value &&
+              value<=infunc->table[count].value)){
+           foundit=1;
+           value  = linearinterp(infunc->table[count].value,
+                                 infunc->table[count+1].value,
+                                 infunc->table[count].location,
+                                 infunc->table[count+1].location,
+                                 value);
+           break;
+         }
+       if (!foundit)
+         return E_NOTINDOMAIN;
+       freeunit(&result);
+       freeunit(theunit);
+       theunit->factor = value;
+       return 0;
+     } else {
+       err=unit2num(theunit);
+       if (err)
+         return err;
+       value=theunit->factor;
+       foundit=0;
+       for(count=0;count<infunc->tablelen-1;count++)
+         if (infunc->table[count].location<=value &&
+             value<=infunc->table[count+1].location){
+           foundit=1;
+           value =  linearinterp(infunc->table[count].location,
+                        infunc->table[count+1].location,
+                        infunc->table[count].value,
+                        infunc->table[count+1].value,
+                        value);
+           break;
+         } 
+       if (!foundit)
+         return E_NOTINDOMAIN;
+       result.factor *= value;
+     }
+   } else {  /* it's a function */
+     if (inverse){
+       thefunc=&(infunc->inverse);
+       if (!thefunc->def)
+         return E_NOINVERSE;
+     }
+     else
+       thefunc=&(infunc->forward);
+     err = completereduce(theunit);
+     if (err)
+       return err;
+     if (thefunc->dimen){
+       err = parseunit(&result, thefunc->dimen, 0, 0);
+       if (err)
+         return E_BADFUNCDIMEN;
+       err = completereduce(&result);
+       if (err)
+         return E_BADFUNCDIMEN;
+       if (compareunits(&result, theunit, ignore_nothing))
+         return E_BADFUNCARG;
+       value = theunit->factor/result.factor;
+     } else 
+       value = theunit->factor;
+     if (thefunc->domain_max && 
+         (value > *thefunc->domain_max || 
+          (thefunc->domain_max_open && value == *thefunc->domain_max)))
+       return E_NOTINDOMAIN;
+     if (thefunc->domain_min && 
+         (value < *thefunc->domain_min ||
+          (thefunc->domain_min_open && value == *thefunc->domain_min)))
+       return E_NOTINDOMAIN;
+     save_value = parameter_value;
+     save_function = function_parameter;
+     parameter_value = theunit;
+     function_parameter = thefunc->param;
+     err = parseunit(&result, thefunc->def, 0,0);
+     function_parameter = save_function;
+     parameter_value = save_value;
+     if (err && (allerrors == ALLERR || err==E_PARSEMEM || err==E_PRODOVERFLOW 
+                 || err==E_NOTROOT || err==E_BADFUNCTYPE))
+       return err;
+     if (err)
+       return E_FUNARGDEF;
+   }
+   freeunit(theunit);
+   initializeunit(theunit);
+   multunit(theunit, &result);
+   return 0;
+}
+
+
+/*
+    append a formatted string to a buffer; first character of buffer
+    should be '\0' on first call
+*/
+size_t
+vbufprintf(char **buf, size_t *bufsize, char *fmt, ...)
+{
+    va_list args;
+    size_t
+	oldlen,			/* length of current buffer contents */
+	newlen,			/* length of string to be added */
+	buflen;			/* length of new buffer contents */
+    double growfactor = 1.5;
+    static char *newbuf = NULL;
+    char *oldbuf;
+
+    oldlen = strlen(*buf);
+    oldbuf = dupstr(*buf);
+
+    /* get length of formatted string to be appended to buffer */
+    va_start(args, fmt);
+    newlen = vsnprintf(NULL, 0, fmt, args);
+    va_end(args);
+
+    /* allocate a buffer for the new string */
+    newbuf = (char *) malloc(newlen + 1);
+    if (newbuf == NULL) {
+	fprintf(stderr, "%s (bufprintf): memory allocation error\n", progname);  
+	exit(EXIT_FAILURE); 
+    }
+
+    /* expand main buffer if necessary */
+    if (*bufsize < oldlen + newlen + 1) {
+	*bufsize = (size_t) ((oldlen + newlen) * growfactor + 1);
+
+	*buf = (char *) realloc(*buf, *bufsize);
+	if (*buf == NULL) {
+	    fprintf(stderr, "%s (bufprintf): memory allocation error\n", progname);  
+	    exit(EXIT_FAILURE); 
+	}
+    }
+
+    /* generate the new formatted string */
+    va_start(args, fmt);
+    newlen = vsprintf(newbuf, fmt, args);
+    va_end(args);
+
+    /* copy old and new strings to buffer */
+    strcpy(*buf, oldbuf);
+    strcat(*buf, newbuf);
+    buflen = strlen(*buf);
+
+    free(oldbuf);
+    free(newbuf);
+
+    return buflen;
+}
+
+
+/*
+    similar to showunit(), but it saves the string in a buffer rather
+    than sending it to an output stream
+*/
+char *
+buildunitstr(struct unittype *theunit)
+{
+    char **ptr;
+    char *buf;
+    int printedslash;
+    int counter = 1;
+    size_t bufsize, newlen;
+
+    bufsize = 256;
+    buf = (char *) mymalloc(bufsize, "(buildunitstr)");
+    *buf = '\0';
+
+    /* factor */
+    newlen = vbufprintf(&buf, &bufsize, num_format.format, theunit->factor);
+
+    /* numerator */
+    for (ptr = theunit->numerator; *ptr; ptr++) {
+	if (ptr > theunit->numerator && **ptr &&
+	  !strcmp(*ptr, *(ptr - 1)))
+	    counter++;
+	else {
+	    if (counter > 1)
+		newlen = vbufprintf(&buf, &bufsize, "%s%d", powerstring, counter);
+		if (**ptr)
+		    newlen = vbufprintf(&buf, &bufsize, " %s", *ptr);
+		counter = 1;
+	}
+    }
+    if (counter > 1)
+	newlen = vbufprintf(&buf, &bufsize, "%s%d", powerstring, counter);
+    counter = 1;
+    printedslash = 0;
+
+    /* denominator */
+    for (ptr = theunit->denominator; *ptr; ptr++) {
+	if (ptr > theunit->denominator && **ptr &&
+          !strcmp(*ptr, *(ptr - 1)))
+	    counter++;
+	else {
+	    if (counter > 1)
+		newlen = vbufprintf(&buf, &bufsize, "%s%d", powerstring, counter);
+		if (**ptr) {
+		    if (!printedslash)
+			newlen = vbufprintf(&buf, &bufsize, " /");
+		    printedslash = 1;
+		    newlen = vbufprintf(&buf, &bufsize, " %s", *ptr);
+		}
+	    counter = 1;
+	}
+    }
+    if (counter > 1)
+	newlen = vbufprintf(&buf, &bufsize, "%s%d", powerstring, counter);
+
+   return buf;
+}
+
+/* 
+   If the given character string has only one unit name in it, then print out
+   the rule for that unit.  In any case, print out the reduced form for
+   the unit.
+*/
+
+void
+showdefinition(char *unitstr, struct unittype *theunit)
+{
+  size_t bufsize = 256;
+  char *buf, *showstr;
+
+  logputs(deftext);
+  showstr = buildunitstr(theunit);
+  buf = (char *) mymalloc(bufsize, "(showdefinition)");
+
+  while((unitstr = lookupunit(unitstr,1))
+        && strspn(unitstr,digits) != strlen(unitstr)
+        && !strchr(unitstr,PRIMITIVECHAR)) {
+    if (strlen(unitstr) > bufsize - 1) {
+	bufsize = strlen(unitstr) + 1;
+	buf = realloc(buf, bufsize);
+    }
+    tightbufprint(buf, unitstr);
+    logputs(buf);
+    if (strcmp(buf, showstr))
+	logputs(" = ");
+  } 
+
+  if (strcmp(buf, showstr))
+      logputs(showstr);
+  logputchar('\n');
+  free(buf);
+  free(showstr);
+}
+
+
+void 
+showfunction(struct functype *func)
+{
+  struct unittype unit;
+  int not_dimensionless, i;
+
+  if (!func->def) {
+    logputs(" is undefined");
+    return;
+  }
+
+  if (func->dimen){                       /* coverity[check_return] */
+    parseunit(&unit,func->dimen,0,0);     /* unit2num returns 0 for */
+    not_dimensionless = unit2num(&unit);  /* dimensionless units    */
+  } 
+    
+  logprintf("(%s) = %s", func->param, func->def);
+  if (func->domain_min || func->domain_max){
+    logputchar('\n');
+    for(i=strwidth(deftext);i;i--) logputchar(' ');
+    logputs("defined for ");
+    if (func->domain_min && func->domain_max) {
+      logprintf(num_format.format, *func->domain_min);
+      if (func->dimen && (not_dimensionless || unit.factor != 1)){
+        if (isdecimal(*func->dimen))
+          logputs(" *");
+        logprintf(" %s",func->dimen);
+      }
+      logputs(func->domain_min_open?" < ":" <= ");
+    }
+    logputs(func->param);
+    if (func->domain_max){
+      logputs(func->domain_max_open?" < ":" <= ");
+      logprintf(num_format.format, *func->domain_max);
+    }
+    else {
+      logputs(func->domain_min_open?" > ":" >= ");
+      logprintf(num_format.format, *func->domain_min);
+    }
+    if (func->dimen && (not_dimensionless || unit.factor != 1)){
+      if (isdecimal(*func->dimen))
+        logputs(" *");
+      logprintf(" %s",func->dimen);
+    }
+    if (!func->dimen) logputs(" (any units)");
+  } else if (func->dimen){
+    logputchar('\n');
+    for(i=strwidth(deftext);i;i--) logputchar(' ');
+    if (not_dimensionless) 
+      logprintf("%s has units %s",func->param, func->dimen);
+    else
+      logprintf("%s is dimensionless",func->param);
+  }    
+  logputchar('\n');
+}
+
+void
+showtable(struct func *fun, int inverse)
+{
+  int i;
+
+  logprintf("%sinterpolated table with points\n",deftext);
+  if (inverse){
+    int reverse, j;
+    reverse = (fun->table[0].value > fun->table[fun->tablelen-1].value);
+    for(i=0;i<fun->tablelen;i++){
+      if (reverse) j = fun->tablelen-i-1;
+      else j=i;
+      if (flags.verbose>0)
+        logputs("\t\t    ");
+      logprintf("~%s(", fun->name);
+      logprintf(num_format.format, fun->table[j].value);
+      if (isdecimal(fun->tableunit[0]))
+        logputs(" *");
+      logprintf(" %s",fun->tableunit);
+      logputs(") = ");
+      logprintf(num_format.format, fun->table[j].location);
+      logputchar('\n');
+    }
+  } else {
+    for(i=0;i<fun->tablelen;i++){
+      if (flags.verbose>0)
+        logputs("\t\t    ");
+      logprintf("%s(", fun->name);
+      logprintf(num_format.format, fun->table[i].location);
+      logputs(") = ");
+      logprintf(num_format.format, fun->table[i].value);
+      if (isdecimal(fun->tableunit[0]))
+        logputs(" *");
+      logprintf(" %s\n",fun->tableunit);
+    }
+  }
+}
+
+
+void
+showfuncdefinition(struct func *fun, int inverse)
+{
+  if (fun->table)  /* It's a table */
+    showtable(fun, inverse);
+  else { 
+    logprintf("%s%s%s", deftext,inverse?"~":"", fun->name);
+    if (inverse)
+      showfunction(&fun->inverse);
+    else
+      showfunction(&fun->forward);
+  }
+}
+
+
+void
+showunitlistdef(struct wantalias *alias)
+{
+  logprintf("%sunit list, ",deftext);
+  tightprint(stdout,alias->definition);
+  if (logfile) tightprint(logfile,alias->definition);
+  logputchar('\n');
+}
+
+
+/* Show conversion to a function.  Input unit 'have' is replaced by the 
+   function inverse and completely reduced. */
+
+int
+showfunc(char *havestr, struct unittype *have, struct func *fun)
+{
+   int err;
+   char *dimen;
+
+   err = evalfunc(have, fun, INVERSE, NORMALERR);
+   if (!err)
+     err = completereduce(have);
+   if (err) {
+     if (err==E_BADFUNCARG){
+       logputs("conformability error");
+       if (fun->table)
+         dimen = fun->tableunit;
+       else if (fun->inverse.dimen)
+         dimen = fun->inverse.dimen;
+       else 
+         dimen = 0;
+       if (!dimen)
+         logputchar('\n');
+       else {
+         struct unittype want;
+         
+         if (emptystr(dimen))
+           dimen = "1";
+         logprintf(": conversion requires dimensions of '%s'\n",dimen);
+         if (flags.verbose==2) logprintf("\t%s = ",havestr);
+         else if (flags.verbose==1) logputchar('\t');
+         showunit(have);
+         if (flags.verbose==2) logprintf("\n\t%s = ",dimen);
+         else if (flags.verbose==1) logprintf("\n\t");
+         else logputchar('\n');            /* coverity[check_return] */
+         parseunit(&want, dimen, 0, 0);    /* coverity[check_return] */
+         completereduce(&want);         /* dimen was already checked for */
+         showunit(&want);               /* errors so no need to check here */
+         logputchar('\n');
+         }
+     } else if (err==E_NOTINDOMAIN)
+       logprintf("Value '%s' is not in the function's range\n",havestr);
+     else if (err==E_NOINVERSE)
+       logprintf("Inverse of the function '%s' is not defined\n",fun->name);
+     else
+       logputs("Function evaluation error (bad function definition)\n");
+     return 1;
+   }
+   if (flags.verbose==2)
+     logprintf("\t%s = %s(", havestr, fun->name);
+   else if (flags.verbose==1) 
+     logputchar('\t');
+   showunit(have);
+   if (flags.verbose==2)
+     logputchar(')');
+   logputchar('\n');
+   return 0;
+}
+
+/* Print the conformability error message */
+
+void
+showconformabilityerr(char *havestr,struct unittype *have,
+                      char *wantstr,struct unittype *want)
+{
+  logputs("conformability error\n");
+  if (flags.verbose==2)
+    logprintf("\t%s = ",havestr);
+  else if (flags.verbose==1)
+    logputchar('\t');
+  showunit(have);
+  if (flags.verbose==2)
+    logprintf("\n\t%s = ",wantstr);
+  else if (flags.verbose==1)
+    logputs("\n\t");
+  else
+    logputchar('\n');
+  showunit(want);
+  logputchar('\n');
+} /* end showconformabilityerr */
+
+
+
+/*
+  determine whether a unit string begins with a fraction; assume it
+  does if it starts with an integer, '|', and another integer
+*/
+int
+isfract(const char *unitstr)
+{
+  char *enddouble=0, *endlong=0;
+
+  while (isdigit(*unitstr))
+    unitstr++;
+  if (*unitstr++ == '|') {
+    (void)strtod(unitstr, &enddouble);
+    (void)strtol(unitstr, &endlong, 10);
+    if (enddouble == endlong)
+      return 1;
+  }
+  return 0;
+}
+
+int
+checksigdigits(char *arg)
+{
+  int errors, ival;
+  char *nonum;
+
+  errors = 0;
+
+  if (!strcmp(arg, "max"))
+    num_format.precision = MAXPRECISION;
+  else {
+    ival = (int) strtol(arg, &nonum, 10);
+    if (!emptystr(nonum)) {
+      fprintf(stderr,
+      "%s: invalid significant digits (%s)--integer value or 'max' required\n",
+              progname, arg);
+      errors++;
+    }
+    else if (ival <= 0) {
+      fprintf(stderr, "%s: number of significant digits must be positive\n",
+              progname);
+      errors++;
+    }
+    else if (ival > MAXPRECISION) {
+      fprintf(stderr,
+           "%s: too many significant digits (%d)--using maximum value (%d)\n",
+           progname, ival, MAXPRECISION);
+      num_format.precision = MAXPRECISION;
+    }
+    else
+      num_format.precision = ival;
+  }
+  if (errors)
+    return -1;
+  else
+    return 0;
+}
+
+/* set output number format specification from significant digits and type */
+
+int
+setnumformat()
+{
+  size_t len;
+
+  if (strchr("Ee", num_format.type))
+    num_format.precision--;
+
+  len = 4;      /* %, decimal point, type, terminating NUL */
+  if (num_format.precision > 0)
+    len += (size_t) floor(log10((double) num_format.precision))+1;
+  num_format.format = (char *) mymalloc(len, "(setnumformat)");
+  sprintf(num_format.format, "%%.%d%c", num_format.precision, num_format.type);
+  return 0;
+}
+
+/*
+    parse and validate the output number format specification and
+    extract its type and precision into the num_format structure.
+    Returns nonzero for invalid format.
+*/
+
+int
+parsenumformat()
+{
+  static char *format_types = NULL;
+  static char *format_flags = "+-# 0'";
+  static char badflag;
+  char *two = "0x1p+1";
+  char *valid="ABCDEFGHIJKLMNOPQRSTUVWXYXabcdefghijklmnopqrstuvwxyx.01234567890";
+  char *dotptr, *lptr, *nonum, *p;
+  char testbuf[80];
+  int errors, ndx;
+
+  if (format_types == NULL){
+    format_types = (char *) mymalloc(strlen(BASE_FORMATS)+4, "(parsenumformat)");
+    strcpy(format_types,BASE_FORMATS);
+
+    /* check for support of type 'F' (MS VS 2012 doesn't have it) */
+    sprintf(testbuf, "%.1F", 1.2);
+    if (strlen(testbuf) == 3 && testbuf[0] == '1' && testbuf[2] == '2')
+      strcat(format_types,"F");
+
+    /* check for support of hexadecimal floating point */
+    sprintf(testbuf, "%.0a", 2.0);
+    if (!strcmp(testbuf,two))
+      strcat(format_types, "aA");
+
+    /* check for support of digit-grouping (') flag */
+    sprintf(testbuf, "%'.0f", 1234.0);
+    if (strlen(testbuf) > 2 && testbuf[0] == '1' && testbuf[2] == '2')
+      badflag = '\0'; /* supported */
+    else
+      badflag = '\''; /* not supported */
+  }
+
+  errors = 0;
+
+  p = num_format.format;
+
+  if (*p != '%') {
+    fprintf(stderr, "%s: number format specification must start with '%%'\n",
+            progname);
+    errors++;
+  }
+  else if (strrchr(num_format.format, '%') != num_format.format) {
+    fprintf(stderr, "%s: only one '%%' allowed in number format specification\n",
+            progname);
+    errors++;
+    p++;
+  }
+  else
+    p++;
+
+  dotptr = strchr(num_format.format, '.');
+  if (dotptr && strrchr(num_format.format, '.') != dotptr) {
+    fprintf(stderr, "%s: only one '.' allowed in number format specification\n",
+            progname);
+    errors++;
+  }
+
+  /* skip over flags */
+  while (*p && strchr(format_flags, *p)) {
+    if (*p == badflag) {   /* only if digit-grouping flag (') not supported */
+      fprintf(stderr, "%s: digit-grouping flag (') not supported\n", progname);
+      errors++;
+    }
+    p++;
+  }
+
+  /* check for type length modifiers, which aren't allowed */
+  if ((lptr = strstr(num_format.format, "hh"))
+     || (lptr = strstr(num_format.format, "ll"))) {
+    fprintf(stderr, "%s: type length modifier (%.2s) not supported\n", progname, lptr);
+    errors++;
+  }
+  else if ((lptr = strpbrk(num_format.format, "hjLltz"))) {
+    fprintf(stderr, "%s: type length modifier (%c) not supported\n", progname, lptr[0]);
+    errors++;
+  }
+
+  /* check for other invalid characters */
+  ndx = strspn(p, valid);
+  if (ndx < strlen(p)) {
+    fprintf(stderr, "%s: invalid character (%c) in width, precision, or type\n",
+    progname, p[ndx]);
+    errors++;
+  }
+
+  if (errors) { /* results of any other checks are likely to be misleading */
+    fprintf(stderr, "%s: invalid number format specification (%s)\n", 
+                    progname, num_format.format);
+    fprintf(stderr, "%s: valid specification is %%[flags][width][.precision]type\n",
+            progname);
+    return -1;
+  }
+
+  /* get width and precision if specified; check precision */
+  num_format.width = (int) strtol(p, &nonum, 10);
+
+  if (*nonum == '.'){
+    if (isdigit(nonum[1]))
+      num_format.precision = (int) strtol(nonum+1, &nonum, 10);
+    else {
+      num_format.precision = 0;
+      nonum++;
+    }
+  }
+  else  /* precision not given--use printf() default */
+    num_format.precision = 6;
+
+  /* check for valid format type */
+  if (emptystr(nonum)) {
+    fprintf(stderr, "%s: missing format type\n", progname);
+    errors++;
+  }
+  else {
+    if (strchr(format_types, *nonum)) {
+      if (nonum[1]) {
+        fprintf(stderr, "%s: invalid character(s) (%s) after format type\n",
+                progname, nonum + 1);
+        errors++;
+      }
+      else 
+        num_format.type = *nonum;
+    }
+    else {
+      fprintf(stderr,
+              "%s: invalid format type (%c)--valid types are [%s]\n",
+              progname, *nonum, format_types);
+      errors++;
+    }
+  }
+
+  if (num_format.precision == 0 && 
+      (num_format.type == 'G' || num_format.type == 'g'))
+    num_format.precision = 1;
+
+  if (errors) {
+    fprintf(stderr, "%s: invalid number format specification (%s)\n", 
+                    progname, num_format.format);
+    fprintf(stderr, "%s: valid specification is %%[flags][width][.precision]type\n",
+            progname);
+    return -1;
+  }
+  else
+    return 0;
+}
+
+/*
+   round a number to the lesser of the displayed precision or the
+   remaining significant digits; indicate in hasnondigits if a number
+   will contain any character other than the digits 0-9 in the current
+   display format.
+*/
+
+double
+round_output(double value, int sigdigits, int *hasnondigits)
+{
+  int buflen;
+  char *buf;
+  double rounded;  
+  double mult_factor, rdigits;
+  int fmt_digits;       /* decimal significant digits in format */
+
+  if (!isfinite(value)){
+    if (hasnondigits)
+      *hasnondigits = 1;
+    return value;
+  }
+
+  fmt_digits = num_format.precision;
+  switch (num_format.type) {
+    case 'A':
+    case 'a':
+      sigdigits = round(sigdigits * log2(10) / 4);
+      fmt_digits++;
+      break;
+    case 'E':
+    case 'e':
+      fmt_digits++;
+      break;
+    case 'F':
+    case 'f':
+      if (fabs(value) > 0)
+        fmt_digits += ceil(log10(fabs(value)));
+      break;
+  }
+
+  if (sigdigits < fmt_digits)
+    rdigits = sigdigits;
+  else
+    rdigits = fmt_digits;
+
+  /* place all significant digits to the right of the radix */
+  if (value != 0)
+    rdigits -= ceil(log10(fabs(value)));
+  /* then handle like rounding to specified decimal places */
+  mult_factor = pow(10.0, rdigits);
+  rounded = round(value * mult_factor) / mult_factor;
+
+  /* allow for sign (1), radix (1), exponent (5), E or E formats (1), NUL */
+  buflen = num_format.precision + 9;
+
+  if (num_format.width > buflen)
+    buflen = num_format.width;
+
+  if (strchr("Ff", num_format.type)) {
+    int len=num_format.precision+2;
+    if (fabs(value) > 1.0)
+      len += (int) floor(log10(fabs(value))) + 1;
+    if (len > buflen)
+      buflen = len;
+  }
+
+  /* allocate space for thousands separators with digit-grouping (') flag */
+  /* assume worst case--that all groups are two digits */
+  if (strchr(num_format.format, '\'') && strchr("FfGg", num_format.type)) 
+    buflen = buflen*3/2;
+
+  buf = (char *) mymalloc(buflen, "(round_output)");
+  sprintf(buf, num_format.format, value);
+
+  if (hasnondigits){
+    if (strspn(buf, "1234567890") != strlen(buf))
+      *hasnondigits = 1;
+    else
+      *hasnondigits = 0;
+  }
+
+  free(buf);
+  return rounded;
+}
+
+/* 
+   Determine significant digits in remainder relative to an original
+   value which is assumed to have full double precision.  Returns
+   the number of binary or decimal digits depending on the value
+   of base, which must be 2 or 10. 
+*/
+
+int
+getsigdigits(double original, double remainder, int base)
+{
+  int sigdigits;
+  double maxdigits;
+  double (*logfunc)(double);
+
+  if (base == 2) {
+    maxdigits = DBL_MANT_DIG;
+    logfunc = log2;
+  }
+  else {
+    maxdigits = DBL_MANT_DIG * log10(2.0);
+    logfunc = log10;
+  }
+
+  if (original == 0)
+    return floor(maxdigits);
+  else if (remainder == 0)
+    return 0;
+
+  sigdigits = floor(maxdigits - logfunc(fabs(original/remainder)));
+
+  if (sigdigits < 0)
+    sigdigits = 0;
+
+  return sigdigits;
+}
+
+/* Rounds a double to the specified number of binary or decimal 
+   digits.  The base argument must be 2 or 10.  */
+
+double
+round_digits(double value, int digits, int base)
+{
+  double mult_factor;
+  double (*logfunc)(double);
+
+  if (digits == 0)
+    return 0.0;
+
+  if (base == 2)
+    logfunc = log2;
+  else
+    logfunc = log10;
+
+  if (value != 0)
+    digits -= ceil(logfunc(fabs(value)));
+
+  mult_factor = pow((double) base, digits);
+
+  return round(value*mult_factor)/mult_factor;
+}
+
+
+/* Returns true if the value will display as equal to the reference 
+   and if hasnondigits is non-null then return true if the displayed
+   output contains any character in "0123456789".  */
+
+int 
+displays_as(double reference, double value, int *hasnondigits)
+{
+  int buflen;
+  char *buf;
+  double rounded;  
+
+  if (!isfinite(value)){
+    if (hasnondigits)
+      *hasnondigits = 1;
+    return 0;
+  }
+  
+  /* allow for sign (1), radix (1), exponent (5), E or E formats (1), NUL */
+  buflen = num_format.precision + 9;
+
+  if (num_format.width > buflen)
+    buflen = num_format.width;
+
+  if (strchr("Ff", num_format.type)) {
+    int len=num_format.precision+2;
+    if (fabs(value) > 1.0)
+      len += (int) floor(log10(fabs(value))) + 1;
+    if (len > buflen)
+      buflen = len;
+  }
+
+  /* allocate space for thousands separators with digit-grouping (') flag */
+  /* assume worst case--that all groups are two digits */
+  if (strchr(num_format.format, '\'') && strchr("FfGg", num_format.type)) 
+    buflen = buflen*3/2;
+
+  buf = (char *) mymalloc(buflen, "(round_to_displayed)");
+  sprintf(buf, num_format.format, value);
+
+  if (hasnondigits){
+    if (strspn(buf, "1234567890") != strlen(buf))
+      *hasnondigits = 1;
+    else
+      *hasnondigits = 0;
+  }
+  rounded = strtod(buf, NULL);
+  free(buf);
+  
+  return rounded==reference;
+}
+
+
+
+/* Print the unit in 'unitstr' along with any necessary punctuation.
+   The 'value' is the multiplier for the unit.  If printnum is set
+   to PRINTNUM then this value is printed, or set it to NOPRINTNUM
+   to prevent the value from being printed. 
+*/
+
+#define PRINTNUM 1
+#define NOPRINTNUM 0
+
+void
+showunitname(double value, char *unitstr, int printnum)
+{
+  int hasnondigits;     /* flag to indicate nondigits in displayed value */
+  int is_one;           /* Does the value display as 1? */
+
+  is_one = displays_as(1, value, &hasnondigits);
+
+  if (printnum && !(is_one && isdecimal(*unitstr)))
+    logprintf(num_format.format, value);
+
+  if (strpbrk(unitstr, "+-"))   /* show sums and differences of units */
+    logprintf(" (%s)", unitstr);   /* in parens */
+     /* fractional unit 1|x and multiplier is all digits and not one--   */
+     /* no space or asterisk or numerator (3|8 in instead of 3 * 1|8 in) */
+  else if (printnum && !flags.showfactor
+           && startswith(unitstr,"1|") && isfract(unitstr)
+           && !is_one && !hasnondigits)
+    logputs(unitstr+1);
+     /* multiplier is unity and unit begins with a number--no space or       */
+     /* asterisk (multiplier was not shown, and the space was already output)*/
+  else if (is_one && isdecimal(*unitstr))
+    logputs(unitstr);
+     /* unitstr begins with a non-fraction number and multiplier was */
+     /* shown--prefix a spaced asterisk */
+  else if (isdecimal(unitstr[0]))
+    logprintf(" * %s", unitstr);
+  else
+    logprintf(" %s", unitstr);
+} 
+
+
+/* Show the conversion factors or print the conformability error message */
+
+int
+showanswer(char *havestr,struct unittype *have,
+           char *wantstr,struct unittype *want)
+{
+   struct unittype invhave;
+   int doingrec;  /* reciprocal conversion? */
+   char *right = NULL, *left = NULL;
+
+   doingrec=0;
+   if (compareunits(have, want, ignore_dimless)) {
+        char **src,**dest;
+
+        invhave.factor=1/have->factor;
+        for(src=have->numerator,dest=invhave.denominator;*src;src++,dest++)
+           *dest=*src;
+        *dest=0;
+        for(src=have->denominator,dest=invhave.numerator;*src;src++,dest++)
+           *dest=*src;
+        *dest=0;
+        if (flags.strictconvert || compareunits(&invhave, want, ignore_dimless)){
+          showconformabilityerr(havestr, have, wantstr, want);
+          return -1;
+        }
+        if (flags.verbose>0)
+          logputchar('\t');
+        logputs("reciprocal conversion\n");
+        have=&invhave;
+        doingrec=1;
+   } 
+   if (flags.verbose==2) {
+     if (!doingrec) 
+       left=right="";
+     else if (strchr(havestr,'/')) {
+       left="1 / (";
+       right=")";
+     } else {
+       left="1 / ";
+       right="";
+     }
+   }   
+
+   /* Print the first line of output. */
+
+   if (flags.verbose==2) 
+     logprintf("\t%s%s%s = ",left,havestr,right);
+   else if (flags.verbose==1)
+     logputs("\t* ");
+   if (flags.verbose==2)
+     showunitname(have->factor / want->factor, wantstr, PRINTNUM);
+   else
+     logprintf(num_format.format, have->factor / want->factor);
+
+   /* Print the second line of output. */
+
+   if (!flags.oneline){
+     if (flags.verbose==2) 
+       logprintf("\n\t%s%s%s = (1 / ",left,havestr,right);
+     else if (flags.verbose==1)
+       logputs("\n\t/ ");
+     else 
+       logputchar('\n');
+     logprintf(num_format.format, want->factor / have->factor);
+     if (flags.verbose==2) {
+       logputchar(')');
+       showunitname(0,wantstr, NOPRINTNUM); 
+     }
+   }
+   logputchar('\n');
+   return 0;
+}
+
+
+/* Checks that the function definition has a valid inverse 
+   Prints a message to stdout if function has bad definition or
+   invalid inverse. 
+*/
+
+#define SIGN(x) ( (x) > 0.0 ?   1 :   \
+                ( (x) < 0.0 ? (-1) :  \
+                                0 ))
+
+void
+checkfunc(struct func *infunc, int verbose)
+{
+  struct unittype theunit, saveunit;
+  struct prefixlist *prefix;
+  int err, i;
+  double direction;
+
+  if (infunc->skip_error_check){
+    if (verbose)
+      printf("skipped function '%s'\n", infunc->name);
+    return;
+  }
+  if (verbose)
+    printf("doing function '%s'\n", infunc->name);
+  if ((prefix=plookup(infunc->name)) 
+      && strlen(prefix->name)==strlen(infunc->name))
+    printf("Warning: '%s' defined as prefix and function\n",infunc->name);
+  if (infunc->table){
+    /* Check for valid unit for the table */
+    if (parseunit(&theunit, infunc->tableunit, 0, 0) ||
+        completereduce(&theunit))
+      printf("Table '%s' has invalid units '%s'\n",
+             infunc->name, infunc->tableunit);
+    freeunit(&theunit);
+
+    /* Check for monotonicity which is needed for unique inverses */
+    if (infunc->tablelen<=1){ 
+      printf("Table '%s' has only one data point\n", infunc->name);
+      return;
+    }
+    direction = SIGN(infunc->table[1].value -  infunc->table[0].value);
+    for(i=2;i<infunc->tablelen;i++)
+      if (SIGN(infunc->table[i].value-infunc->table[i-1].value) != direction){
+        printf("Table '%s' lacks unique inverse around entry " ERRNUMFMT "\n",
+               infunc->name, infunc->table[i].location);
+        return;
+      }
+    return;
+  }
+  if (infunc->forward.dimen){
+    if (parseunit(&theunit, infunc->forward.dimen, 0, 0) ||
+        completereduce(&theunit)){
+      printf("Function '%s' has invalid units '%s'\n", 
+             infunc->name, infunc->forward.dimen);
+      freeunit(&theunit);
+      return;
+    }
+  } else initializeunit(&theunit); 
+  if (infunc->forward.domain_max && infunc->forward.domain_min)
+    theunit.factor *= 
+             (*infunc->forward.domain_max+*infunc->forward.domain_min)/2;
+  else if (infunc->forward.domain_max) 
+    theunit.factor = theunit.factor * *infunc->forward.domain_max - 1;
+  else if (infunc->forward.domain_min)
+    theunit.factor = theunit.factor * *infunc->forward.domain_min + 1;
+  else 
+    theunit.factor *= 7;   /* Arbitrary choice where we evaluate inverse */
+  if (infunc->forward.dimen){
+    unitcopy(&saveunit, &theunit);
+    err = evalfunc(&theunit, infunc, FUNCTION, ALLERR);
+    if (err) {
+      printf("Error in definition %s(%s) as '%s':\n",
+             infunc->name, infunc->forward.param, infunc->forward.def);
+      printf("      %s\n",errormsg[err]);
+      freeunit(&theunit);
+      freeunit(&saveunit);
+      return;
+    }
+  } else {
+#   define MAXPOWERTOCHECK 4
+    struct unittype resultunit, arbunit;
+    char unittext[9]; 
+    double factor;
+    int errors[MAXPOWERTOCHECK], errcount=0;
+    char *indent;
+
+    strcpy(unittext,"(kg K)^ ");
+    factor = theunit.factor;
+    initializeunit(&saveunit);
+    initializeunit(&resultunit);
+    for(i=0;i<MAXPOWERTOCHECK;i++){
+      lastchar(unittext) = '0'+i;
+      err = parseunit(&arbunit, unittext, 0, 0);
+      if (err) initializeunit(&arbunit);
+      arbunit.factor = factor;
+      unitcopy(&resultunit, &arbunit);
+      errors[i] = evalfunc(&resultunit, infunc, FUNCTION, ALLERR); 
+      if (errors[i]) errcount++;
+      else {
+        freeunit(&saveunit);
+        freeunit(&theunit);
+        unitcopy(&saveunit, &arbunit);
+        unitcopy(&theunit, &resultunit);
+      }
+      freeunit(&resultunit);
+      freeunit(&arbunit);
+    }
+    if (!errors[0] && errcount==3) {
+      printf("Warning: function '%s(%s)' defined as '%s'\n",
+             infunc->name, infunc->forward.param, infunc->forward.def);
+      printf("         appears to require a dimensionless argument, 'units' keyword not given\n");
+      indent = "         ";
+    }
+    else if (errcount==MAXPOWERTOCHECK) {
+      printf("Error or missing 'units' keyword in definion %s(%s) as '%s'\n",
+             infunc->name, infunc->forward.param, infunc->forward.def);
+      indent="      ";
+    }
+    else if (errcount){
+      printf("Warning: function '%s(%s)' defined as '%s'\n",
+             infunc->name, infunc->forward.param, infunc->forward.def);
+      printf("         failed for some test inputs:\n");
+      indent = "         ";
+    }
+    for(i=0;i<MAXPOWERTOCHECK;i++) 
+      if (errors[i]) {
+        lastchar(unittext) = '0'+i;
+        printf("%s%s(",indent,infunc->name);
+        printf(num_format.format, factor);
+        printf("%s): %s\n", unittext, errormsg[errors[i]]);
+      }
+  }
+  if (completereduce(&theunit)){
+    printf("Definition %s(%s) as '%s' is irreducible\n",
+           infunc->name, infunc->forward.param, infunc->forward.def);
+    freeunit(&theunit);
+    freeunit(&saveunit);
+    return;
+  }    
+  if (!(infunc->inverse.def)){
+    printf("Warning: no inverse for function '%s'\n", infunc->name);
+    freeunit(&theunit);
+    freeunit(&saveunit);
+    return;
+  }
+  err = evalfunc(&theunit, infunc, INVERSE, ALLERR);
+  if (err){
+    printf("Error in inverse ~%s(%s) as '%s':\n",
+           infunc->name,infunc->inverse.param, infunc->inverse.def);
+    printf("      %s\n",errormsg[err]);
+    freeunit(&theunit);
+    freeunit(&saveunit);
+    return;
+  }
+  divunit(&theunit, &saveunit);
+  if (unit2num(&theunit) || fabs(theunit.factor-1)>1e-12)
+    printf("Inverse is not the inverse for function '%s'\n", infunc->name);
+  freeunit(&theunit);
+}
+
+
+struct namedef { 
+  char *name;
+  char *def;
+};
+    
+#define CONFORMABLE 1
+#define TEXTMATCH 2
+
+void 
+addtolist(struct unittype *have, char *searchstring, char *rname, char *name, 
+               char *def, struct namedef **list, int *listsize, 
+               int *maxnamelen, int *count, int searchtype)
+{
+  struct unittype want;
+  int len = 0;
+  int keepit = 0;
+
+  if (!name) 
+    return;
+  if (searchtype==CONFORMABLE){
+    initializeunit(&want);
+    if (!parseunit(&want, name,0,0) && !completereduce(&want))
+      keepit = !compareunits(have,&want,ignore_dimless);
+  } else if (searchtype == TEXTMATCH) {
+    keepit = (strstr(rname,searchstring) != NULL);
+  }
+  if (keepit){
+    if (*count==*listsize){
+      *listsize += 100;
+      *list = (struct namedef *) 
+        realloc(*list,*listsize*sizeof(struct namedef));
+      if (!*list){
+        fprintf(stderr, "%s: memory allocation error (addtolist)\n",
+                progname);  
+        exit(EXIT_FAILURE);
+      }
+    }
+    (*list)[*count].name = rname;
+    if (strchr(def, PRIMITIVECHAR))
+      (*list)[*count].def = "<primitive unit>";
+    else
+      (*list)[*count].def = def;
+    (*count)++;
+    len = strwidth(name);
+    if (len>*maxnamelen)
+          *maxnamelen = len;
+  }
+  if (searchtype == CONFORMABLE)
+    freeunit(&want);
+}
+
+
+int 
+compnd(const void *a, const void *b)
+{
+  return strcmp(((struct namedef *)a)->name, ((struct namedef *)b)->name);
+}
+
+
+int 
+screensize()
+{
+   int lines = 20;      /* arbitrary but probably safe value */
+
+#if defined (_WIN32) && defined (_MSC_VER)
+   CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+   if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
+       lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+#endif
+
+#ifdef HAVE_IOCTL
+   struct winsize ws;
+   int fd;
+   fd = open("/dev/tty", O_RDWR);
+   if (fd>=0 && ioctl(fd, TIOCGWINSZ, &ws)==0)
+     lines = ws.ws_row;
+#endif
+
+   return lines;
+}
+
+/*
+  determines whether the output will fit on the screen, and opens a
+  pager if it won't
+*/
+FILE *
+get_output_fp(int lines)
+{
+  FILE *fp = NULL;
+
+  if (isatty(fileno(stdout)) && screensize() < lines) {
+    if ((fp = popen(pager, "w")) == NULL) {
+      fprintf(stderr, "%s: can't run pager '%s--'", progname, pager);
+      perror((char*) NULL);
+    }
+  }
+  if (!fp)
+    fp = stdout;
+
+  return fp;
+}
+
+int
+countlines(char *msg)
+{
+  int nlines = 0;
+  char *p;
+
+  for (p = msg; *p; p++)
+    if (*p == '\n')
+      nlines++;
+
+  return nlines;
+}
+
+/* 
+   If have is non-NULL then search through all units and print the ones
+   which are conformable with have.  Otherwise search through all the 
+   units for ones whose names contain the second argument as a substring.
+*/
+
+void 
+tryallunits(struct unittype *have, char *searchstring)
+{
+  struct unitlist *uptr;
+  struct namedef *list;
+  int listsize, maxnamelen, count;
+  struct func *funcptr;
+  struct wantalias *aliasptr;
+  int i, j, searchtype;
+  FILE *outfile;
+  char *seploc, *firstunit;
+
+  list = (struct namedef *) mymalloc( 100 * sizeof(struct namedef), 
+                                       "(tryallunits)");
+  listsize = 100;
+  maxnamelen = 0;
+  count = 0;
+
+  if (have)
+    searchtype = CONFORMABLE;
+  else {
+    if (!searchstring)
+      searchstring="";
+    searchtype = TEXTMATCH;
+  }
+
+  for(i=0;i<HASHSIZE;i++)
+    for (uptr = utab[i]; uptr; uptr = uptr->next)
+      addtolist(have, searchstring, uptr->name, uptr->name, uptr->value, 
+                &list, &listsize, &maxnamelen, &count, searchtype);
+  for(i=0;i<SIMPLEHASHSIZE;i++)
+    for(funcptr=ftab[i];funcptr;funcptr=funcptr->next){
+      if (funcptr->table) 
+        addtolist(have, searchstring, funcptr->name, funcptr->tableunit, 
+                "<piecewise linear>", &list, &listsize, &maxnamelen, &count, 
+                searchtype);
+      else
+        addtolist(have, searchstring, funcptr->name, funcptr->inverse.dimen, 
+                "<nonlinear>", &list, &listsize, &maxnamelen, &count, 
+                searchtype);
+  }
+  for(aliasptr=firstalias;aliasptr;aliasptr=aliasptr->next){
+    firstunit = dupstr(aliasptr->definition);/* coverity[var_assigned] */
+    seploc = strchr(firstunit,UNITSEPCHAR);  /* Alias definitions allowed in */
+    *seploc = 0;                             /* database contain UNITSEPCHAR */
+    addtolist(have, searchstring, aliasptr->name, firstunit, 
+              aliasptr->definition, &list, &listsize, &maxnamelen, &count,
+              searchtype);
+    free(firstunit);
+  }
+    
+  qsort(list, count, sizeof(struct namedef), compnd);
+
+  if (count==0)
+    puts("No matching units found.");
+#ifdef SIGPIPE
+  signal(SIGPIPE, SIG_IGN);
+#endif
+  /* see if we need a pager */
+  outfile = get_output_fp(count);
+  for(i=0;i<count;i++){
+    fputs(list[i].name,outfile);
+    if (flags.verbose > 0 || flags.interactive) {
+        for(j=strwidth(list[i].name);j<=maxnamelen;j++)
+          putc(' ',outfile);
+        tightprint(outfile,list[i].def);
+    }
+    fputc('\n',outfile);
+  }
+  if (outfile != stdout)
+    pclose(outfile);
+#ifdef SIGPIPE
+  signal(SIGPIPE, SIG_DFL);
+#endif
+}
+
+
+/* If quiet is false then prompt user with the query.  
+
+   Fetch one line of input and return it in *buffer.
+
+   The bufsize argument is a dummy argument if we are using readline. 
+   The readline version frees buffer if it is non-null.  The other
+   version keeps the same buffer and grows it as needed.
+
+   If no data is read, then this function exits the program. 
+*/
+
+
+void
+getuser_noreadline(char **buffer, int *bufsize, const char *query)
+{
+#ifdef SUPPORT_UTF8
+  int valid = 0;
+  while(!valid){
+    fputs(query, stdout);
+    if (!fgetslong(buffer, bufsize, stdin,0)){
+      if (!flags.quiet)
+        putchar('\n');
+      exit(EXIT_SUCCESS);
+    }
+    valid = strwidth(*buffer)>=0;
+    if (!valid)
+      printf("Error: %s\n",invalid_utf8);
+  }
+#else
+  fputs(query, stdout);
+  if (!fgetslong(buffer, bufsize, stdin,0)){
+    if (!flags.quiet)
+      putchar('\n');
+    exit(EXIT_SUCCESS);
+  }
+#endif
+}  
+
+
+#ifndef READLINE
+#  define getuser getuser_noreadline
+#else 
+           /* we DO have readline */
+void
+getuser_readline(char **buffer, int *bufsize, const char *query)
+{
+#ifdef SUPPORT_UTF8
+  int valid = 0;
+  while (!valid){
+    if (*buffer) free(*buffer);
+    *buffer = readline(query);
+    if (*buffer)
+      replacectrlchars(*buffer);
+    if (!*buffer || strwidth(*buffer)>=0)
+        valid=1;
+    else
+      printf("Error: %s\n",invalid_utf8);
+  }
+#else
+    if (*buffer) free(*buffer);
+    *buffer = readline(query);
+    if (*buffer)
+      replacectrlchars(*buffer);
+#endif
+  if (nonempty(*buffer)) add_history(*buffer);
+  if (!*buffer){
+    if (!flags.quiet)
+       putchar('\n');
+    exit(EXIT_SUCCESS);
+  }
+}
+
+
+void
+getuser(char **buffer, int *bufsize, const char *query)
+{
+  if (flags.readline)
+    getuser_readline(buffer,bufsize,query);
+  else
+    getuser_noreadline(buffer,bufsize,query);
+}
+
+
+/* Unit name completion for readline.  
+
+   Complete function names or alias names or builtin functions.  
+
+   Complete to the end of a prefix or complete to the end of a unit.
+   If the text includes a full prefix plus part of a unit and if the
+   prefix is longer than one character then complete that compound.
+   Don't complete a prefix fragment into prefix plus anything.
+*/
+   
+#define CU_ALIAS 0
+#define CU_BUILTIN 1
+#define CU_FUNC 2
+#define CU_PREFIX 3
+#define CU_UNITS 4
+#define CU_DONE 5
+
+char *
+completeunits(char *text, int state)
+{
+  static int uhash, fhash, phash, checktype;
+  static struct prefixlist *curprefix, *unitprefix;
+  static struct unitlist *curunit;
+  static struct func *curfunc;
+  static struct wantalias *curalias;
+  static char **curbuiltin;
+  char *output = 0;
+
+#ifndef NO_SUPPRESS_APPEND
+  rl_completion_suppress_append = 1;
+#endif
+  
+  if (!state){     /* state == 0 means this is the first call, so initialize */
+    checktype = 0; /* start at first type */
+    fhash = uhash = phash = 0;
+    unitprefix=0; /* search for unit continuations starting with this prefix */
+    curfunc=ftab[fhash];
+    curunit=utab[uhash];
+    curprefix=ptab[phash];
+    curbuiltin = builtins;
+    curalias = firstalias;
+  }
+  while (checktype != CU_DONE){
+    if (checktype == CU_ALIAS){
+      while(curalias){
+        if (startswith(curalias->name,text))
+          output = dupstr(curalias->name);
+        curalias = curalias->next;
+        if (output) return output;
+      }
+      checktype++;
+    }
+    if (checktype == CU_BUILTIN){
+      while(*curbuiltin){
+        if (startswith(*curbuiltin,text))
+          output = dupstr(*curbuiltin);
+        curbuiltin++;
+        if (output) return output;
+      }
+      checktype++;
+    }
+    while (checktype == CU_FUNC){
+      while (!curfunc && fhash<SIMPLEHASHSIZE-1){
+        fhash++;
+        curfunc = ftab[fhash];
+      }
+      if (!curfunc) 
+        checktype++;
+      else {
+        if (startswith(curfunc->name,text))
+          output = dupstr(curfunc->name);
+        curfunc = curfunc->next;
+        if (output) return output;  
+      }
+    }
+    while (checktype == CU_PREFIX){
+      while (!curprefix && phash<SIMPLEHASHSIZE-1){
+        phash++;
+        curprefix = ptab[phash];
+      }
+      if (!curprefix)
+        checktype++;
+      else {
+        if (startswith(curprefix->name,text))
+          output = dupstr(curprefix->name);
+        curprefix = curprefix->next;
+        if (output) return output;
+      }
+    }
+    while (checktype == CU_UNITS){    
+      while (!curunit && uhash<HASHSIZE-1){
+        uhash++;
+        curunit = utab[uhash];
+      }
+      /* If we're done with the units go through them again with */
+      /* the largest possible prefix stripped off */
+      if (!curunit && !unitprefix 
+            && (unitprefix = plookup(text)) && strlen(unitprefix->name)>1){
+        uhash = 0;
+        curunit = utab[uhash];
+      }
+      if (!curunit) {
+        checktype++;
+        break;
+      }
+      if (unitprefix){
+        if (startswith(curunit->name, text+unitprefix->len)){
+          output = (char *)mymalloc(1+strlen(curunit->name)+unitprefix->len,
+                                    "(completeunits)");
+          strcpy(output, unitprefix->name);
+          strcat(output, curunit->name);
+        }       
+      } 
+      else if (startswith(curunit->name,text)) 
+        output = dupstr(curunit->name);
+      curunit=curunit->next;
+      if (output)
+        return output;
+    }
+  } 
+  return 0;
+}
+
+#endif /* READLINE */
+
+
+
+/* see if current directory contains an executable file */
+int
+checkcwd (char *file)
+{
+  FILE *fp;
+  char *p;
+
+  fp = openfile(file, "r");
+  if (fp){
+    fclose(fp);
+    return 1;
+  }
+#ifdef _WIN32
+  else if (!((p = strrchr(file, '.')) && isexe(p))) {
+    char *pathname;
+
+    pathname = mymalloc(strlen(file) + strlen(EXE_EXT) + 1,
+                        "(checkcwd)");
+    strcpy(pathname, file);
+    strcat(pathname, EXE_EXT);
+    fp = openfile(pathname, "r");
+    free(pathname);
+    if (fp) {
+      fclose(fp);
+      return 1;
+    }
+  }
+#endif
+
+  return 0;
+}
+
+/* 
+   return the last component of a pathname, and remove a .exe extension
+   if one exists.
+*/
+
+char *
+getprogramname(char *path)
+{
+  size_t proglen;
+  char *p;
+
+  path = pathend(path);
+
+  /* get rid of filename extensions in Windows */
+  proglen = strlen(path);
+
+  if ((p = strrchr(path, '.')) && isexe(p))
+    proglen -= 4;
+  return dupnstr(path, proglen);
+}
+
+/*
+   Find the directory that contains the invoked executable.
+*/
+
+char *
+getprogdir(char *progname, char **fullprogname)
+{
+  char *progdir = NULL;
+  char *p;
+
+#if defined (_WIN32) && defined (_MSC_VER)
+  char buf[FILENAME_MAX + 1];
+
+  /* get the full pathname of the current executable and be done with it */
+  /* TODO: is there way to do this with gcc? */
+
+  if (GetModuleFileName(NULL, buf, FILENAME_MAX + 1))
+    progdir = dupstr(buf);
+#endif
+
+  /* If path name is absolute or includes more than one component use it */
+
+  if (!progdir && (isfullpath(progname) || hasdirsep(progname)))
+    progdir = dupstr(progname);
+
+
+  /*
+  command.com and cmd.exe under Windows always run a program that's in the
+  current directory whether or not the current directory is in PATH, so we need
+  to check the current directory.
+
+  This could return a false positive if units is run from a Unix-like command
+  interpreter under Windows if the current directory is not in PATH but
+  contains 'units' or 'units.exe'
+  */
+#if defined (_WIN32) && !defined (_MSC_VER)
+  if (!progdir && checkcwd(progname)) 
+      progdir = dupstr(progname);
+#endif
+
+  /* search PATH to find the executable */
+  if (!progdir) {
+    char *env;
+    env = getenv("PATH");
+    if (env) {
+      /* search PATH */
+      char *direc, *direc_end, *pathname;
+      int len;
+      FILE *fp;
+
+      pathname = mymalloc(strlen(env)+strlen(progname)+strlen(EXE_EXT)+2,
+                                                         "(getprogdir)");
+      direc = env;
+      while (direc) {
+        direc_end = strchr(direc,PATHSEP);
+        if (!direc_end)
+          len = strlen(direc);
+        else 
+          len = direc_end-direc;
+        strncpy(pathname, direc, len);
+        if (len>0)
+          pathname[len++]=DIRSEP;
+        strcpy(pathname+len, progname);
+        fp = openfile(pathname, "r");
+        if (fp){
+          progdir = dupstr(pathname);
+          break;
+        }
+#ifdef _WIN32
+        /*
+          executable may or may not have '.exe' suffix, so we need to
+          look for both
+        */
+        if (!((p = strrchr(pathname, '.')) && isexe(p))) {
+          strcat(pathname, EXE_EXT);
+          fp = openfile(pathname, "r");
+          if (fp){
+            progdir = dupstr(pathname);
+            break;
+          }
+        }
+#endif
+        direc = direc_end;
+        if (direc) direc++;
+      }
+      free(pathname);
+      if (fp)
+        fclose(fp);
+    }
+  }
+
+  if (!progdir) {
+    fprintf(stderr, "%s: cannot find program directory\n", progname);
+    exit(EXIT_FAILURE);
+  }
+
+  *fullprogname = dupstr(progdir);      /* used by printversion() */
+  p = pathend(progdir);
+  *p = '\0';  
+ 
+  return progdir; 
+}
+
+/*
+  find a possible data directory relative to a 'bin' directory that
+  contains the executable
+*/
+
+char *
+getdatadir()
+{
+    int progdirlen;
+    char *p;
+
+    if (isfullpath(DATADIR))
+      return DATADIR;
+    if (!progdir || emptystr(DATADIR))
+      return NULL;
+    progdirlen = strlen(progdir);
+    datadir = (char *) mymalloc(progdirlen + strlen(DATADIR) + 2,
+				"(getdatadir)");
+    strcpy(datadir, progdir);
+    if (isdirsep(progdir[progdirlen - 1]))
+      datadir[progdirlen - 1] = '\0';   /* so pathend() can work */
+    p = pathend(datadir);
+    if ((strlen(p) == 3) && (tolower(p[0]) == 'b') \
+       && (tolower(p[1]) == 'i') && (tolower(p[2]) == 'n')) {
+      p = DATADIR;
+      while (*p == '.')         /* ignore "./", "../" */
+	p++;
+      if (isdirsep(*p))
+	p++;
+      strcpy(pathend(datadir), p);
+      
+      return datadir;
+    }
+    else
+      return NULL;
+}
+
+void
+showfilecheck(int errnum, char *filename)
+{
+  if (errnum==ENOENT)
+    printf("  Checking %s\n", filename);
+  else
+    printf("  Checking %s: %s\n", filename, strerror(errnum)); 
+}
+
+/*
+  On Windows, find the locale map
+  
+  If print is set to ERRMSG then display error message if the file is
+  not valid.  If print is set to SHOWFILES then display files that are
+  checked (when the filename is not a fully specified path).  If print
+  is set to NOERRMSG then do not display anything.
+
+  Returns filename of valid local map file or NULL if no file was found.
+*/
+#ifdef _WIN32
+char *
+findlocalemap(int print)
+{
+  FILE *map = NULL;
+  char *filename = NULL;
+  char *file;
+
+  /*
+      Try the environment variable UNITSLOCALEMAP, then the #defined
+      value LOCALEMAP, then the directory containing the units
+      executable, then the directory given by datadir, and finally
+      the directory containing the units data file.
+  */
+
+  /* check the environment variable UNITSLOCALEMAP */
+  file = getenv("UNITSLOCALEMAP");
+  if (nonempty(file)) {
+    map = openfile(file,"rt");
+    if (!map) {
+      if (print == ERRMSG) {
+        fprintf(stderr,
+                "%s: cannot open locale map '%s'\n  specified in UNITSLOCALEMAP environment variable. ",
+                progname, file);
+        perror((char *) NULL);
+      }
+      return NULL;
+    }
+    else
+      filename = dupstr(file);
+  }
+
+  /* check the #defined value LOCALEMAP */
+  if (!map) {
+    file = LOCALEMAP;
+    map = openfile(file,"rt");
+    if (map)
+      filename = dupstr(file);
+  }
+
+  if (!map && !progdir) {
+    if (print == ERRMSG) {
+      fprintf(stderr,
+              "%s: cannot find locale map--program directory not set\n",
+              progname);
+      exit(EXIT_FAILURE);
+    }
+    else
+      return NULL;
+  }
+
+  /* check the directory with the units executable */
+  if (!map) {
+    filename = (char *) mymalloc(strlen(progdir) + strlen(file) + 2,
+               "(findlocalemap)");
+    strcpy(filename, progdir);
+    strcat(filename, file);
+    map = openfile(filename,"rt");
+    if (print==SHOWFILES && !map)
+      showfilecheck(errno, filename);
+  }
+
+  /* check data directory */
+  if (!map && datadir) {     
+    if (filename)
+      free(filename);
+    filename = (char *) mymalloc(strlen(datadir) + strlen(file) + 3,
+                                 "(findlocalemap)");
+    strcpy(filename, datadir);
+    strcat(filename, DIRSEPSTR);
+    strcat(filename, file);
+    map = openfile(filename, "rt");
+    if (print==SHOWFILES && !map)
+      showfilecheck(errno, filename);
+  }
+
+  /* check the directory with the units data file  */
+  if (!map && unitsfiles[0]) {
+    char *lastfilename = NULL;
+
+    if (filename) {
+      if (datadir)
+	lastfilename = dupstr(filename);
+      free(filename);
+    }
+    filename = (char *) mymalloc(strlen(unitsfiles[0]) + strlen(file) + 2,
+               "(findlocalemap)");
+    strcpy(filename, unitsfiles[0]);
+    strcpy(pathend(filename), file);
+
+    /* don't bother if we've just checked for it */
+    if (lastfilename && strcmp(filename, lastfilename)) {
+      map = openfile(filename,"rt");
+      if (print==SHOWFILES && !map)
+	showfilecheck(errno, filename);
+    }
+  }
+
+  if (map) {
+    fclose(map);
+    return filename;
+  }
+  else {
+    if (filename)
+      free(filename);
+    return NULL;
+  }
+}
+#endif
+
+/*
+  Find the units database file. 
+  
+  If print is set to ERRMSG then display error message if the file is
+  not valid.  If print is set to SHOWFILES then display files that are
+  checked (when the filename is not a fully specified path).  If print
+  is set to NOERRMSG then do not display anything.
+
+  Returns filename of valid database file or NULL if no file
+  was found.  
+*/
+
+char *
+findunitsfile(int print)
+{
+  FILE *testfile=0;
+  char *file;
+
+  file = getenv("UNITSFILE");
+  if (nonempty(file)) {
+    testfile = openfile(file, "rt");
+    if (!testfile) {
+      if (print==ERRMSG) {
+        fprintf(stderr,
+                "%s: cannot open units file '%s' in environment variable UNITSFILE.  ",
+                progname, file);
+        perror((char *) NULL);
+      }
+      return NULL;
+    }
+  }
+
+  if (!testfile && isfullpath(UNITSFILE)){
+    file = UNITSFILE;
+    testfile = openfile(file, "rt");
+    if (!testfile) {
+      if (print==ERRMSG) {
+        fprintf(stderr,
+                "%s: cannot open units data file '%s'.  ", progname, UNITSFILE);
+        perror((char *) NULL);
+      }
+      return NULL;
+    }
+  }      
+
+  if (!testfile && !progdir) {
+    if (print==ERRMSG) {
+      fprintf(stderr,
+              "%s: cannot open units file '%s' and cannot find program directory.\n", progname, UNITSFILE);
+      perror((char *) NULL);
+    }
+    return NULL;
+  }
+
+  if (!testfile) {
+    /* check the directory containing the units executable */
+    file = (char *) mymalloc(strlen(progdir)+strlen(UNITSFILE)+1,
+                             "(findunitsfile)");
+    strcpy(file, progdir);
+    strcat(file, UNITSFILE);
+    testfile = openfile(file, "rt");
+    if (print==SHOWFILES && !testfile)
+      showfilecheck(errno, file);
+    if (!testfile)
+      free(file);
+  }
+
+  /* check data directory */
+  if (!testfile && datadir) {
+    file = (char *) mymalloc(strlen(datadir) + strlen(UNITSFILE) + 2,
+                             "(findunitsfile)");
+    strcpy(file, datadir);
+    strcat(file, DIRSEPSTR);
+    strcat(file, UNITSFILE);
+    testfile = openfile(file, "rt");
+    if (print==SHOWFILES && !testfile)
+      showfilecheck(errno, file);
+    if (!testfile)
+      free(file);
+  }
+
+  if (!testfile) {
+    if (print==ERRMSG)
+      fprintf(stderr,"%s: cannot find units file '%s'\n", progname, UNITSFILE);
+     return NULL;
+  }
+  else {
+    fclose(testfile);
+    return file;
+  }
+}
+
+/*
+  Find the user's home directory.  Unlike *nix, Windows doesn't usually
+  set HOME, so we check several other places as well.
+*/
+char *
+findhome(char **errmsg)
+{
+  struct stat statbuf;
+  int allocated = 0;
+  char *homedir;
+  char notfound[] = "Specified home directory '%s' does not exist";
+  char notadir[] = "Specified home directory '%s' is not a directory";
+
+  /*
+    Under UNIX just check HOME.  Under Windows, if HOME is not set then
+    check HOMEDRIVE:HOMEPATH and finally USERPROFILE
+  */
+  homedir = getenv("HOME");
+
+#ifdef _WIN32
+  if (!nonempty(homedir)) {
+    /* try a few other places */
+    /* try HOMEDRIVE and HOMEPATH */
+    char *homedrive, *homepath;
+    if ((homedrive = getenv("HOMEDRIVE")) && *homedrive && (homepath = getenv("HOMEPATH")) && *homepath) {
+      homedir = mymalloc(strlen(homedrive)+strlen(homepath)+1,"(personalfile)");
+      allocated = 1;
+      strcpy(homedir, homedrive);
+      strcat(homedir, homepath);
+    }
+    else
+      /* finally, try USERPROFILE */
+      homedir = getenv("USERPROFILE");
+  }
+#endif
+  
+  /*
+    If a home directory is specified, see if it exists and is a
+    directory.  If not set error message text. 
+  */
+  
+  if (nonempty(homedir)) {
+    if (stat(homedir, &statbuf) != 0) {
+      *errmsg = malloc(strlen(notfound)+strlen(homedir));
+      sprintf(*errmsg, notfound, homedir);
+    }
+    else if (!(statbuf.st_mode & S_IFDIR)) {
+      *errmsg = malloc(strlen(notadir)+strlen(homedir));
+      sprintf(*errmsg, notadir, homedir);
+    }
+    if (!allocated)
+      homedir = dupstr(homedir);
+    return homedir;
+  }
+  else {
+    *errmsg = "no home directory";
+    return NULL;
+  }
+}
+
+/*
+  Find a personal file.  First checks the specified environment variable 
+  (envname) for the filename to  use.  If this is unset then search user's 
+  home directory for basename.  If there is no home directory, returns 
+  NULL.  Otherwise if the file exists then returns its name in newly allocated
+  space and sets *exists to 1. If the file does not exist then sets *exist to 
+  zero and:
+      With checkonly == 0, prints error message and returns NULL
+      With checkonly != 0, returns filename (does not print error message)
+*/
+char *
+personalfile(const char *envname, const char *basename,
+             int checkonly, int *exists)
+{
+  FILE *testfile;
+  char *filename=NULL;
+
+  *exists = 0; 
+
+  /* First check the specified environment variable for a file name */ 
+  if (envname) 
+    filename = getenv(envname);
+  if (nonempty(filename)){       
+    testfile = openfile(filename, "rt");
+    if (testfile){
+      fclose(testfile);
+      *exists = 1;
+      return filename;
+    }
+    if (checkonly)
+      return filename;
+    else {
+      fprintf(stderr, "%s: cannot open file '%s' specified in %s environment variable: ",
+              progname, filename, envname);
+      perror((char *) NULL);
+      return NULL;
+    }
+  }
+  /* Environment variable not set: look in home directory */
+  else if (nonempty(homedir)) {
+    filename = mymalloc(strlen(homedir)+strlen(basename)+2,
+                        "(personalfile)");
+    strcpy(filename,homedir);
+
+    if (strcmp(homedir, "/") && strcmp(homedir, "\\"))
+      strcat(filename,DIRSEPSTR);
+    strcat(filename,basename);
+
+    testfile = openfile(filename, "rt");
+    if (testfile){
+      fclose(testfile);
+      *exists = 1;
+      return filename;
+    }
+    if (checkonly)
+      return filename;
+    else {
+      if (errno==EACCES || errno==EISDIR) {
+        fprintf(stderr,"%s: cannot open file '%s': ",progname,filename);
+        perror(NULL);
+      }
+      free(filename);
+      return NULL;
+    }
+  }
+  else
+    return NULL;
+}
+
+
+/* print usage message */
+
+void 
+usage()
+{
+  int nlines;
+  char *unitsfile;
+  char *msg = "\nUsage: %s [options] ['from-unit' 'to-unit']\n\n\
+Options:\n\
+    -h, --help           show this help and exit\n\
+    -c, --check          check that all units reduce to primitive units\n\
+        --check-verbose  like --check, but lists units as they are checked\n\
+        --verbose-check    so you can find units that cause endless loops\n\
+    -d, --digits         show output to specified number of digits (default: %d)\n\
+    -e, --exponential    exponential format output\n\
+    -f, --file           specify a units data file (-f '' loads default file)\n"
+#ifdef READLINE
+"\
+    -H, --history        specify readline history file (-H '' disables history)\n"
+#endif
+"\
+    -L, --log            specify a file to log conversions\n\
+    -l, --locale         specify a desired locale\n\
+    -m, --minus          make - into a subtraction operator (default)\n\
+        --oldstar        use old '*' precedence, higher than '/'\n\
+        --newstar        use new '*' precedence, equal to '/'\n\
+    -n, --nolists        disable conversion to unit lists\n\
+    -S, --show-factor    show non-unity factor before 1|x in multi-unit output\n\
+        --conformable    in non-interactive mode, show all conformable units\n\
+    -o, --output-format  specify printf numeric output format (default: %%.%d%c)\n\
+    -p, --product        make '-' into a product operator\n\
+    -q, --quiet          suppress prompting\n\
+        --silent         same as --quiet\n\
+    -s, --strict         suppress reciprocal unit conversion (e.g. Hz<->s)\n\
+    -v, --verbose        show slightly more verbose output\n\
+        --compact        suppress printing of tab, '*', and '/' character\n\
+    -1, --one-line       suppress the second line of output\n\
+    -t, --terse          terse output (--strict --compact --quiet --one-line)\n\
+    -r, --round          round last element of unit list output to an integer\n\
+    -U, --unitsfile      show units data filename and exit\n\
+    -u, --units          specify a CGS units system or natural units system:\n\
+                            gauss[ian],esu,emu,hlu,natural,natural-gauss,\n\
+                            hartree,planck,planck-red,si\n\
+    -V, --version        show version, data filenames (with -t: version only)\n\
+    -I, --info           show version, files, and program properties\n";
+  FILE *fp = NULL;
+
+  unitsfile = findunitsfile(NOERRMSG);
+
+  nlines = countlines(msg);
+  /* see if we need a pager */
+  fp = get_output_fp(nlines + 4);
+
+  fprintf(fp, msg, progname, DEFAULTPRECISION, DEFAULTPRECISION, DEFAULTTYPE);
+  if (!unitsfile)
+    fprintf(fp, "Units data file '%s' not found.\n\n", UNITSFILE);
+  else
+    fprintf(fp, "\nTo learn about the available units look in '%s'\n\n", unitsfile);
+  fputs("Report bugs to adrianm@gnu.org.\n\n", fp);
+
+  if (fp != stdout)
+    pclose(fp);
+}
+
+/* Print message about how to get help */
+
+void 
+helpmsg()
+{
+  fprintf(stderr,"\nTry '%s --help' for more information.\n",progname);
+  exit(EXIT_FAILURE);
+}
+
+/* show units version, and optionally, additional information */
+void
+printversion()
+{
+  int exists;
+  char *u_unitsfile = NULL;  /* units data file specified in UNITSFILE */
+  char *m_unitsfile;         /* personal units data file from HOME_UNITS_ENV */
+  char *p_unitsfile;         /* personal units data file */
+  FILE *fp, *histfile;
+#ifdef _WIN32
+  char *localemap;
+#endif
+
+  if (flags.verbose == 0) {
+    printf("GNU Units version %s\n", VERSION);
+    return;
+  }
+
+  printf("GNU Units version %s\n%s, %s, locale %s\n",
+         VERSION, RVERSTR,UTF8VERSTR,mylocale);
+#if defined (_WIN32) && defined (HAVE_MKS_TOOLKIT)
+  puts("With MKS Toolkit");
+#endif
+
+  if (flags.verbose == 2) {
+    if (!fullprogname)
+      getprogdir(progname, &fullprogname);
+    if (fullprogname)
+      printf("\n%s program is %s\n", progname, fullprogname);
+  }
+
+  /* units data file */
+
+  putchar('\n');
+  if (isfullpath(UNITSFILE))
+    printf("Default units data file is '%s'\n", UNITSFILE);
+  else
+    printf("Default units data file is '%s';\n  %s will search for this file\n",
+           UNITSFILE, progname);
+  if (flags.verbose < 2)
+    printf("Default personal units file: %s\n", homeunitsfile);
+
+  if (flags.verbose == 2){
+    u_unitsfile = getenv("UNITSFILE");
+    if (u_unitsfile)
+      printf("Environment variable UNITSFILE set to '%s'\n", u_unitsfile);
+    else
+      puts("Environment variable UNITSFILE not set");
+
+    unitsfiles[0] = findunitsfile(SHOWFILES);
+    
+    if (unitsfiles[0]) {
+      /* We searched for the file in program and data dirs */
+      if (!isfullpath(UNITSFILE) && !nonempty(u_unitsfile))
+        printf("Found data file '%s'\n", unitsfiles[0]);
+      else
+        printf("Units data file is '%s'\n", unitsfiles[0]);
+    } 
+    else {
+      if (errno && (nonempty(u_unitsfile) || isfullpath(UNITSFILE)))
+        printf("*** Units data file invalid: %s ***\n",strerror(errno));
+      else
+        puts("*** Units data file not found ***");
+    }
+    if (homedir_error)
+      printf("\n%s\n", homedir_error);
+    else
+      printf("\nHome directory is '%s'\n", homedir);
+  }
+
+  /* personal units data file: environment */
+  if (flags.verbose == 2){
+    m_unitsfile = getenv(HOME_UNITS_ENV);
+    putchar('\n');
+    if (m_unitsfile) {
+      printf("Environment variable %s set to '%s'\n",
+             HOME_UNITS_ENV,m_unitsfile);
+    }
+    else
+      printf("Environment variable %s not set\n", HOME_UNITS_ENV);
+
+    p_unitsfile = personalfile(HOME_UNITS_ENV, homeunitsfile, 1, &exists);
+    if (p_unitsfile) {
+      printf("Personal units data file is '%s'\n", p_unitsfile);
+      if (!exists){
+        if (homedir_error && !nonempty(m_unitsfile))
+          printf("  (File invalid: %s)\n", homedir_error);
+        else if (errno==ENOENT && !nonempty(m_unitsfile))
+          puts("  (File does not exist)");
+        else 
+          printf("  (File invalid: %s)\n",strerror(errno));
+      }  
+    }
+    else
+      puts("Personal units data file not found: no home directory");
+  }
+#ifdef READLINE
+  if (flags.verbose == 2) {
+    historyfile = personalfile(NULL,HISTORY_FILE,1,&exists);
+    if (historyfile){
+      printf("\nDefault readline history file is '%s'\n", historyfile);
+      histfile = openfile(historyfile,"r+");
+      if (!histfile)
+        printf("  (File invalid: %s)\n",
+               homedir_error ? homedir_error : strerror(errno));
+      else
+        fclose(histfile);
+    }  
+    else 
+      puts("\nReadline history file unusable: no home directory");
+  }
+#endif
+
+#ifdef _WIN32
+  /* locale map */
+  if (flags.verbose == 2) {
+    putchar('\n');
+    localemap = getenv("UNITSLOCALEMAP");
+    if (localemap)
+      printf("Environment variable UNITSLOCALEMAP set to '%s'\n", localemap);
+    else
+      puts("Environment variable UNITSLOCALEMAP not set");
+
+    if (isfullpath(LOCALEMAP))
+      printf("Default locale map is '%s'\n", LOCALEMAP);
+    else
+      printf("Default locale map is '%s';\n  %s will search for this file\n",
+             LOCALEMAP, progname);
+
+    localemap = findlocalemap(SHOWFILES);
+    if (localemap && !isfullpath(LOCALEMAP))
+      printf("Found locale map '%s'\n", localemap);
+    else if (localemap)
+      printf("Locale map is '%s'\n", localemap);
+    else
+      puts("*** Locale map not found ***");
+  }
+#endif
+
+  printf("\n%s\n\n", LICENSE);
+}
+
+void
+showunitsfile()
+{
+  char *unitsfile;
+  unitsfile = findunitsfile(NOERRMSG);
+  if (unitsfile)
+    printf("%s\n", unitsfile);
+  else
+    puts("Units data file not found");
+}
+
+
+char *shortoptions = "VIUu:vqechSstf:o:d:mnpr1l:L:"
+#ifdef READLINE
+    "H:"
+#endif
+  ;
+
+struct option longoptions[] = {
+  {"check", no_argument, &flags.unitcheck, 1},
+  {"check-verbose", no_argument, &flags.unitcheck, 2},
+  {"compact", no_argument, &flags.verbose, 0},
+  {"digits", required_argument, 0, 'd'},
+  {"exponential", no_argument, 0, 'e'},
+  {"file", required_argument, 0, 'f'},
+  {"help", no_argument, 0, 'h'},
+#ifdef READLINE  
+  {"history", required_argument, 0, 'H'},
+#endif  
+  {"info", no_argument, 0, 'I'},
+  {"locale", required_argument, 0, 'l'}, 
+  {"log", required_argument, 0, 'L'}, 
+  {"minus", no_argument, &parserflags.minusminus, 1},
+  {"newstar", no_argument, &parserflags.oldstar, 0},
+  {"nolists", no_argument, 0, 'n'},
+  {"oldstar", no_argument, &parserflags.oldstar, 1},
+  {"one-line", no_argument, &flags.oneline, 1},
+  {"output-format", required_argument, 0, 'o'},
+  {"product", no_argument, &parserflags.minusminus, 0},
+  {"quiet", no_argument, &flags.quiet, 1},
+  {"round",no_argument, 0, 'r'},
+  {"show-factor", no_argument, 0, 'S'},
+  {"conformable", no_argument, &flags.showconformable, 1 },
+  {"silent", no_argument, &flags.quiet, 1},
+  {"strict",no_argument,&flags.strictconvert, 1},
+  {"terse",no_argument, 0, 't'},
+  {"unitsfile", no_argument, 0, 'U'},
+  {"units", required_argument, 0, 'u'},
+  {"verbose", no_argument, &flags.verbose, 2},
+  {"verbose-check", no_argument, &flags.unitcheck, 2},
+  {"version", no_argument, 0, 'V'},
+  {0,0,0,0} };
+
+/* Process the args.  Returns 1 if interactive mode is desired, and 0
+   for command line operation.  If units appear on the command line
+   they are returned in the from and to parameters. */
+
+int
+processargs(int argc, char **argv, char **from, char **to)
+{
+   optarg = 0;
+   optind = 0;
+   int optchar, optindex;
+   int ind;
+   int doprintversion=0;
+   char *unitsys=0, *temp;
+
+   while ( -1 != 
+      (optchar = 
+         getopt_long(argc, argv,shortoptions,longoptions, &optindex ))) {
+      switch (optchar) {
+         case 'm':
+            parserflags.minusminus = 1;
+            break;
+         case 'p':
+            parserflags.minusminus = 0;
+            break;
+         case 't':
+            flags.oneline = 1;
+            flags.quiet = 1;
+            flags.strictconvert = 1;
+            flags.verbose = 0;
+            break;
+
+         /* numeric output format */
+         case 'd':
+            if (checksigdigits(optarg) < 0)
+              exit(EXIT_FAILURE);
+            else    /* ignore anything given with 'o' option */
+              num_format.format = NULL; 
+            break;
+         case 'e':  /* ignore anything given with 'o' option */
+            num_format.format = NULL;   
+            num_format.type = 'e';
+            break;
+         case 'o':
+            num_format.format = optarg;
+            break;
+
+         case 'c':
+            flags.unitcheck = 1;
+            break;
+         case 'f':
+            for(ind=0;unitsfiles[ind];ind++); 
+            if (ind==MAXFILES){
+              fprintf(stderr, "At most %d -f specifications are allowed\n",
+                      MAXFILES);
+              exit(EXIT_FAILURE);
+            }
+            if (optarg && *optarg)
+              unitsfiles[ind] = optarg;
+            else {
+              unitsfiles[ind] = findunitsfile(ERRMSG);
+              if (!unitsfiles[ind]) 
+                exit(EXIT_FAILURE);
+            }
+            unitsfiles[ind+1] = 0;
+            break;
+         case 'L':
+            logfilename = optarg;
+            break;
+         case 'l':
+            mylocale = optarg;
+            break;
+         case 'n':
+            flags.unitlists = 0;
+            break;
+         case 'q':
+            flags.quiet = 1;
+            break; 
+         case 'r':
+            flags.round = 1;
+            break;
+         case 'S':
+            flags.showfactor = 1;
+            break;
+         case 's':
+            flags.strictconvert = 1;
+            break;
+         case 'v':
+            flags.verbose = 2;
+            break;
+         case '1':
+            flags.oneline = 1;
+            break;
+         case 'I':
+            flags.verbose = 2;  /* fall through */
+         case 'V':
+            doprintversion = 1;
+            break;
+         case 'U':
+            showunitsfile();
+            exit(EXIT_SUCCESS);
+            break;
+         case 'u':
+            unitsys = optarg;
+            for(ind=0;unitsys[ind];ind++)
+              unitsys[ind] = tolower(unitsys[ind]);
+            break;
+         case 'h':
+            usage();
+            exit(EXIT_SUCCESS);
+#ifdef READLINE     
+         case 'H':
+           if (emptystr(optarg))
+              historyfile=NULL;
+            else
+              historyfile = optarg;
+            break;
+#endif      
+         case 0: break;  /* This is reached if a long option is 
+                            processed with no return value set. */
+         case '?':        /* Invalid option or missing argument returns '?' */
+         default:
+            helpmsg();    /* helpmsg() exits with error */
+      } 
+   }
+
+   temp = strchr(mylocale,'.');
+   if (temp)
+     *temp = '\0';
+
+   if (doprintversion){
+     printversion();
+     exit(EXIT_SUCCESS);
+   }
+
+   /* command-line option overwrites environment */
+   if (unitsys)
+     setenv("UNITS_SYSTEM", unitsys, 1);
+
+   if (flags.unitcheck) {
+     if (optind != argc){
+       fprintf(stderr, 
+               "Too many arguments (arguments are not allowed with -c).\n");
+       helpmsg();         /* helpmsg() exits with error */
+     }
+   } else {
+     if (optind == argc - 2) {
+        if (flags.showconformable) {
+          fprintf(stderr,"Too many arguments (only one unit expression allowed with '--conformable').\n");
+          helpmsg();             /* helpmsg() exits with error */
+        }
+        flags.quiet=1;
+        *from = argv[optind];
+        *to = dupstr(argv[optind+1]); /* This string may get rewritten later */
+        return 0;                     /* and we might call free() on it      */
+     }
+
+     if (optind == argc - 1) {
+        flags.quiet=1;
+        *from = argv[optind];
+        *to=0;
+        return 0;
+     }
+     if (optind < argc - 2) {
+        fprintf(stderr,"Too many arguments (maybe you need quotes).\n");
+        helpmsg();             /* helpmsg() exits with error */
+     }
+   }
+
+   return 1;
+}
+
+/* 
+   Show a pointer under the input to indicate a problem.
+   Prints 'position' spaces and then the pointer.
+   If 'position' is negative, nothing is printed.
+ */
+
+void
+showpointer(int position)
+{
+  if (position >= 0){
+    while (position--) putchar(' ');
+    puts(POINTER);
+  }
+} /* end showpointer */
+
+
+/*
+   Process the string 'unitstr' as a unit, placing the processed data
+   in the unit structure 'theunit'.  Returns 0 on success and 1 on
+   failure.  If an error occurs an error message is printed to stdout.
+   A pointer ('^') will be printed if an error is detected, and  promptlen 
+   should be set to the printing width of the prompt string, or set 
+   it to NOPOINT to supress printing of the pointer.  
+ */
+
+ 
+int 
+processunit(struct unittype *theunit, char *unitstr, int promptlen)
+{
+  char *errmsg;
+  int errloc,err;
+  char savechar;
+
+  if (flags.unitlists && strchr(unitstr, UNITSEPCHAR)){
+    puts("Unit list not allowed");
+    return 1;
+  }
+  if ((err=parseunit(theunit, unitstr, &errmsg, &errloc))){
+    if (promptlen >= 0){
+      if (err!=E_UNKNOWNUNIT || !irreducible){
+        if (errloc>0) {
+          savechar = unitstr[errloc];
+          unitstr[errloc] = 0;
+          showpointer(promptlen+strwidth(unitstr)-1);
+          unitstr[errloc] = savechar;
+        } 
+        else showpointer(promptlen);
+      }
+    }
+    else
+      printf("Error in '%s': ", unitstr);
+    fputs(errmsg,stdout);
+    if (err==E_UNKNOWNUNIT && irreducible)
+      printf(" '%s'", irreducible);
+    putchar('\n');
+    return 1;
+  }
+  if ((err=completereduce(theunit))){
+    fputs(errormsg[err],stdout);
+    if (err==E_UNKNOWNUNIT)
+      printf(" '%s'", irreducible);
+    putchar('\n');
+    return 1;
+  }
+  return 0;
+}
+
+
+/* Checks for a new unit defined on the prompt with the form _<NAME> = <DEF> */
+
+int
+definevariable(char *def, int promptlen)
+{
+  int dummy, err;
+  struct unittype unit;
+  
+  char *value = strchr(def,'=');
+  if (!value)
+    return 0;
+  *value++=0;
+  if (processunit(&unit, value, promptlen + (value-def)))
+      return 1;
+  removespaces(def);
+  removespaces(value);
+  err = *def!='_' || newunit(def,value, &dummy, 0, 0, NULL, 1, 1);
+  if (err)
+    printf("Invalid variable name: %s\n", def);
+  return 1;
+}  
+
+
+
+
+/*
+   Checks the input parameter unitstr (a list of units separated by
+   UNITSEPCHAR) for errors.  All units must be parseable and
+   conformable to each other.  Returns 0 on success and 1 on failure.
+
+   If an error is found then print an error message on stdout.  A
+   pointer ('^') will be printed to mark the error.  The promptlen
+   parameter should be set to the printing width of the prompt string
+   so that the pointer is correctly aligned.
+
+   To suppress the printing of the pointer set promptlen to NOPOINT.
+   To suppress printing of error messages entirely set promptlen to 
+   NOERRMSG.
+*/
+
+
+int
+checkunitlist(char *unitstr, int promptlen)
+{
+  struct unittype unit[2], one;
+  char *firstunitstr,*nextunitstr;
+  int unitidx = 0;
+
+  int printerror = promptlen != NOERRMSG;
+
+  initializeunit(&one);
+
+  firstunitstr = unitstr;
+
+  initializeunit(unit);
+  initializeunit(unit+1);
+
+  while (unitstr) {
+    if ((nextunitstr = strchr(unitstr, UNITSEPCHAR)) != 0)
+      *nextunitstr = '\0';
+
+    if (!unitstr[strspn(unitstr, " ")]) {  /* unitstr is blank */
+      if (!nextunitstr) {  /* terminal UNITSEPCHAR indicates repetition */
+        freeunit(unit);    /* of last unit and is permitted */
+        return 0;
+      }
+      else {               /* internal blank units are not allowed */
+        if (printerror){
+          showpointer(promptlen);
+          puts("Error: blank unit not allowed");
+        }
+        freeunit(unit);
+        return 1;
+      }
+    }
+
+    /* processunit() prints error messages; avoid it to supress them */
+
+    if ((printerror && processunit(unit+unitidx,unitstr,promptlen)) ||
+        (!printerror && 
+           (parseunit(unit+unitidx, unitstr,0,0) 
+             || completereduce(unit+unitidx) 
+             || compareunits(unit+unitidx,&one, ignore_primitive)))){
+      if (printerror)
+        printf("Error in unit list entry: %s\n",unitstr);
+      freeunit(unit);
+      freeunit(unit+1);
+      return 1;
+    }
+
+
+    if (unitidx == 0)
+      unitidx = 1;
+    else {
+      if (compareunits(unit, unit+1, ignore_dimless)){
+        if (printerror){
+          int wasverbose = flags.verbose;
+          FILE *savelog = logfile;
+          logfile=0;
+          flags.verbose = 2;   /* always use verbose form to be unambiguous */
+                               /* coverity[returned_null] */
+          *(strchr(firstunitstr, UNITSEPCHAR)) = '\0';
+          removespaces(firstunitstr);
+          removespaces(unitstr);
+          showpointer(promptlen);
+          showconformabilityerr(firstunitstr, unit, unitstr, unit+1);
+          flags.verbose = wasverbose;
+          logfile = savelog;
+        }
+        freeunit(unit);
+        freeunit(unit+1);
+        return 1;
+      }
+      freeunit(unit+1);
+    }
+
+    if (nextunitstr) {
+      if (promptlen >= 0) promptlen += strwidth(unitstr)+1;
+      *(nextunitstr++) = UNITSEPCHAR;
+    }
+    unitstr = nextunitstr;
+  }
+
+  freeunit(unit);
+
+  return 0;
+} /* end checkunitlist */
+
+
+/*
+   Call either processunit or checkunitlist, depending on whether the
+   string 'unitstr' contains a separator character.  Returns 0 on
+   success and 1 on failure.  If an error occurs an error message is
+   printed to stdout.
+
+   A pointer will be printed if an error is detected, and  promptlen 
+   should be set to the printing width of the prompt string, or set 
+   it to NOPOINT to supress printing of the pointer.  
+*/
+
+int
+processwant(struct unittype *theunit, char *unitstr, int promptlen)
+{
+  if (flags.unitlists && strchr(unitstr, UNITSEPCHAR))
+    return checkunitlist(unitstr, promptlen);
+  else
+    return processunit(theunit, unitstr, promptlen);
+}
+
+
+void
+checkallaliases(int verbose)
+{
+  struct wantalias *aliasptr;
+
+  for(aliasptr = firstalias; aliasptr; aliasptr=aliasptr->next){
+    if (verbose)
+      printf("doing unit list '%s'\n", aliasptr->name);
+    if (checkunitlist(aliasptr->definition,NOERRMSG))
+      printf("Unit list '%s' contains errors\n", aliasptr->name);
+    if (ulookup(aliasptr->name))
+      printf("Unit list '%s' hides a unit definition.\n", aliasptr->name);
+    if (fnlookup(aliasptr->name))
+      printf("Unit list '%s' hides a function definition.\n", aliasptr->name);
+  }
+}
+
+
+
+/* 
+   Check that all units and prefixes are reducible to primitive units and that
+   function definitions are valid and have correct inverses.  A message is
+   printed for every unit that does not reduce to primitive units.
+
+*/
+
+
+void 
+checkunits(int verbosecheck)
+{
+  struct unittype have,second,one;
+  struct unitlist *uptr;
+  struct prefixlist *pptr;
+  struct func *funcptr;
+  char *prefixbuf, *testunit;
+  int i, reduce_err;
+  char *err;
+
+  initializeunit(&one);
+
+  /* Check all functions for valid definition and correct inverse */
+  
+  for(i=0;i<SIMPLEHASHSIZE;i++)
+    for(funcptr=ftab[i];funcptr;funcptr=funcptr->next)
+      checkfunc(funcptr, verbosecheck);
+
+  checkallaliases(verbosecheck);
+
+  /* Now check all units for validity */
+
+  for(i=0;i<HASHSIZE;i++)
+    for (uptr = utab[i]; uptr; uptr = uptr->next){
+      if (verbosecheck)
+        printf("doing '%s'\n",uptr->name);
+      if (strchr(uptr->value, PRIMITIVECHAR))
+        continue; 
+      else if (fnlookup(uptr->name)) 
+          printf("Unit '%s' hidden by function '%s'\n", uptr->name, uptr->name);
+      else if (parseunit(&have, uptr->value,&err,0))
+          printf("'%s' defined as '%s': %s\n",uptr->name, uptr->value,err);
+      else if ((reduce_err=completereduce(&have))){
+          printf("'%s' defined as '%s' irreducible: %s",uptr->name, uptr->value,errormsg[reduce_err]);
+          if (reduce_err==E_UNKNOWNUNIT && irreducible)
+            printf(" '%s'", irreducible);
+          putchar('\n');
+      }
+      else {
+        parserflags.minusminus = !parserflags.minusminus; 
+                                                 /* coverity[check_return] */
+        parseunit(&second, uptr->name, 0, 0);    /* coverity[check_return] */
+        completereduce(&second);     /* Can't fail because it worked above */
+        if (compareunits(&have, &second, ignore_nothing)){
+          printf("'%s': replace '-' with '+-' for subtraction or '*' to multiply\n", uptr->name);
+        }
+        freeunit(&second);
+        parserflags.minusminus=!parserflags.minusminus;
+      }
+
+      freeunit(&have);
+    }
+
+  /* Check prefixes */ 
+
+  testunit="meter";
+  for(i=0;i<SIMPLEHASHSIZE;i++)
+    for(pptr = ptab[i]; pptr; pptr = pptr->next){
+      if (verbosecheck)
+        printf("doing '%s-'\n",pptr->name);
+      prefixbuf = mymalloc(strlen(pptr->name) + strlen(testunit) + 1,
+                           "(checkunits)");
+      strcpy(prefixbuf,pptr->name);
+      strcat(prefixbuf,testunit);
+      if (parseunit(&have, prefixbuf,0,0) || completereduce(&have) || 
+          compareunits(&have,&one,ignore_primitive))
+        printf("'%s-' defined as '%s' irreducible\n",pptr->name, pptr->value);
+      else { 
+        int plevel;    /* check for bad '/' character in prefix */
+        char *ch;
+        plevel = 0;
+        for(ch=pptr->value;*ch;ch++){
+          if (*ch==')') plevel--;
+          else if (*ch=='(') plevel++;
+          else if (plevel==0 && *ch=='/'){
+            printf(
+              "'%s-' defined as '%s' contains a bad '/'. (Add parentheses.)\n",
+              pptr->name, pptr->value);
+            break;
+          }
+        }           
+      }  
+      freeunit(&have);
+      free(prefixbuf);
+    }
+}
+
+
+/*
+   Converts the input value 'havestr' (which is already parsed into
+   the unit structure 'have') into a sum of the UNITSEPCHAR-separated
+   units listed in 'wantstr'.  You must call checkunitlist first to
+   ensure 'wantstr' is error-free.  Prints the results (or an error message)
+   on stdout.  Returns 0 on success and 1 on failure.
+*/
+
+int
+showunitlist(char *havestr, struct unittype *have, char *wantstr)
+{
+  struct unittype want, lastwant;
+  char *lastunitstr, *nextunitstr, *lastwantstr=0;
+  double remainder;     /* portion of have->factor remaining */
+  double round_dir;     /* direction of rounding */
+  double value;         /* value (rounded to integer with 'r' option) */
+  int firstunit = 1;    /* first unit in a multi-unit string */
+  int value_shown = 0;  /* has a value been shown? */
+  int sigdigits;
+  char val_sign;  
+
+  initializeunit(&want);
+  remainder = fabs(have->factor);
+  val_sign = have->factor < 0 ? '-' : '+';
+  lastunitstr = 0;
+  nextunitstr = 0;
+  round_dir = 0;
+
+  if (flags.round) {
+    /* disable unit repetition with terminal UNITSEPCHAR when rounding */
+    if (lastchar(wantstr) == UNITSEPCHAR)
+      lastchar(wantstr) = 0;
+    if ((lastwantstr = strrchr(wantstr, UNITSEPCHAR)))
+      lastwantstr++;
+  }
+
+  while (wantstr) {
+    if ((nextunitstr = strchr(wantstr, UNITSEPCHAR)))
+      *(nextunitstr++) = '\0';
+    removespaces(wantstr);
+
+    /*
+      if wantstr ends in UNITSEPCHAR, repeat last unit--to give integer
+      and fractional parts (3 oz + 0.371241 oz rather than 3.371241 oz)
+    */
+    if (emptystr(wantstr))              /* coverity[alias_transfer] */
+      wantstr = lastunitstr;
+
+    if (processunit(&want, wantstr, NOPOINT)) {
+      freeunit(&want);
+      return 1;
+    }
+
+    if (firstunit){
+      /* checkunitlist() ensures conformability within 'wantstr',
+         so we just need to check the first unit to see if it conforms
+         to 'have' */
+      if (compareunits(have, &want, ignore_dimless)) {
+        showconformabilityerr(havestr, have, wantstr, &want);
+        freeunit(&want);
+        return 1;
+      }
+
+      /* round to nearest integral multiple of last unit */
+      if (flags.round) {
+        value = remainder;
+        if (nonempty(lastwantstr)) {      /* more than one unit */
+          removespaces(lastwantstr);
+          initializeunit(&lastwant);
+          if (processunit(&lastwant, lastwantstr, NOPOINT)) {
+            freeunit(&lastwant);
+            return 1;
+          }
+          remainder = round(remainder / lastwant.factor) * lastwant.factor;
+        }
+        else    /* first unit is last unit */
+          remainder = round(remainder / want.factor) * want.factor;
+
+        round_dir = remainder - value;
+      }
+      if (flags.verbose == 2) {
+        removespaces(havestr);
+        logprintf("\t%s = ", havestr);
+      } else if (flags.verbose == 1)
+        logputchar('\t');
+    } /* end if first unit */
+
+    if (0==(sigdigits = getsigdigits(have->factor, remainder, 10)))
+      break;    /* nothing left */
+
+    /* Remove sub-precision junk accumulating in the remainder.  Rounding
+       is base 2 to ensure that we keep all valid bits. */
+    remainder = round_digits(remainder,
+                             getsigdigits(have->factor,remainder,2),2);
+    if (nextunitstr)
+      remainder = want.factor * modf(remainder / want.factor, &value);
+    else
+      value = remainder / want.factor;
+
+    /* The remainder represents less than one of the current want unit.
+       But with display rounding it may round up to 1, leading to an output
+       like "4 feet + 12 inch".  Check for this case and if the remainder 
+       indeed rounds up to 1 then add that remainder into the current unit 
+       and set the remainder to zero. */
+    if (nextunitstr){
+      double rounded_next =
+          round_digits(remainder/want.factor,
+                       getsigdigits(have->factor, remainder / want.factor, 10),
+                       10);
+      if (displays_as(1,rounded_next, NULL)){
+        value++;               
+        remainder = 0;         /* Remainder is zero */
+      }
+    }
+
+    /* Round the value to significant digits to prevent display
+       of bogus sub-precision decimal digits */
+    value = round_digits(value,sigdigits,10);
+
+    /* This ensures that testing value against zero will work below
+       at the last unit, which is the only case where value is not integer */
+    if (!nextunitstr && displays_as(0, value, NULL))
+      value=0;
+
+    if (!flags.verbose){
+      if (!firstunit) 
+        logputchar(UNITSEPCHAR);
+      logprintf(num_format.format,value);
+      value_shown=1;
+    } else { /* verbose case */
+      if (value != 0) {
+        if (value_shown) /* have already displayed a number so need a '+' */
+          logprintf(" %c ",val_sign);
+        else if (val_sign=='-')
+          logputs("-");
+        showunitname(value, wantstr, PRINTNUM);
+        if (sigdigits <= floor(log10(value))+1)   /* Used all sig digits */
+          logprintf(" (at %d-digit precision limit)", DBL_DIG);
+        value_shown=1;
+      }
+    }
+    
+    freeunit(&want);
+    lastunitstr = wantstr;
+    wantstr = nextunitstr;
+    firstunit = 0;
+  }
+
+  /* if the final unit value was rounded print indication */
+  if (!value_shown) {  /* provide output if every value rounded to zero */
+    logputs("0 ");
+    if (isdecimal(*lastunitstr))
+      logputs("* ");
+    logputs(lastunitstr);
+  }
+
+  if (round_dir != 0) {
+    if (flags.verbose){
+      if (round_dir > 0)
+        logprintf(" (rounded up to nearest %s) ", lastunitstr);
+      else
+        logprintf(" (rounded down to nearest %s) ", lastunitstr);
+    } else 
+      logprintf("%c%c", UNITSEPCHAR, round_dir > 0 ?'-':'+');   
+  }
+  logputchar('\n');
+  return 0;
+} /* end showunitlist */
+
+
+#if defined (_WIN32) && defined (HAVE_MKS_TOOLKIT)
+int
+ismksmore(char *pager)
+{
+    static int mksmore = -1;
+
+    if (mksmore >= 0)
+      return mksmore;
+
+    /*
+      Tries to determine whether the MKS Toolkit version of more(1) or
+      less(1) will run.  In older versions of the Toolkit, neither
+      accepted '+<lineno>', insisting on the POSIX-compliant '+<lineno>g'.
+    */
+    if (strstr(pager, "more") || strstr(pager, "less")) {
+      char *mypager, *mkspager, *mksroot, *p;
+      char pathbuf[FILENAME_MAX + 1];
+      struct _stat mybuf, mksbuf;
+
+      mypager = NULL;
+      mkspager = NULL;
+      mksmore = 0;
+      if (strlen(pager) > FILENAME_MAX) {
+        fprintf(stderr, "%s: cannot invoke pager--value '%s' in PAGER too long\n",
+                progname, pager);
+        return 0;       /* TODO: this really isn't the right value */
+      }
+      else if (!isfullpath(pager)) {
+        mypager = (char *) mymalloc(strlen(pager) + strlen(EXE_EXT) + 1, "(ishelpquery)");
+        strcpy(mypager, pager);
+        if (!((p = strrchr(mypager, '.')) && isexe(p)))
+          strcat(mypager, EXE_EXT);
+
+        _searchenv(mypager, "PATH", pathbuf);
+      }
+      else
+        strcpy(pathbuf, pager);
+
+      mksroot = getenv("ROOTDIR");
+      if (mksroot) {
+        char * mksprog;
+
+        if (strstr(pager, "more"))
+          mksprog = "more.exe";
+        else
+          mksprog = "less.exe";
+        mkspager = (char *) mymalloc(strlen(mksroot) + strlen("/mksnt/") + strlen(mksprog) + 1,
+                                     "(ishelpquery)");
+        strcpy(mkspager, mksroot);
+        strcat(mkspager, "\\mksnt\\");
+        strcat(mkspager, mksprog);
+      }
+
+      if (*pathbuf && mksroot) {
+        if (_stat(mkspager, &mksbuf)) {
+          fprintf(stderr, "%s: cannot stat file '%s'. ", progname, mkspager);
+          perror((char *) NULL);
+          return 0;
+        }
+        if (_stat(pathbuf, &mybuf)) {
+          fprintf(stderr, "%s: cannot stat file '%s'. ", progname, pathbuf);
+          perror((char *) NULL);
+          return 0;
+        }
+        /*
+          if we had inodes, this would be simple ... but if it walks
+          like a duck and swims like a duck and quacks like a duck ...
+        */
+        if (mybuf.st_size   == mksbuf.st_size
+          && mybuf.st_ctime == mksbuf.st_ctime
+          && mybuf.st_mtime == mksbuf.st_mtime
+          && mybuf.st_atime == mksbuf.st_atime
+          && mybuf.st_mode  == mksbuf.st_mode)
+            mksmore = 1;
+      }
+      if (mypager)
+        free(mypager);
+      if (mkspager)
+        free(mkspager);
+    }
+
+    return mksmore;
+}
+#endif
+
+/* 
+   Checks to see if the input string contains HELPCOMMAND possibly
+   followed by a unit name on which help is sought.  If not, then
+   return 0.  Otherwise invoke the pager on units file at the line
+   where the specified unit is defined.  Then return 1.  */
+
+int
+ishelpquery(char *str, struct unittype *have)
+{
+  struct unitlist *unit;
+  struct func *function;
+  struct wantalias *alias;
+  struct prefixlist *prefix;
+  char commandbuf[1000];  /* Hopefully this is enough overkill as no bounds */
+  int unitline;           /* checking is performed. */
+  char *file;
+  char **exitptr;
+  char *commandstr;	  /* command string varies with OS */
+  
+  if (have && !strcmp(str, UNITMATCH)){
+    tryallunits(have,0);
+    return 1;
+  }
+  for(exitptr=exit_commands;*exitptr;exitptr++)
+    if (!strcmp(str, *exitptr))
+      exit(EXIT_SUCCESS);
+  if (startswith(str, SEARCHCOMMAND)){
+    str+=strlen(SEARCHCOMMAND);
+    if (!emptystr(str) && *str != ' ')
+      return 0;
+    removespaces(str);
+    if (emptystr(str)){
+      printf("\n\
+Type 'search text' to see a list of all unit names \n\
+containing 'text' as a substring\n\n");
+      return 1;
+    }
+    tryallunits(0,str);
+    return 1;
+  }
+  if (startswith(str, HELPCOMMAND)){
+    str+=strlen(HELPCOMMAND);
+    if (!emptystr(str) && *str != ' ')
+      return 0;
+    removespaces(str);
+
+    if (emptystr(str)){
+      int nlines;
+      char *unitsfile;
+      char *msg = "\n\
+%s converts between different measuring systems and    %s6 inches\n\
+acts as a units-aware calculator.  At the '%s'    %scm\n\
+prompt, type in the units you want to convert from or             * 15.24\n\
+an expression to evaluate.  At the '%s' prompt,           / 0.065\n\
+enter the units to convert to or press return to see\n\
+the reduced form or definition.                           %stempF(75)\n\
+                                                          %stempC\n\
+The first example shows that 6 inches is about 15 cm              23.889\n\
+or (1/0.065) cm.  The second example shows how to\n\
+convert 75 degrees Fahrenheit to Celsius.  The third      %sbu^(1/3)\n\
+example converts the cube root of a bushel to a list      %sft;in\n\
+of semicolon-separated units.                                     1 ft + 0.9 in\n\
+\n\
+To quit from %s type 'quit' or 'exit'.       %s2 btu + 450 ft lbf\n\
+                                                %s(kg^2/s)/(day lb/m^2)\n\
+At the '%s' prompt type '%s' to get a            * 1.0660684e+08\n\
+list of conformable units.  At either prompt you        / 9.3802611e-09\n\
+type 'help myunit' to browse the units database\n\
+and read the comments relating to myunit or see         %s6 tbsp sugar\n\
+other units related to myunit.  Typing 'search          %sg\n\
+text' will show units whose names contain 'text'.               * 75\n\
+                                                                / 0.013333333\n\n";
+    char *fmsg = "To learn about the available units look in '%s'\n\n";
+    FILE *fp;
+
+    /* presumably, this cannot fail because it was already checked at startup */
+    unitsfile = findunitsfile(NOERRMSG);
+
+    nlines = countlines(msg);
+    /* but check again anyway ... */
+    if (unitsfile)
+      nlines += countlines(fmsg);
+
+    /* see if we need a pager */
+    fp = get_output_fp(nlines);
+
+    fprintf(fp, msg, 
+      progname, QUERYHAVE,
+      QUERYHAVE, QUERYWANT,
+      QUERYWANT,
+      QUERYHAVE,QUERYWANT,QUERYHAVE,QUERYWANT,
+      progname, QUERYHAVE,QUERYWANT,
+      QUERYWANT,
+      UNITMATCH,
+      QUERYHAVE,QUERYWANT);
+
+      if (unitsfile)
+        fprintf(fp, fmsg, unitsfile);
+
+      if (fp != stdout)
+        pclose(fp);
+
+      return 1;
+    }
+    if ((function = fnlookup(str))){
+      file = function->file;
+      unitline = function->linenumber;
+    }
+    else if ((unit = ulookup(str))){
+      unitline = unit->linenumber;
+      file = unit->file;
+    }
+    else if ((prefix = plookup(str)) && strlen(str)==prefix->len){
+      unitline = prefix->linenumber;
+      file = prefix->file;
+    }
+    else if ((alias = aliaslookup(str))){
+      unitline = alias->linenumber;
+      file = alias->file;
+    }
+    else {
+      printf("Unknown unit '%s'\n",str);
+      return 1;
+    }
+
+  /*
+    With Microsoft compilers, system() uses cmd.exe.
+    Inner escaped quotes are necessary for filenames with spaces; outer
+    escaped quotes are necessary for cmd.exe to see the command as a
+    single string containing one or more quoted strings
+    (e.g., cmd /c ""command" "arg1" "arg2" ... ")
+  */
+#if defined (_WIN32)
+  #if defined (HAVE_MKS_TOOLKIT)
+    if (strstr(pager, "pg"))
+      commandstr = "\"\"%s\" +%d \"%s\"\"";
+    else if (ismksmore(pager)) {
+      /*
+	use the POSIX-compliant '+<number>g' for compatibility with older
+	versions of the Toolkit that don't accept '+<number>'
+      */
+      commandstr = "\"\"%s\" +%dg \"%s\"\"";
+    }
+    else {
+      /*
+	more.com apparently takes the number as an offset rather than a
+	line number, so that '+1' starts at line 2 of the file.
+	more.com also cannot back up, so allow two lines of preceding
+	context.
+      */
+      unitline -= 3;
+      if (unitline < 0)
+	unitline = 0;
+      commandstr = "\"\"%s\"  +%d \"%s\"\"";
+    }
+  #else /* more.com is probably the only option */
+    unitline -= 3;
+    if (unitline < 0)
+      unitline = 0;
+    commandstr = "\"\"%s\"  +%d \"%s\"\"";
+  #endif
+#else	/* *nix */
+    commandstr = "%s +%d %s";
+#endif
+    sprintf(commandbuf, commandstr, pager, unitline, file);
+    if (system(commandbuf))
+      fprintf(stderr,"%s: cannot invoke pager '%s' to display help\n", 
+              progname, pager);
+    return 1;
+  }
+  return 0;
+}
+
+
+#ifdef SUPPORT_UTF8
+void
+checklocale()
+{
+  char *temp;
+  temp = setlocale(LC_CTYPE,"");
+  utf8mode = (strcmp(nl_langinfo(CODESET),"UTF-8")==0);
+  if (temp){
+    mylocale = dupstr(temp);
+    temp = strchr(mylocale,'.');
+    if (temp)
+      *temp = 0;
+  } else
+    mylocale = DEFAULTLOCALE;
+}
+
+#else
+
+void
+checklocale()
+{
+  char *temp=0;
+  /* environment overrides system */
+  temp = getenv("LC_CTYPE");
+  if (!temp)
+    temp = getenv("LC_ALL");
+  if (!temp)
+    temp = getenv("LANG");
+#ifndef NO_SETLOCALE  
+  if (temp)
+    temp = setlocale(LC_CTYPE,temp);
+  if (!temp)
+    temp = setlocale(LC_CTYPE,"");	/* try system default */
+#endif
+  if (!temp)
+    mylocale = DEFAULTLOCALE;
+  else {
+    mylocale = dupstr(temp);  
+    temp = strchr(mylocale,'.');
+    if (temp)
+      *temp = 0;
+  }
+}
+
+#endif
+
+/* 
+   Replaces an alias in the specified string input.  Returns 1 if the
+   alias that is found contains errors. 
+*/
+
+int
+replacealias(char **string, int *buflen)
+{
+  int usefree = 1;
+  struct wantalias *aliasptr;
+  char *input;
+
+  if (!flags.readline && buflen)
+    usefree = 0;
+
+  if (nonempty(*string)) {  /* check that string is defined and nonempty */
+    input = *string;
+    removespaces(input);
+    if ((aliasptr=aliaslookup(input))){
+      if (checkunitlist(aliasptr->definition,NOERRMSG)){
+        puts("Unit list definition contains errors.");
+        return 1;
+      }
+      if (usefree){
+        free(*string);
+        *string = dupstr(aliasptr->definition);
+      } else {                           /* coverity[dead_error_line] */
+        while (strlen(aliasptr->definition)>*buflen)
+          growbuffer(string, buflen);
+        strcpy(*string, aliasptr->definition);
+      }
+    }
+  }
+  return 0;
+}
+
+
+/* 
+   Remaps the locale name returned on Windows systems to the value
+   returned on Unix-like systems
+*/
+
+void
+remaplocale(char *filename)
+{
+  FILE *map;
+  char *value;
+  char name[80];
+
+  map = openfile(filename,"rt");
+  if (!map) {
+    fprintf(stderr,"%s: cannot open locale map '%s'. ",progname,filename);
+    perror((char *) NULL);
+  }
+  else {
+    while(!feof(map)){
+      if (!fgets(name,80,map))
+        break;
+      lastchar(name) = 0;
+      value=strchr(name,'#');
+      if (value) *value=0;
+      value=strchr(name,'\t');
+      if (!value) continue;
+      *value++=0;
+      removespaces(value);
+      removespaces(name);
+      if (!strcmp(name, mylocale))
+        mylocale = dupstr(value);
+    }
+    fclose(map);
+  }
+}
+
+
+void
+close_logfile(void)
+{
+  if (logfile){
+    fputc('\n',logfile);
+    fclose(logfile);
+  }
+}
+
+
+void
+open_logfile(void)
+{  
+  time_t logtime;
+  char * timestr;
+
+  logfile = openfile(logfilename, "at");
+  if (!logfile){
+    fprintf(stderr, "%s: cannot write to log file '%s'.  ", 
+            progname, logfilename);
+    perror(0);
+    exit(EXIT_FAILURE);
+  }
+  time(&logtime);
+  timestr = ctime(&logtime);
+  fprintf(logfile, "### Log started %s \n", timestr);
+  atexit(close_logfile);
+}
+
+void
+write_files_sig(int sig)
+{
+#ifdef READLINE
+  if (historyfile)
+    save_history();
+#endif
+  close_logfile();
+  signal(sig, SIG_DFL);
+  raise(sig);
+}
+
+
+
+int test_int(int a, int b)
+{
+    return a + b;
+}
+
+char *g_argv_0 = NULL;
+
+
+void conversion_setup();
+
+#define exit return
+int
+conversion_worker(char *havestr, char *wantstr)
+{
+    struct func *funcval;
+    struct wantalias *alias;
+    struct unittype have;
+    struct unittype want;
+
+    conversion_setup();
+
+    replacectrlchars(havestr);
+    if (wantstr)
+        replacectrlchars(wantstr);
+#ifdef SUPPORT_UTF8
+    if (strwidth(havestr)<0){
+       printf("Error: %s on input\n",invalid_utf8);
+       exit(EXIT_FAILURE);
+     }
+     if (wantstr && strwidth(wantstr)<0){
+       printf("Error: %s on input\n",invalid_utf8);
+       exit(EXIT_FAILURE);
+     }
+#endif
+    replace_operators(havestr);
+    removespaces(havestr);
+    if (wantstr) {
+        replace_operators(wantstr);
+        removespaces(wantstr);
+    }
+    if ((funcval = fnlookup(havestr))){
+        showfuncdefinition(funcval, FUNCTION);
+        exit(EXIT_SUCCESS);
+    }
+    if ((funcval = invfnlookup(havestr))){
+        showfuncdefinition(funcval, INVERSE);
+        exit(EXIT_SUCCESS);
+    }
+
+    if ((alias = aliaslookup(havestr))){
+        showunitlistdef(alias);
+        exit(EXIT_SUCCESS);
+    }
+    if (processunit(&have, havestr, NOPOINT))
+        exit(EXIT_FAILURE);
+    if (flags.showconformable == 1) {
+        tryallunits(&have,0);
+        exit(EXIT_SUCCESS);
+    }
+    if (!wantstr){
+        showdefinition(havestr,&have);
+        exit(EXIT_SUCCESS);
+    }
+    if (replacealias(&wantstr, 0)) /* the 0 says that we can free wantstr */
+        exit(EXIT_FAILURE);
+    if ((funcval = fnlookup(wantstr))){
+        if (showfunc(havestr, &have, funcval))  /* Clobbers have */
+            exit(EXIT_FAILURE);
+        else
+            exit(EXIT_SUCCESS);
+    }
+    if (processwant(&want, wantstr, NOPOINT))
+        exit(EXIT_FAILURE);
+    if (strchr(wantstr, UNITSEPCHAR)){
+        if (showunitlist(havestr, &have, wantstr))
+            exit(EXIT_FAILURE);
+        else
+            exit(EXIT_SUCCESS);
+    }
+    if (showanswer(havestr,&have,wantstr,&want))
+        exit(EXIT_FAILURE);
+    else
+        exit(EXIT_SUCCESS);
+}
+#undef exit
+
+
+void
+conversion_setup()
+{
+    static int setup = 0;
+
+    if (setup)
+        return;
+
+    setup = 1;
+
+    flags.quiet = 1;       /* Do not supress prompting */
+    flags.unitcheck = 0;   /* Unit checking is off */
+    flags.verbose = 2;     /* Medium verbosity */
+    flags.round = 0;       /* Rounding off */
+    flags.strictconvert=0; /* Strict conversion disabled (reciprocals active) */
+    flags.unitlists = 1;   /* Multi-unit conversion active */
+    flags.oneline = 1;     /* One line output is disabled */
+    flags.showconformable=0;  /* show unit conversion rather than all conformable units */
+    flags.showfactor = 0;  /* Don't show a multiplier for a 1|x fraction */
+    /*       in unit list output */
+    parserflags.minusminus = 1;  /* '-' character gives subtraction */
+    parserflags.oldstar = 0;     /* '*' has same precedence as '/' */
+
+    progname = getprogramname(g_argv_0);
+
+
+
+    if (!(isfullpath(UNITSFILE) && isfullpath(LOCALEMAP)))
+        progdir = getprogdir(g_argv_0, &fullprogname);
+    else {
+        progdir = NULL;
+        fullprogname = NULL;
+    }
+    datadir = getdatadir();      /* directory to search as last resort */
+
+    checklocale();
+
+    homedir = findhome(&homedir_error);
+
+    unitsfiles[0] = 0;
+
+    if (!unitsfiles[0]){
+        char *unitsfile;
+        unitsfile = findunitsfile(ERRMSG);
+        if (!unitsfile)
+            exit(EXIT_FAILURE);
+        else {
+            int file_exists;
+
+            unitsfiles[0] = unitsfile;
+            unitsfiles[1] = personalfile(HOME_UNITS_ENV,homeunitsfile,
+                                         0, &file_exists);
+            unitsfiles[2] = 0;
+        }
+    }
+
+    char **unitfileptr;
+    for(unitfileptr=unitsfiles;*unitfileptr;unitfileptr++){
+        int unitcount, prefixcount, funccount;
+        int readerr = readunits(*unitfileptr, stderr, &unitcount, &prefixcount,
+                                &funccount, 0);
+        if (readerr==E_MEMORY || readerr==E_FILE)
+            exit(EXIT_FAILURE);
+    }
+}
+
+
+void
+do_a_conversion(char *input, char *output)
+{
+    char *inp, *out;
+
+    char units[] = "./units";
+
+    g_argv_0 = units;
+
+    checklocale();
+
+    num_format.format = NULL;
+    num_format.precision = DEFAULTPRECISION;
+    num_format.type = DEFAULTTYPE;
+
+    if (num_format.format != NULL) {
+        if (parsenumformat())
+            exit(EXIT_FAILURE);
+    }
+    else
+        setnumformat();
+
+    conversion_worker(input, output);
+}
+
+
+int test_main()
+{
+    char a[] = "pi", b[] = "";
+    do_a_conversion(a, b);
+    do_a_conversion(a, b);
+    do_a_conversion(a, b);
+}
+
+
+int
+old_main(int argc, char **argv)
+{
+    static struct unittype have, want;
+    char *havestr=0, *wantstr=0;
+    int leading_spaces;
+    struct func *funcval;
+    struct wantalias *alias;
+    int havestrsize=0;   /* Only used if READLINE is undefined */
+    int wantstrsize=0;   /* Only used if READLINE is undefined */
+    int readerr;
+    char **unitfileptr;
+    int unitcount=0, prefixcount=0, funccount=0;   /* for counting units */
+    char *queryhave, *querywant, *comment;
+    int queryhavewidth, querywantwidth;
+#ifdef _WIN32
+    char *localemap;
+#endif
+
+    /* Set program parameter defaults */
+    num_format.format = NULL;
+    num_format.precision = DEFAULTPRECISION;
+    num_format.type = DEFAULTTYPE;
+
+    flags.quiet = 0;       /* Do not supress prompting */
+    flags.unitcheck = 0;   /* Unit checking is off */
+    flags.verbose = 1;     /* Medium verbosity */
+    flags.round = 0;       /* Rounding off */
+    flags.strictconvert=0; /* Strict conversion disabled (reciprocals active) */
+    flags.unitlists = 1;   /* Multi-unit conversion active */
+    flags.oneline = 0;     /* One line output is disabled */
+    flags.showconformable=0;  /* show unit conversion rather than all conformable units */
+    flags.showfactor = 0;  /* Don't show a multiplier for a 1|x fraction */
+    /*       in unit list output */
+    parserflags.minusminus = 1;  /* '-' character gives subtraction */
+    parserflags.oldstar = 0;     /* '*' has same precedence as '/' */
+
+    progname = getprogramname(argv[0]);
+
+    /*
+      unless UNITSFILE and LOCALEMAP have absolute pathnames, we may need
+      progdir to search for supporting files
+    */
+    if (!(isfullpath(UNITSFILE) && isfullpath(LOCALEMAP)))
+        progdir = getprogdir(argv[0], &fullprogname);
+    else {
+        progdir = NULL;
+        fullprogname = NULL;
+    }
+    datadir = getdatadir();      /* directory to search as last resort */
+
+    checklocale();
+
+    homedir = findhome(&homedir_error);
+
+#ifdef READLINE
+    #  if RL_READLINE_VERSION > 0x0402
+      rl_completion_entry_function = (rl_compentry_func_t *)completeunits;
+#  else
+      rl_completion_entry_function = (Function *)completeunits;
+#  endif
+   rl_basic_word_break_characters = " \t+-*/()|^;";
+   flags.readline = isatty(0);
+   if (flags.readline){
+     int file_exists;
+     historyfile = personalfile(NULL,HISTORY_FILE,1,&file_exists);
+   }
+#else
+    flags.readline = 0;
+#endif
+
+    unitsfiles[0] = 0;
+
+#ifdef _WIN32
+    if (!strcmp(homeunitsfile,".units"))
+     homeunitsfile = "unitdef.units";
+#endif
+
+    pager = getenv("PAGER");
+    if (!pager)
+        pager = DEFAULTPAGER;
+
+    flags.interactive = processargs(argc, argv, &havestr, &wantstr);
+
+#ifdef READLINE
+    if (flags.interactive && flags.readline && historyfile){
+     rl_initialize();
+     read_history(historyfile);
+     init_history_length = history_length;
+     init_history_base = history_base;
+     atexit(save_history);
+   }
+#endif
+
+    signal(SIGINT, write_files_sig);
+    signal(SIGTERM, write_files_sig);
+#ifdef SIGQUIT
+    signal(SIGQUIT, SIG_IGN);         /* Ignore QUIT signal sent by Ctrl-\ or Ctrl-4 */
+#endif
+    if (logfilename) {
+        if (!flags.interactive)
+            fprintf(stderr,
+                    "Log file '%s' ignored in non-interactive mode.\n",logfilename);
+        else open_logfile();
+    }
+
+    /* user has specified the complete format--use it */
+    if (num_format.format != NULL) {
+        if (parsenumformat())
+            exit(EXIT_FAILURE);
+    }
+    else
+        setnumformat();
+
+    if (flags.verbose==0)
+        deftext = "";
+
+    if (!unitsfiles[0]){
+        char *unitsfile;
+        unitsfile = findunitsfile(ERRMSG);
+        if (!unitsfile)
+            exit(EXIT_FAILURE);
+        else {
+            int file_exists;
+
+            unitsfiles[0] = unitsfile;
+            unitsfiles[1] = personalfile(HOME_UNITS_ENV,homeunitsfile,
+                                         0, &file_exists);
+            unitsfiles[2] = 0;
+        }
+    }
+
+#ifdef _WIN32
+    localemap = findlocalemap(ERRMSG);
+   if (localemap)
+     remaplocale(localemap);
+#endif
+
+    for(unitfileptr=unitsfiles;*unitfileptr;unitfileptr++){
+        readerr = readunits(*unitfileptr, stderr, &unitcount, &prefixcount,
+                            &funccount, 0);
+        if (readerr==E_MEMORY || readerr==E_FILE)
+            exit(EXIT_FAILURE);
+    }
+
+    if (flags.quiet)
+        queryhave = querywant = "";   /* No prompts are being printed */
+    else {
+        if (!promptprefix){
+            queryhave = QUERYHAVE;
+            querywant = QUERYWANT;
+        } else {
+            queryhave = (char *)mymalloc(strlen(promptprefix)+strlen(QUERYHAVE)+1,
+                                         "(main)");
+            querywant = (char *)mymalloc(strlen(promptprefix)+strlen(QUERYWANT)+1,
+                                         "(main)");
+            strcpy(queryhave, promptprefix);
+            strcat(queryhave, QUERYHAVE);
+            memset(querywant, ' ', strlen(promptprefix));
+            strcpy(querywant+strlen(promptprefix), QUERYWANT);
+        }
+        printf("%d units, %d prefixes, %d nonlinear units\n\n",
+               unitcount, prefixcount,funccount);
+    }
+    queryhavewidth = strwidth(queryhave);
+    querywantwidth = strwidth(querywant);
+
+    if (flags.unitcheck) {
+        checkunits(flags.unitcheck==2 || flags.verbose==2);
+        exit(EXIT_SUCCESS);
+    }
+
+    if (!flags.interactive) {
+        replacectrlchars(havestr);
+        if (wantstr)
+            replacectrlchars(wantstr);
+#ifdef SUPPORT_UTF8
+        if (strwidth(havestr)<0){
+       printf("Error: %s on input\n",invalid_utf8);
+       exit(EXIT_FAILURE);
+     }
+     if (wantstr && strwidth(wantstr)<0){
+       printf("Error: %s on input\n",invalid_utf8);
+       exit(EXIT_FAILURE);
+     }
+#endif
+        replace_operators(havestr);
+        removespaces(havestr);
+        if (wantstr) {
+            replace_operators(wantstr);
+            removespaces(wantstr);
+        }
+        if ((funcval = fnlookup(havestr))){
+            showfuncdefinition(funcval, FUNCTION);
+            exit(EXIT_SUCCESS);
+        }
+        if ((funcval = invfnlookup(havestr))){
+            showfuncdefinition(funcval, INVERSE);
+            exit(EXIT_SUCCESS);
+        }
+        if ((alias = aliaslookup(havestr))){
+            showunitlistdef(alias);
+            exit(EXIT_SUCCESS);
+        }
+        if (processunit(&have, havestr, NOPOINT))
+            exit(EXIT_FAILURE);
+        if (flags.showconformable == 1) {
+            tryallunits(&have,0);
+            exit(EXIT_SUCCESS);
+        }
+        if (!wantstr){
+            showdefinition(havestr,&have);
+            exit(EXIT_SUCCESS);
+        }
+        if (replacealias(&wantstr, 0)) /* the 0 says that we can free wantstr */
+            exit(EXIT_FAILURE);
+        if ((funcval = fnlookup(wantstr))){
+            if (showfunc(havestr, &have, funcval))  /* Clobbers have */
+                exit(EXIT_FAILURE);
+            else
+                exit(EXIT_SUCCESS);
+        }
+        if (processwant(&want, wantstr, NOPOINT))
+            exit(EXIT_FAILURE);
+        if (strchr(wantstr, UNITSEPCHAR)){
+            if (showunitlist(havestr, &have, wantstr))
+                exit(EXIT_FAILURE);
+            else
+                exit(EXIT_SUCCESS);
+        }
+        if (showanswer(havestr,&have,wantstr,&want))
+            exit(EXIT_FAILURE);
+        else
+            exit(EXIT_SUCCESS);
+    } else {
+        /******************/
+        /**  interactive **/
+        /******************/
+        for (;;) {
+            do {
+                fflush(stdout);
+                getuser(&havestr,&havestrsize,queryhave);
+                replace_operators(havestr);
+                comment = strip_comment(havestr);
+                leading_spaces = strspn(havestr," ");
+                removespaces(havestr);
+                if (logfile && comment && emptystr(havestr))
+                    fprintf(logfile, "#%s\n", comment);
+            } while (emptystr(havestr)
+                     || ishelpquery(havestr,0)
+                     || definevariable(havestr,queryhavewidth+leading_spaces)
+                     || (!fnlookup(havestr)
+                         && !invfnlookup(havestr)
+                         && !aliaslookup(havestr)
+                         && processunit(&have, havestr, queryhavewidth+leading_spaces)));
+            if (logfile) {
+                if (comment)
+                    fprintf(logfile, "%s%s\t#%s\n", LOGFROM, havestr,comment);
+                else
+                    fprintf(logfile, "%s%s\n", LOGFROM, havestr);
+            }
+            if ((alias = aliaslookup(havestr))){
+                showunitlistdef(alias);
+                continue;
+            }
+            if ((funcval = fnlookup(havestr))){
+                showfuncdefinition(funcval, FUNCTION);
+                continue;
+            }
+            if ((funcval = invfnlookup(havestr))){
+                showfuncdefinition(funcval, INVERSE);
+                continue;
+            }
+            do {
+                int repeat;
+                do {
+                    repeat = 0;
+                    fflush(stdout);
+                    getuser(&wantstr,&wantstrsize,querywant);
+                    replace_operators(wantstr);
+                    comment = strip_comment(wantstr);
+                    leading_spaces = strspn(wantstr," ");
+                    removespaces(wantstr);
+                    if (logfile && comment && emptystr(wantstr)){
+                        fprintf(logfile, "#%s\n", comment);
+                        repeat = 1;
+                    }
+                    if (ishelpquery(wantstr, &have)){
+                        repeat = 1;
+                        printf("%s%s\n",queryhave, havestr);
+                    }
+                } while (repeat);
+            } while (replacealias(&wantstr, &wantstrsize)
+                     || (!fnlookup(wantstr)
+                         && processwant(&want, wantstr, querywantwidth+leading_spaces)));
+            if (logfile) {
+                fprintf(logfile, "%s", LOGTO);
+                tightprint(logfile, wantstr);
+                if (comment)
+                    fprintf(logfile, "\t#%s", comment);
+                putc('\n', logfile);
+            }
+            if (emptystr(wantstr))
+                showdefinition(havestr,&have);
+            else if (strchr(wantstr, UNITSEPCHAR))
+                showunitlist(havestr, &have, wantstr);
+            else if ((funcval = fnlookup(wantstr)))
+                showfunc(havestr, &have, funcval); /* Clobbers have */
+            else {
+                showanswer(havestr,&have,wantstr, &want);
+                freeunit(&want);
+            }
+            unitcopy(&lastunit, &have);
+            lastunitset=1;
+            freeunit(&have);
+        }
+    }
+    return (0);
+}
+
+/* NOTES:
+
+mymalloc, growbuffer and tryallunits are the only places with print
+statements that should (?) be removed to make a library.  How can
+error reporting in these functions (memory allocation errors) be
+handled cleanly for a library implementation?  
+
+Way to report the reduced form of the two operands of a sum when
+they are not conformable.
+
+Way to report the required domain when getting an domain error.
+
+*/
diff --git a/units.h b/units.h
new file mode 100644
index 0000000..11c3091
--- /dev/null
+++ b/units.h
@@ -0,0 +1,170 @@
+/*
+ *  units, a program for units conversion
+ *  Copyright (C) 1996, 1997, 1999, 2000, 2001, 2014, 2017
+ *  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 3 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *     
+ *  This program was written by Adrian Mariano (adrianm@gnu.org)
+ */
+
+#include <math.h>
+#include <errno.h>
+
+/* Apparently popen and pclose require leading _ under windows */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#  define popen _popen
+#  define pclose _pclose
+#endif
+
+
+#ifdef NO_ISFINITE
+#  if defined _WIN32 && defined _MSC_VER
+#    define isfinite(x)  (!_isnan(x) && _finite(x))
+#  else
+#    define isfinite(x) ( -DBL_MAX <= (x) && (x) <= DBL_MAX )
+#  endif
+#endif
+
+#ifdef STRINGS_H
+#  include <strings.h>
+#else
+#  include <string.h>
+#endif 
+
+#ifndef NO_STDLIB_H
+#  include <stdlib.h>
+#else
+   char *malloc(),  *realloc(),  *getenv();
+#endif
+
+#ifndef strchr
+#  ifdef NO_STRCHR
+#    define strchr(a,b) index((a),(b))
+#  else
+     char *strchr();
+#  endif
+#endif /* !strchr */
+
+#define E_NORMAL 0
+#define E_PARSE 1
+#define E_PRODOVERFLOW 2
+#define E_REDUCE 3
+#define E_CIRCULARDEF 4
+#define E_BADSUM 5
+#define E_NOTANUMBER 6
+#define E_NOTROOT 7
+#define E_UNKNOWNUNIT 8
+#define E_FUNC 9         /* If errno is set after calling a function */
+#define E_BADFUNCTYPE 10
+#define E_BADFUNCARG 11
+#define E_NOTINDOMAIN 12
+#define E_BADFUNCDIMEN 13
+#define E_NOINVERSE 14
+#define E_PARSEMEM 15
+#define E_FUNARGDEF 16
+#define E_FILE 17
+#define E_BADFILE 18
+#define E_MEMORY 19
+#define E_BADNUM 20
+#define E_UNITEND 21
+#define E_LASTUNSET 22
+#define E_IRRATIONAL_EXPONENT 23
+#define E_BASE_NOTROOT 24
+#define E_DIMEXPONENT 25
+#define E_NOTAFUNC 26
+#define E_OVERFLOW 27
+#define E_UNDERFLOW 28
+
+extern char *errormsg[];
+
+/* 
+   Data type used to store a single unit being operated on. 
+
+   The numerator and denominator arrays contain lists of units
+   (strings) which are terminated by a null pointer.  The special
+   string NULLUNIT is used to mark blank units that occur in the
+   middle of the list.  
+*/
+
+extern char *NULLUNIT;
+
+#define MAXSUBUNITS 100         /* Size of internal unit reduction buffer */
+
+struct unittype {
+   char *numerator[MAXSUBUNITS];
+   char *denominator[MAXSUBUNITS];
+   double factor;
+};
+
+
+struct functype {
+  char *param;
+  char *def;
+  char *dimen;
+  double *domain_min, *domain_max;
+  int domain_min_open, domain_max_open;
+};
+
+struct pair {
+  double location, value;
+};
+
+struct func {
+  char *name;
+  struct functype forward;
+  struct functype inverse;
+  struct pair *table;
+  int tablelen;
+  char *tableunit;
+  struct func *next;
+  int skip_error_check;    /* do not check for errors when running units -c */
+  int linenumber;
+  char *file;              /* file where defined */ 
+};
+
+struct parseflag {
+  int oldstar;      /* Does '*' have higher precedence than '/' */
+  int minusminus;   /* Does '-' character give subtraction */
+};
+extern struct parseflag parserflags;
+
+extern struct unittype *parameter_value;
+extern char *function_parameter;
+
+extern int lastunitset;
+extern struct unittype lastunit;
+
+void *mymalloc(int bytes, const char *mesg);
+int hassubscript(const char *str);
+void initializeunit(struct unittype *theunit);
+void freeunit(struct unittype *theunit);
+void unitcopy(struct unittype *dest,struct unittype *src);
+int divunit(struct unittype *left, struct unittype *right);
+void invertunit(struct unittype *theunit);
+int multunit(struct unittype *left, struct unittype *right);
+int expunit(struct unittype *theunit, int  power);
+int addunit(struct unittype *unita, struct unittype *unitb);
+int rootunit(struct unittype *inunit,int n);
+int unitpower(struct unittype *base, struct unittype *exponent);
+char *dupstr(const char *str);
+char *dupnstr(const char *string, int length);
+int unit2num(struct unittype *input);
+struct func *fnlookup(const char *str);
+int evalfunc(struct unittype *theunit, struct func *infunc, int inverse, 
+             int allerror);
+int parseunit(struct unittype *output, const char *input, char **errstr,
+              int *errloc);
+
diff --git a/units_cur b/units_cur
new file mode 100755
index 0000000..259d4dd
--- /dev/null
+++ b/units_cur
@@ -0,0 +1,721 @@
+#!/usr/bin/python
+#
+# units_cur for units, a program for updated currency exchange rates
+#
+# Copyright (C) 2017-2018, 2022
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#    
+#
+# This program was written by Adrian Mariano (adrianm@gnu.org)
+#
+
+# For Python 2 & 3 compatibility
+from __future__ import absolute_import, division, print_function
+#
+#
+
+version = '5.0'
+
+# Version 5.0:
+#
+# Rewrite to support multiple different data sources due to disappearance
+# of the Yahoo feed.  Includes support for base currency selection. 
+#
+# Version 4.3: 20 July 2018
+#
+# Validate rate data from server
+#
+# Version 4.2: 18 April 2018
+#
+# Handle case of empty/malformed entry returned from the server
+#
+# Version 4.1: 30 October 2017
+#
+# Fixed to include USD in the list of currency codes.  
+#
+# Version 4: 2 October 2017 
+#
+# Complete rewrite to use Yahoo YQL API due to removal of TimeGenie RSS feed.
+# Switched to requests library using JSON.  One program now runs under
+# Python 2 or Python 3.  Thanks to Ray Hamel for some help with this update.  
+
+# Normal imports
+import requests
+import codecs
+import json
+from argparse import ArgumentParser
+from collections import OrderedDict
+from datetime import date
+from os import linesep
+from os.path import getmtime
+from sys import exit, stderr, stdout
+
+datestr = date.today().isoformat()
+
+output_dir = ''
+
+currency_file = output_dir + 'currency.units'
+cpi_file = output_dir + 'cpi.units'
+
+# valid metals 
+
+validmetals = ['silver','gold','platinum']
+
+PRIMITIVE = '!        # Base unit, the primitive unit of currency'
+
+# This exchange rate table lists the currency ISO 4217 codes, their
+# long text names, and any fixed definitions.  If the definition is
+# empty then units_cur will query the server for a value.
+
+rate_index = 1
+currency = OrderedDict([
+    ('ATS', ['austriaschilling',  '1|13.7603 euro']),
+    ('BEF', ['belgiumfranc',      '1|40.3399 euro']),
+    ('CYP', ['cypruspound',       '1|0.585274 euro']),
+    ('EEK', ['estoniakroon',      '1|15.6466 euro # Equal to 1|8 germanymark']),
+    ('FIM', ['finlandmarkka',     '1|5.94573 euro']),
+    ('FRF', ['francefranc',       '1|6.55957 euro']),
+    ('DEM', ['germanymark',       '1|1.95583 euro']),
+    ('GRD', ['greecedrachma',     '1|340.75 euro']),
+    ('IEP', ['irelandpunt',       '1|0.787564 euro']),
+    ('ITL', ['italylira',         '1|1936.27 euro']),
+    ('LVL', ['latvialats',        '1|0.702804 euro']),
+    ('LTL', ['lithuanialitas',    '1|3.4528 euro']),
+    ('LUF', ['luxembourgfranc',   '1|40.3399 euro']),
+    ('MTL', ['maltalira',         '1|0.4293 euro']),
+    ('SKK', ['slovakiakoruna',    '1|30.1260 euro']),
+    ('SIT', ['sloveniatolar',     '1|239.640 euro']),
+    ('ESP', ['spainpeseta',       '1|166.386 euro']),
+    ('NLG', ['netherlandsguilder','1|2.20371 euro']),
+    ('PTE', ['portugalescudo',    '1|200.482 euro']),
+    ('CVE', ['capeverdeescudo',   '1|110.265 euro']),
+    ('BGN', ['bulgarialev',       '1|1.9558 euro']),
+    ('BAM', ['bosniaconvertiblemark','germanymark']),
+    ('KMF', ['comorosfranc',      '1|491.96775 euro']),
+    ('XOF', ['westafricafranc',   '1|655.957 euro']),
+    ('XPF', ['cfpfranc',          '1|119.33 euro']),
+    ('XAF', ['centralafricacfafranc','1|655.957 euro']),
+    ('AED', ['uaedirham','']),
+    ('AFN', ['afghanistanafghani','']),
+    ('ALL', ['albanialek','']),
+    ('AMD', ['armeniadram','']),
+    ('ANG', ['antillesguilder','']),
+    ('AOA', ['angolakwanza','']),
+    ('ARS', ['argentinapeso','']),
+    ('AUD', ['australiadollar','']),
+    ('AWG', ['arubaflorin','']),
+    ('AZN', ['azerbaijanmanat','']),
+    ('BAM', ['bosniaconvertiblemark','']),
+    ('BBD', ['barbadosdollar','']),
+    ('BDT', ['bangladeshtaka','']),
+    ('BGN', ['bulgarialev','']),
+    ('BHD', ['bahraindinar','']),
+    ('BIF', ['burundifranc','']),
+    ('BMD', ['bermudadollar','']),
+    ('BND', ['bruneidollar','']),
+    ('BOB', ['boliviaboliviano','']),
+    ('BRL', ['brazilreal','']),
+    ('BSD', ['bahamasdollar','']),
+    ('BTN', ['bhutanngultrum','']),
+    ('BWP', ['botswanapula','']),
+    ('BYN', ['belarusruble','']),
+    ('BYR', ['oldbelarusruble','1|10000 BYN']),  
+    ('BZD', ['belizedollar','']),
+    ('CAD', ['canadadollar','']),
+    ('CDF', ['drcfranccongolais','']),
+    ('CHF', ['swissfranc','']),
+    ('CLP', ['chilepeso','']),
+    ('CNY', ['chinayuan','']),
+    ('COP', ['colombiapeso','']),
+    ('CRC', ['costaricacolon','']),
+    ('CUP', ['cubapeso','']),
+    ('CVE', ['capeverdeescudo','']),
+    ('CZK', ['czechiakoruna','']),
+    ('DJF', ['djiboutifranc','']),
+    ('DKK', ['denmarkkrone','']),
+    ('DOP', ['dominicanrepublicpeso','']),
+    ('DZD', ['algeriadinar','']),
+    ('EGP', ['egyptpound','']),
+    ('ERN', ['eritreanakfa','']),
+    ('ETB', ['ethiopiabirr','']),
+    ('EUR', ['euro','']),
+    ('FJD', ['fijidollar','']),
+    ('FKP', ['falklandislandspound','']),
+    ('GBP', ['ukpound','']),
+    ('GEL', ['georgialari','']),
+    ('GHS', ['ghanacedi','']),
+    ('GIP', ['gibraltarpound','']),
+    ('GMD', ['gambiadalasi','']),
+    ('GNF', ['guineafranc','']),
+    ('GTQ', ['guatemalaquetzal','']),
+    ('GYD', ['guyanadollar','']),
+    ('HKD', ['hongkongdollar','']),
+    ('HNL', ['honduraslempira','']),
+    ('HRK', ['croatiakuna','']),
+    ('HTG', ['haitigourde','']),
+    ('HUF', ['hungaryforint','']),
+    ('IDR', ['indonesiarupiah','']),
+    ('ILS', ['israelnewshekel','']),
+    ('INR', ['indiarupee','']),
+    ('IQD', ['iraqdinar','']),
+    ('IRR', ['iranrial','']),
+    ('ISK', ['icelandkrona','']),
+    ('JMD', ['jamaicadollar','']),
+    ('JOD', ['jordandinar','']),
+    ('JPY', ['japanyen','']),
+    ('KES', ['kenyaschilling','']),
+    ('KGS', ['kyrgyzstansom','']),
+    ('KHR', ['cambodiariel','']),
+    ('KMF', ['comorosfranc','']),
+    ('KPW', ['northkoreawon','']),
+    ('KRW', ['southkoreawon','']),
+    ('KWD', ['kuwaitdinar','']),
+    ('KYD', ['caymanislandsdollar','']),
+    ('KZT', ['kazakhstantenge','']),
+    ('LAK', ['laokip','']),
+    ('LBP', ['lebanonpound','']),
+    ('LKR', ['srilankarupee','']),
+    ('LRD', ['liberiadollar','']),
+    ('LSL', ['lesotholoti','']),
+    ('LYD', ['libyadinar','']),
+    ('MAD', ['moroccodirham','']),
+    ('MDL', ['moldovaleu','']),
+    ('MGA', ['madagascarariary','']),
+    ('MKD', ['macedoniadenar','']),
+    ('MMK', ['myanmarkyat','']),
+    ('MNT', ['mongoliatugrik','']),  
+    ('MOP', ['macaupataca','']),
+    ('MRO', ['mauritaniaoldouguiya','1|10 MRU']),
+    ('MRU', ['mauritaniaouguiya', '']), 
+    ('MUR', ['mauritiusrupee','']),
+    ('MVR', ['maldiverufiyaa','']),
+    ('MWK', ['malawikwacha','']),
+    ('MXN', ['mexicopeso','']),
+    ('MYR', ['malaysiaringgit','']),
+    ('MZN', ['mozambiquemetical','']),
+    ('NAD', ['namibiadollar','']),
+    ('NGN', ['nigerianaira','']),
+    ('NIO', ['nicaraguacordobaoro','']),
+    ('NOK', ['norwaykrone','']),
+    ('NPR', ['nepalrupee','']),
+    ('NZD', ['newzealanddollar','']),
+    ('OMR', ['omanrial','']),
+    ('PAB', ['panamabalboa','']),
+    ('PEN', ['perunuevosol','']),
+    ('PGK', ['papuanewguineakina','']),
+    ('PHP', ['philippinepeso','']),
+    ('PKR', ['pakistanrupee','']),
+    ('PLN', ['polandzloty','']),
+    ('PYG', ['paraguayguarani','']),
+    ('QAR', ['qatarrial','']),
+    ('RON', ['romanianewlei','']),
+    ('RSD', ['serbiadinar','']),
+    ('RUB', ['russiaruble','']),
+    ('RWF', ['rwandafranc','']),
+    ('SAR', ['saudiarabiariyal','']),
+    ('SBD', ['solomonislandsdollar','']),
+    ('SCR', ['seychellesrupee','']),
+    ('SDG', ['sudanpound','']),
+    ('SEK', ['swedenkrona','']),
+    ('SGD', ['singaporedollar','']),
+    ('SHP', ['sainthelenapound','']),
+#    ('SLL', ['sierraleoneoldleone','1|1000 SLE']),
+#    ('SLE', ['sierraleoneleone','']),
+    ('SOS', ['somaliaschilling','']),
+    ('SRD', ['surinamedollar','']),
+    ('SSP', ['southsudanpound','']),
+    ('STD', ['saotome&principeolddobra','']),
+    ('STN', ['saotome&principedobra','']),
+#    ('SVC', ['elsalvadorcolon','']),  # eliminated in 2001
+    ('SYP', ['syriapound','']),
+    ('SZL', ['swazilandlilangeni','']),
+    ('THB', ['thailandbaht','']),
+    ('TJS', ['tajikistansomoni','']),
+    ('TMT', ['turkmenistanmanat','']),
+    ('TND', ['tunisiadinar','']),
+    ('TOP', ["tongapa'anga",'']),
+    ('TRY', ['turkeylira','']),
+    ('TTD', ['trinidadandtobagodollar','']),
+    ('TWD', ['taiwandollar','']),
+    ('TZS', ['tanzaniashilling','']),
+    ('UAH', ['ukrainehryvnia','']),
+    ('UGX', ['ugandaschilling','']),
+    ('USD', ['US$', '']),
+    ('UYU', ['uruguaypeso','']),
+    ('UZS', ['uzbekistansum','']),
+    ('VEF', ['venezuelabolivarfuerte','']),
+    ('VES', ['venezuelabolivarsoberano','']),
+    ('VND', ['vietnamdong','']),
+    ('VUV', ['vanuatuvatu','']),
+    ('WST', ['samoatala','']),
+    ('XAF', ['centralafricacfafranc','']),
+    ('XCD', ['eastcaribbeandollar','']),
+    ('XDR', ['specialdrawingrights','']),
+    ('YER', ['yemenrial','']),
+    ('ZAR', ['southafricarand','']),
+    ('ZMW', ['zambiakwacha','']),
+    ('ZWL', ['zimbabwedollar','']),
+    ('FOK', ['faroeislandskróna','DKK']),
+    ('GGP', ['guernseypound', 'GBP']),
+    ('IMP', ['isleofmanpound','GBP']),
+    ('JEP', ['jerseypound','GBP']),
+    ('KID', ['kiribatidollar','AUD']),
+    ('TVD', ['tuvaludollar','AUD']),
+])
+
+
+def validfloat(x):
+  try:
+    float(x)
+    return True
+  except ValueError:
+    return False
+
+def addrate(verbose,form,code,rate):
+  if code not in currency.keys():
+    if (verbose):
+      stderr.write('Got unknown currency with code {}\n'.format(code))
+  else:
+    if not currency[code][rate_index]:
+      if validfloat(rate):
+        currency[code][rate_index] = form.format(rate)
+      else:
+        stderr.write('Got invalid rate "{}" for currency "{}"\n'.format(
+                                  rate, code))
+    elif verbose:
+      if currency[code][rate_index] != form.format(rate):
+        stderr.write('Got value "{}" for currency "{}" but '
+                   'it is already defined as {}\n'.format(rate, code,
+                                                  currency[code][rate_index]))
+
+def getjson(address,args=None):
+  try:
+    res = requests.get(address,args)
+    res.raise_for_status()
+    return(res.json())
+  except requests.exceptions.RequestException as e:
+    stderr.write('Error connecting to currency server:\n{}.\n'.format(e))
+    exit(1)
+        
+########################################################
+#
+#  Connect to floatrates for currency update
+#
+
+def floatrates(verbose,base,dummy):
+  webdata = getjson('https://www.floatrates.com/daily/'+base+'.json')
+  for index in webdata:
+    entry = webdata[index]
+    if 'rate' not in entry or 'code' not in entry:    # Skip empty/bad entries
+      if verbose:
+        stderr.write('Got bad entry from server: '+str(entry)+'\n')
+    else:      
+      addrate(verbose,'{} '+base,entry['code'],entry['inverseRate'])
+  currency[base][rate_index] = PRIMITIVE
+  return('FloatRates ('+base+' base)')
+
+########################################################
+#
+# Connect to European central bank site
+#
+
+def eubankrates(verbose,base,dummy):
+  if verbose and base!='EUR':
+    stderr.write('European bank uses euro for base currency.  Specified base {} ignored.\n'.format(base))
+  import xml.etree.ElementTree as ET
+  try:
+    res=requests.get('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')
+    res.raise_for_status()
+    data = ET.fromstring(res.content)[2][0]
+  except requests.exceptions.RequestException as e:
+    stderr.write('Error connecting to currency server:\n{}.\n'.
+                 format(e))
+    exit(1)
+  for entry in data.iter():
+    if entry.get('time'):
+      continue
+    rate = entry.get('rate')
+    code = entry.get('currency')
+    if not rate or not code:                # Skip empty/bad entries
+      if verbose:
+        stderr.write('Got bad entry from server, code {} and rate {}\n'.format(code,rate))
+    else:      
+      addrate(verbose,'1|{} euro', code, rate)
+  currency['EUR'][rate_index]=PRIMITIVE
+  return('the European Central Bank (euro base)')
+      
+########################################################
+#
+# Connect to fixer.io (requires API key)
+#
+# Free API key does not allow changing base currency
+# With free key only euro base is supported, and https is not allowed
+#
+      
+def fixer(verbose,base,key):
+  if not key:
+    stderr.write('API key required for this source\n')
+    exit(1)
+  if verbose and base!='EUR':
+    stderr.write('Fixer uses euro for base currency.  Specified base {} ignored.\n'.format(base))
+  webdata = getjson('http://data.fixer.io/api/latest', {'access_key':key})
+  if not webdata['success']:
+    stderr.write('Currency server error: '+webdata['error']['info'])
+    exit(1)
+  for code in webdata['rates']:
+    addrate(verbose,'1|{} euro', code, webdata['rates'][code])
+  currency['EUR'][rate_index] = PRIMITIVE
+  return('Fixer (euro base)')
+
+########################################################
+#
+# Connect to openexchangerates (requires API key)
+#
+# Free API key does not allow changing the base currency
+#
+         
+def openexchangerates(verbose,base,key):
+  if not key:
+    stderr.write('API key required for this source\n')
+    exit(1)
+  if verbose and base!='USD':
+    stderr.write('Open Exchange Rates uses US dollar for base currency.  Specified base {} ignored.\n'.format(base))
+  webdata = getjson('https://openexchangerates.org/api/latest.json',
+                    {'app_id':key}
+                    )
+  for code in webdata['rates']:
+    addrate(verbose,'1|{} US$', code, webdata['rates'][code])
+  currency['USD'][rate_index] = PRIMITIVE
+  return('open exchange rates (USD base)')
+
+########################################################
+#
+# Connect to exchangerate-api.com
+#
+# The open access endpoint gives updates once per day.
+# User can optionally supply a paid account's API key to get newer data and more precision.
+# Base currency can be changed with both open and paid versions of the API.
+#
+
+def exchangerate_api(verbose,base,key):
+  if not key:
+    webdata = getjson('https://open.er-api.com/v6/latest/'+base)
+    if not webdata['result'] or webdata['result'] != 'success':
+      stderr.write('Currency server error: '+webdata['error-type']+'\n')
+      exit(1)
+    for code, rate in webdata['rates'].items() :
+      addrate(verbose,'1|{} '+base,code,rate)
+  else:
+    webdata = getjson('https://v6.exchangerate-api.com/v6/'+key+'/latest/'+base)
+    if not webdata['result'] or webdata['result'] != 'success':
+      stderr.write('Currency server error: '+webdata['error-type']+'\n')
+      exit(1)
+    for code, rate in webdata['conversion_rates'].items() :
+      addrate(verbose,'1|{} '+base,code,rate)
+  currency[base][rate_index] = PRIMITIVE
+  return('exchangerate-api.com ('+base+' base)')
+
+
+#######################################################
+#
+# list of valid source names and corresponding functions
+#
+
+sources = {       
+  'exchangerate_api': exchangerate_api,  
+  'floatrates': floatrates,
+  'eubank' : eubankrates,
+  'fixer' : fixer,
+  'openexchangerates': openexchangerates,
+}
+
+default_currency = 'exchangerate_api'
+
+#######################################################
+#
+# Argument Processing
+#
+
+ap = ArgumentParser(
+    description="Update currency information for 'units' "
+    "into the specified filenames or the default currency "
+    "file name {} and CPI file name {}.  The special "
+    "filename '-' will send either or both files to "
+    "stdout.".format(currency_file,cpi_file),
+)
+
+ap.add_argument(
+    'currency_file',
+    default=currency_file,
+    help='the file to update',
+    metavar='currency_file',
+    nargs='?',
+    type=str,
+)
+
+ap.add_argument(
+    'cpi_file',
+    default=cpi_file,
+    help='the file to update',
+    metavar='cpi_file',
+    nargs='?',
+    type=str,
+)
+
+ap.add_argument('-V','--version',
+                action='version',
+                version='%(prog)s version ' + version,
+                help='display units_cur version',
+)
+
+ap.add_argument('-v','--verbose',
+                action='store_true',
+                help='display details when fetching currency data',
+)
+
+ap.add_argument('-s','--source',choices=list(sources.keys()),
+                default=default_currency, 
+                help='set currency data source (default: {})'.format(default_currency),
+)
+
+ap.add_argument('-b','--base',default='USD',
+                help='set the base currency (when allowed by source).  BASE should be a 3 letter ISO currency code, e.g. USD.  The specified currency will be the primitive currency unit used by units.  Only the floatrates source supports this option.',
+)                
+
+ap.add_argument('-k','--key',default='',
+                help='set API key for sources that require it'
+)
+
+ap.add_argument('--blskey',default='',
+                help='set BLS key for fetching CPI data'
+)
+
+
+args = ap.parse_args()  
+currency_file = args.currency_file
+cpi_file = args.cpi_file
+verbose = args.verbose
+source = args.source
+base = args.base
+apikey = args.key
+cpikey = args.blskey
+
+if base not in currency.keys():
+  stderr.write('Base currency {} is not a known currency code.\n'.format(base))
+  exit(1)
+
+########################################################
+#
+# Fetch currency data from specified curerncy source
+#
+
+sourcename = sources[source](verbose,base,apikey)
+            
+# Delete currencies where we have no rate data
+for code in list(currency.keys()):
+  if not currency[code][rate_index]:
+    if verbose:
+      stderr.write('No data for {}\n'.format(code))
+    del currency[code]
+
+cnames = [currency[code][0] for code in currency.keys()]
+crates = [currency[code][1] for code in currency.keys()]
+
+codestr = '\n'.join('{:23}{}'.
+   format(code, name) for (code,name) in zip(currency.keys(), cnames))
+
+maxlen = max(len(name) for name in cnames) + 2
+
+ratestr = '\n'.join(
+    '{:{}}{}'.format(name, maxlen, rate) for (name, rate) in zip(cnames, crates)
+    )
+
+#######################################################
+#
+# Get precious metals data and bitcoin
+#
+
+metals = getjson('https://services.packetizer.com/spotprices',{'f':'json'})
+bitcoin = getjson('https://services.packetizer.com/btc',{'f':'json'})
+
+metallist = ['']*len(validmetals)
+for metal, price in metals.items():
+  if metal in validmetals:
+    metalindex = validmetals.index(metal)
+    if validfloat(price):
+      if not metallist[metalindex]:
+        metallist[validmetals.index(metal)] = '{:19}{} US$/troyounce'.format(
+                                                         metal + 'price', price)
+      elif verbose:
+        stderr.write('Got value "{}" for metal "{}" but '
+                     'it is already defined\n'.format(price,metal))
+    else:
+      stderr.write('Got invalid rate "{}" for metal "{}"\n'.format(price,metal))
+  elif metal != 'date' and verbose:  # Don't print a message for the "date" entry
+    stderr.write('Got unknown metal "{}" with value "{}"\n'.format(metal,price))
+metalstr = '\n'.join(metallist)
+
+if validfloat(bitcoin['usd']):
+  bitcoinstr = '{:{}}{} US$ # From services.packetizer.com/btc\n'.format(
+                'bitcoin',maxlen,bitcoin['usd'])
+else:
+  stderr.write('Got invalid bitcoin rate "{}"\n', bitcoint['usd'])
+  bitcointstr=''
+
+#######################################################
+#
+# Get CPI data
+#
+
+docpi=False
+if cpi_file=='-':
+  docpi=True
+else:
+  try:
+    filedate = getmtime(cpi_file)
+    filedate = date.fromtimestamp(filedate)
+    today = date.today()
+    dmonth = (today.month-filedate.month) + 12*(today.year-filedate.year)
+    if dmonth>1 or (dmonth==1 and today.day>18):
+      docpi=True
+  except FileNotFoundError:
+    docpi=True
+  
+if docpi:
+  headers = {'Content-type': 'application/json'}
+  yearlist = list(range(date.today().year,1912,-10))
+  if yearlist[-1]>1912:
+    yearlist.append(1912)
+  cpi=[]
+  lastcpi = 0
+  query = {"seriesid": ['CUUR0000SA0']}
+  if cpikey:
+    query["registrationkey"]=cpikey
+  ########################################################################
+  # The api.bls.gov site currently (2024-02-15) resolves to an
+  # IPv4 address which works, and an IPv6 address which does
+  # not.  The urllib3 package does not currently implement the
+  # Happy Eyeballs algorithm, nor any other mechanism to re-try
+  # hung connections with alternative addresses returned by DNS.
+  # In the interest of expediency, we temporarily force the
+  # connection to api.bls.gov to only use IPv4; hopefully at
+  # some future date either urllib3 will gain the necessary
+  # features and/or the BLS will fix their configuration so
+  # that all A and AAAA records for api.bls.gov resolve to an
+  # operational server.  At that time we can remove the three
+  # references to "requests.packages.urllib3.util.connection.HAS_IPV6"
+  # in this function.
+  ########################################################################
+  save_rpuucH = requests.packages.urllib3.util.connection.HAS_IPV6
+  requests.packages.urllib3.util.connection.HAS_IPV6 = False
+  for endyear in range(len(yearlist)-1):
+    query["startyear"]=str(yearlist[endyear+1]+1)
+    query["endyear"]=str(yearlist[endyear])
+    data = json.dumps(query)
+    p = requests.post('https://api.bls.gov/publicAPI/v2/timeseries/data/', data=data, headers=headers)
+    json_data = json.loads(p.text)
+    if json_data['status']=="REQUEST_NOT_PROCESSED":
+      docpi=False
+      stderr.write("Unable to update CPI data: Exceeded daily threshold for BLS requests\n")
+      break
+    for series in json_data['Results']['series']:
+        for item in series['data']:
+            if not ('M01' <= item['period'] <= 'M12'):
+                continue
+            year = int(item['year']) + int(item['period'][1:])/12
+            value = item['value']
+            cpi.append(' '*10 + "{} {} \\".format(year,value))
+            if lastcpi==0:
+              lastcpi=value
+              lastyear=year
+            firstcpi=value
+            firstyear=year
+  requests.packages.urllib3.util.connection.HAS_IPV6 = save_rpuucH              
+if docpi:   # Check again because request may have failed
+  cpi.reverse()
+  cpistr = '\n'.join(cpi)
+
+  cpi_text = ("""!message Consumer price index data from US BLS, {datestr}
+
+UScpi[1] noerror \\
+{cpistr}
+
+UScpi_now          {lastcpi}
+UScpi_lastdate     {lastyear}
+
+USdollars_in(date) units=[1;$] domain=[{firstyear},{lastyear}] \\
+                               range=[1,{maxinfl}] \\
+                               US$ UScpi_now / UScpi(date) ;\\
+                               ~UScpi(US$ UScpi_now / USdollars_in)
+USinflation_since(date) units=[1;1] domain=[{firstyear},{lastyear}] \\
+                               range=[1,{maxinfl}] \\
+                                UScpi_now / UScpi(date) ;\\
+                               ~UScpi(UScpi_now / USinflation_since)
+
+""".format(datestr=datestr,cpistr=cpistr,maxinfl=float(lastcpi)/float(firstcpi),lastcpi=lastcpi,
+           firstyear=firstyear,lastyear=lastyear)
+  ).replace('\n', linesep)  
+  
+#######################################################
+#
+# Format output and write the currency file
+#
+
+currency_text = (
+"""# ISO Currency Codes
+
+{codestr}
+
+# Currency exchange rates source 
+
+!message Currency exchange rates from {sourcename} on {datestr}
+
+{ratestr}
+{bitcoinstr}
+
+# Precious metals prices from Packetizer (services.packetizer.com/spotprices)
+
+{metalstr}
+
+""".format(codestr=codestr, datestr=datestr, ratestr=ratestr, metalstr=metalstr,
+           bitcoinstr=bitcoinstr, sourcename=sourcename)
+).replace('\n', linesep)
+
+
+######################################
+
+try:
+    if currency_file == '-':
+        codecs.StreamReader(stdout, codecs.getreader('utf8')).write(currency_text)
+    else:    
+        with codecs.open(currency_file, 'w', 'utf8') as of:
+            of.write(currency_text)
+except IOError as e:
+    stderr.write('Unable to write to output file:\n{}\n'.format(e))
+    exit(1)
+
+if docpi:    
+  try:
+      if cpi_file == '-':
+          codecs.StreamReader(stdout, codecs.getreader('utf8')).write(cpi_text)
+      else:    
+          with codecs.open(cpi_file, 'w', 'utf8') as of:
+              of.write(cpi_text)
+  except IOError as e:
+      stderr.write('Unable to write to output file:\n{}\n'.format(e))
+      exit(1)
+    
diff --git a/units_cur_inst b/units_cur_inst
new file mode 100644
index 0000000..587cd31
--- /dev/null
+++ b/units_cur_inst
@@ -0,0 +1,721 @@
+#!/opt/homebrew/bin/python3
+#
+# units_cur for units, a program for updated currency exchange rates
+#
+# Copyright (C) 2017-2018, 2022
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#    
+#
+# This program was written by Adrian Mariano (adrianm@gnu.org)
+#
+
+# For Python 2 & 3 compatibility
+from __future__ import absolute_import, division, print_function
+#
+#
+
+version = '5.0'
+
+# Version 5.0:
+#
+# Rewrite to support multiple different data sources due to disappearance
+# of the Yahoo feed.  Includes support for base currency selection. 
+#
+# Version 4.3: 20 July 2018
+#
+# Validate rate data from server
+#
+# Version 4.2: 18 April 2018
+#
+# Handle case of empty/malformed entry returned from the server
+#
+# Version 4.1: 30 October 2017
+#
+# Fixed to include USD in the list of currency codes.  
+#
+# Version 4: 2 October 2017 
+#
+# Complete rewrite to use Yahoo YQL API due to removal of TimeGenie RSS feed.
+# Switched to requests library using JSON.  One program now runs under
+# Python 2 or Python 3.  Thanks to Ray Hamel for some help with this update.  
+
+# Normal imports
+import requests
+import codecs
+import json
+from argparse import ArgumentParser
+from collections import OrderedDict
+from datetime import date
+from os import linesep
+from os.path import getmtime
+from sys import exit, stderr, stdout
+
+datestr = date.today().isoformat()
+
+output_dir='/usr/local/com/units/'
+
+currency_file = output_dir + 'currency.units'
+cpi_file = output_dir + 'cpi.units'
+
+# valid metals 
+
+validmetals = ['silver','gold','platinum']
+
+PRIMITIVE = '!        # Base unit, the primitive unit of currency'
+
+# This exchange rate table lists the currency ISO 4217 codes, their
+# long text names, and any fixed definitions.  If the definition is
+# empty then units_cur will query the server for a value.
+
+rate_index = 1
+currency = OrderedDict([
+    ('ATS', ['austriaschilling',  '1|13.7603 euro']),
+    ('BEF', ['belgiumfranc',      '1|40.3399 euro']),
+    ('CYP', ['cypruspound',       '1|0.585274 euro']),
+    ('EEK', ['estoniakroon',      '1|15.6466 euro # Equal to 1|8 germanymark']),
+    ('FIM', ['finlandmarkka',     '1|5.94573 euro']),
+    ('FRF', ['francefranc',       '1|6.55957 euro']),
+    ('DEM', ['germanymark',       '1|1.95583 euro']),
+    ('GRD', ['greecedrachma',     '1|340.75 euro']),
+    ('IEP', ['irelandpunt',       '1|0.787564 euro']),
+    ('ITL', ['italylira',         '1|1936.27 euro']),
+    ('LVL', ['latvialats',        '1|0.702804 euro']),
+    ('LTL', ['lithuanialitas',    '1|3.4528 euro']),
+    ('LUF', ['luxembourgfranc',   '1|40.3399 euro']),
+    ('MTL', ['maltalira',         '1|0.4293 euro']),
+    ('SKK', ['slovakiakoruna',    '1|30.1260 euro']),
+    ('SIT', ['sloveniatolar',     '1|239.640 euro']),
+    ('ESP', ['spainpeseta',       '1|166.386 euro']),
+    ('NLG', ['netherlandsguilder','1|2.20371 euro']),
+    ('PTE', ['portugalescudo',    '1|200.482 euro']),
+    ('CVE', ['capeverdeescudo',   '1|110.265 euro']),
+    ('BGN', ['bulgarialev',       '1|1.9558 euro']),
+    ('BAM', ['bosniaconvertiblemark','germanymark']),
+    ('KMF', ['comorosfranc',      '1|491.96775 euro']),
+    ('XOF', ['westafricafranc',   '1|655.957 euro']),
+    ('XPF', ['cfpfranc',          '1|119.33 euro']),
+    ('XAF', ['centralafricacfafranc','1|655.957 euro']),
+    ('AED', ['uaedirham','']),
+    ('AFN', ['afghanistanafghani','']),
+    ('ALL', ['albanialek','']),
+    ('AMD', ['armeniadram','']),
+    ('ANG', ['antillesguilder','']),
+    ('AOA', ['angolakwanza','']),
+    ('ARS', ['argentinapeso','']),
+    ('AUD', ['australiadollar','']),
+    ('AWG', ['arubaflorin','']),
+    ('AZN', ['azerbaijanmanat','']),
+    ('BAM', ['bosniaconvertiblemark','']),
+    ('BBD', ['barbadosdollar','']),
+    ('BDT', ['bangladeshtaka','']),
+    ('BGN', ['bulgarialev','']),
+    ('BHD', ['bahraindinar','']),
+    ('BIF', ['burundifranc','']),
+    ('BMD', ['bermudadollar','']),
+    ('BND', ['bruneidollar','']),
+    ('BOB', ['boliviaboliviano','']),
+    ('BRL', ['brazilreal','']),
+    ('BSD', ['bahamasdollar','']),
+    ('BTN', ['bhutanngultrum','']),
+    ('BWP', ['botswanapula','']),
+    ('BYN', ['belarusruble','']),
+    ('BYR', ['oldbelarusruble','1|10000 BYN']),  
+    ('BZD', ['belizedollar','']),
+    ('CAD', ['canadadollar','']),
+    ('CDF', ['drcfranccongolais','']),
+    ('CHF', ['swissfranc','']),
+    ('CLP', ['chilepeso','']),
+    ('CNY', ['chinayuan','']),
+    ('COP', ['colombiapeso','']),
+    ('CRC', ['costaricacolon','']),
+    ('CUP', ['cubapeso','']),
+    ('CVE', ['capeverdeescudo','']),
+    ('CZK', ['czechiakoruna','']),
+    ('DJF', ['djiboutifranc','']),
+    ('DKK', ['denmarkkrone','']),
+    ('DOP', ['dominicanrepublicpeso','']),
+    ('DZD', ['algeriadinar','']),
+    ('EGP', ['egyptpound','']),
+    ('ERN', ['eritreanakfa','']),
+    ('ETB', ['ethiopiabirr','']),
+    ('EUR', ['euro','']),
+    ('FJD', ['fijidollar','']),
+    ('FKP', ['falklandislandspound','']),
+    ('GBP', ['ukpound','']),
+    ('GEL', ['georgialari','']),
+    ('GHS', ['ghanacedi','']),
+    ('GIP', ['gibraltarpound','']),
+    ('GMD', ['gambiadalasi','']),
+    ('GNF', ['guineafranc','']),
+    ('GTQ', ['guatemalaquetzal','']),
+    ('GYD', ['guyanadollar','']),
+    ('HKD', ['hongkongdollar','']),
+    ('HNL', ['honduraslempira','']),
+    ('HRK', ['croatiakuna','']),
+    ('HTG', ['haitigourde','']),
+    ('HUF', ['hungaryforint','']),
+    ('IDR', ['indonesiarupiah','']),
+    ('ILS', ['israelnewshekel','']),
+    ('INR', ['indiarupee','']),
+    ('IQD', ['iraqdinar','']),
+    ('IRR', ['iranrial','']),
+    ('ISK', ['icelandkrona','']),
+    ('JMD', ['jamaicadollar','']),
+    ('JOD', ['jordandinar','']),
+    ('JPY', ['japanyen','']),
+    ('KES', ['kenyaschilling','']),
+    ('KGS', ['kyrgyzstansom','']),
+    ('KHR', ['cambodiariel','']),
+    ('KMF', ['comorosfranc','']),
+    ('KPW', ['northkoreawon','']),
+    ('KRW', ['southkoreawon','']),
+    ('KWD', ['kuwaitdinar','']),
+    ('KYD', ['caymanislandsdollar','']),
+    ('KZT', ['kazakhstantenge','']),
+    ('LAK', ['laokip','']),
+    ('LBP', ['lebanonpound','']),
+    ('LKR', ['srilankarupee','']),
+    ('LRD', ['liberiadollar','']),
+    ('LSL', ['lesotholoti','']),
+    ('LYD', ['libyadinar','']),
+    ('MAD', ['moroccodirham','']),
+    ('MDL', ['moldovaleu','']),
+    ('MGA', ['madagascarariary','']),
+    ('MKD', ['macedoniadenar','']),
+    ('MMK', ['myanmarkyat','']),
+    ('MNT', ['mongoliatugrik','']),  
+    ('MOP', ['macaupataca','']),
+    ('MRO', ['mauritaniaoldouguiya','1|10 MRU']),
+    ('MRU', ['mauritaniaouguiya', '']), 
+    ('MUR', ['mauritiusrupee','']),
+    ('MVR', ['maldiverufiyaa','']),
+    ('MWK', ['malawikwacha','']),
+    ('MXN', ['mexicopeso','']),
+    ('MYR', ['malaysiaringgit','']),
+    ('MZN', ['mozambiquemetical','']),
+    ('NAD', ['namibiadollar','']),
+    ('NGN', ['nigerianaira','']),
+    ('NIO', ['nicaraguacordobaoro','']),
+    ('NOK', ['norwaykrone','']),
+    ('NPR', ['nepalrupee','']),
+    ('NZD', ['newzealanddollar','']),
+    ('OMR', ['omanrial','']),
+    ('PAB', ['panamabalboa','']),
+    ('PEN', ['perunuevosol','']),
+    ('PGK', ['papuanewguineakina','']),
+    ('PHP', ['philippinepeso','']),
+    ('PKR', ['pakistanrupee','']),
+    ('PLN', ['polandzloty','']),
+    ('PYG', ['paraguayguarani','']),
+    ('QAR', ['qatarrial','']),
+    ('RON', ['romanianewlei','']),
+    ('RSD', ['serbiadinar','']),
+    ('RUB', ['russiaruble','']),
+    ('RWF', ['rwandafranc','']),
+    ('SAR', ['saudiarabiariyal','']),
+    ('SBD', ['solomonislandsdollar','']),
+    ('SCR', ['seychellesrupee','']),
+    ('SDG', ['sudanpound','']),
+    ('SEK', ['swedenkrona','']),
+    ('SGD', ['singaporedollar','']),
+    ('SHP', ['sainthelenapound','']),
+#    ('SLL', ['sierraleoneoldleone','1|1000 SLE']),
+#    ('SLE', ['sierraleoneleone','']),
+    ('SOS', ['somaliaschilling','']),
+    ('SRD', ['surinamedollar','']),
+    ('SSP', ['southsudanpound','']),
+    ('STD', ['saotome&principeolddobra','']),
+    ('STN', ['saotome&principedobra','']),
+#    ('SVC', ['elsalvadorcolon','']),  # eliminated in 2001
+    ('SYP', ['syriapound','']),
+    ('SZL', ['swazilandlilangeni','']),
+    ('THB', ['thailandbaht','']),
+    ('TJS', ['tajikistansomoni','']),
+    ('TMT', ['turkmenistanmanat','']),
+    ('TND', ['tunisiadinar','']),
+    ('TOP', ["tongapa'anga",'']),
+    ('TRY', ['turkeylira','']),
+    ('TTD', ['trinidadandtobagodollar','']),
+    ('TWD', ['taiwandollar','']),
+    ('TZS', ['tanzaniashilling','']),
+    ('UAH', ['ukrainehryvnia','']),
+    ('UGX', ['ugandaschilling','']),
+    ('USD', ['US$', '']),
+    ('UYU', ['uruguaypeso','']),
+    ('UZS', ['uzbekistansum','']),
+    ('VEF', ['venezuelabolivarfuerte','']),
+    ('VES', ['venezuelabolivarsoberano','']),
+    ('VND', ['vietnamdong','']),
+    ('VUV', ['vanuatuvatu','']),
+    ('WST', ['samoatala','']),
+    ('XAF', ['centralafricacfafranc','']),
+    ('XCD', ['eastcaribbeandollar','']),
+    ('XDR', ['specialdrawingrights','']),
+    ('YER', ['yemenrial','']),
+    ('ZAR', ['southafricarand','']),
+    ('ZMW', ['zambiakwacha','']),
+    ('ZWL', ['zimbabwedollar','']),
+    ('FOK', ['faroeislandskróna','DKK']),
+    ('GGP', ['guernseypound', 'GBP']),
+    ('IMP', ['isleofmanpound','GBP']),
+    ('JEP', ['jerseypound','GBP']),
+    ('KID', ['kiribatidollar','AUD']),
+    ('TVD', ['tuvaludollar','AUD']),
+])
+
+
+def validfloat(x):
+  try:
+    float(x)
+    return True
+  except ValueError:
+    return False
+
+def addrate(verbose,form,code,rate):
+  if code not in currency.keys():
+    if (verbose):
+      stderr.write('Got unknown currency with code {}\n'.format(code))
+  else:
+    if not currency[code][rate_index]:
+      if validfloat(rate):
+        currency[code][rate_index] = form.format(rate)
+      else:
+        stderr.write('Got invalid rate "{}" for currency "{}"\n'.format(
+                                  rate, code))
+    elif verbose:
+      if currency[code][rate_index] != form.format(rate):
+        stderr.write('Got value "{}" for currency "{}" but '
+                   'it is already defined as {}\n'.format(rate, code,
+                                                  currency[code][rate_index]))
+
+def getjson(address,args=None):
+  try:
+    res = requests.get(address,args)
+    res.raise_for_status()
+    return(res.json())
+  except requests.exceptions.RequestException as e:
+    stderr.write('Error connecting to currency server:\n{}.\n'.format(e))
+    exit(1)
+        
+########################################################
+#
+#  Connect to floatrates for currency update
+#
+
+def floatrates(verbose,base,dummy):
+  webdata = getjson('https://www.floatrates.com/daily/'+base+'.json')
+  for index in webdata:
+    entry = webdata[index]
+    if 'rate' not in entry or 'code' not in entry:    # Skip empty/bad entries
+      if verbose:
+        stderr.write('Got bad entry from server: '+str(entry)+'\n')
+    else:      
+      addrate(verbose,'{} '+base,entry['code'],entry['inverseRate'])
+  currency[base][rate_index] = PRIMITIVE
+  return('FloatRates ('+base+' base)')
+
+########################################################
+#
+# Connect to European central bank site
+#
+
+def eubankrates(verbose,base,dummy):
+  if verbose and base!='EUR':
+    stderr.write('European bank uses euro for base currency.  Specified base {} ignored.\n'.format(base))
+  import xml.etree.ElementTree as ET
+  try:
+    res=requests.get('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')
+    res.raise_for_status()
+    data = ET.fromstring(res.content)[2][0]
+  except requests.exceptions.RequestException as e:
+    stderr.write('Error connecting to currency server:\n{}.\n'.
+                 format(e))
+    exit(1)
+  for entry in data.iter():
+    if entry.get('time'):
+      continue
+    rate = entry.get('rate')
+    code = entry.get('currency')
+    if not rate or not code:                # Skip empty/bad entries
+      if verbose:
+        stderr.write('Got bad entry from server, code {} and rate {}\n'.format(code,rate))
+    else:      
+      addrate(verbose,'1|{} euro', code, rate)
+  currency['EUR'][rate_index]=PRIMITIVE
+  return('the European Central Bank (euro base)')
+      
+########################################################
+#
+# Connect to fixer.io (requires API key)
+#
+# Free API key does not allow changing base currency
+# With free key only euro base is supported, and https is not allowed
+#
+      
+def fixer(verbose,base,key):
+  if not key:
+    stderr.write('API key required for this source\n')
+    exit(1)
+  if verbose and base!='EUR':
+    stderr.write('Fixer uses euro for base currency.  Specified base {} ignored.\n'.format(base))
+  webdata = getjson('http://data.fixer.io/api/latest', {'access_key':key})
+  if not webdata['success']:
+    stderr.write('Currency server error: '+webdata['error']['info'])
+    exit(1)
+  for code in webdata['rates']:
+    addrate(verbose,'1|{} euro', code, webdata['rates'][code])
+  currency['EUR'][rate_index] = PRIMITIVE
+  return('Fixer (euro base)')
+
+########################################################
+#
+# Connect to openexchangerates (requires API key)
+#
+# Free API key does not allow changing the base currency
+#
+         
+def openexchangerates(verbose,base,key):
+  if not key:
+    stderr.write('API key required for this source\n')
+    exit(1)
+  if verbose and base!='USD':
+    stderr.write('Open Exchange Rates uses US dollar for base currency.  Specified base {} ignored.\n'.format(base))
+  webdata = getjson('https://openexchangerates.org/api/latest.json',
+                    {'app_id':key}
+                    )
+  for code in webdata['rates']:
+    addrate(verbose,'1|{} US$', code, webdata['rates'][code])
+  currency['USD'][rate_index] = PRIMITIVE
+  return('open exchange rates (USD base)')
+
+########################################################
+#
+# Connect to exchangerate-api.com
+#
+# The open access endpoint gives updates once per day.
+# User can optionally supply a paid account's API key to get newer data and more precision.
+# Base currency can be changed with both open and paid versions of the API.
+#
+
+def exchangerate_api(verbose,base,key):
+  if not key:
+    webdata = getjson('https://open.er-api.com/v6/latest/'+base)
+    if not webdata['result'] or webdata['result'] != 'success':
+      stderr.write('Currency server error: '+webdata['error-type']+'\n')
+      exit(1)
+    for code, rate in webdata['rates'].items() :
+      addrate(verbose,'1|{} '+base,code,rate)
+  else:
+    webdata = getjson('https://v6.exchangerate-api.com/v6/'+key+'/latest/'+base)
+    if not webdata['result'] or webdata['result'] != 'success':
+      stderr.write('Currency server error: '+webdata['error-type']+'\n')
+      exit(1)
+    for code, rate in webdata['conversion_rates'].items() :
+      addrate(verbose,'1|{} '+base,code,rate)
+  currency[base][rate_index] = PRIMITIVE
+  return('exchangerate-api.com ('+base+' base)')
+
+
+#######################################################
+#
+# list of valid source names and corresponding functions
+#
+
+sources = {       
+  'exchangerate_api': exchangerate_api,  
+  'floatrates': floatrates,
+  'eubank' : eubankrates,
+  'fixer' : fixer,
+  'openexchangerates': openexchangerates,
+}
+
+default_currency = 'exchangerate_api'
+
+#######################################################
+#
+# Argument Processing
+#
+
+ap = ArgumentParser(
+    description="Update currency information for 'units' "
+    "into the specified filenames or the default currency "
+    "file name {} and CPI file name {}.  The special "
+    "filename '-' will send either or both files to "
+    "stdout.".format(currency_file,cpi_file),
+)
+
+ap.add_argument(
+    'currency_file',
+    default=currency_file,
+    help='the file to update',
+    metavar='currency_file',
+    nargs='?',
+    type=str,
+)
+
+ap.add_argument(
+    'cpi_file',
+    default=cpi_file,
+    help='the file to update',
+    metavar='cpi_file',
+    nargs='?',
+    type=str,
+)
+
+ap.add_argument('-V','--version',
+                action='version',
+                version='%(prog)s version ' + version,
+                help='display units_cur version',
+)
+
+ap.add_argument('-v','--verbose',
+                action='store_true',
+                help='display details when fetching currency data',
+)
+
+ap.add_argument('-s','--source',choices=list(sources.keys()),
+                default=default_currency, 
+                help='set currency data source (default: {})'.format(default_currency),
+)
+
+ap.add_argument('-b','--base',default='USD',
+                help='set the base currency (when allowed by source).  BASE should be a 3 letter ISO currency code, e.g. USD.  The specified currency will be the primitive currency unit used by units.  Only the floatrates source supports this option.',
+)                
+
+ap.add_argument('-k','--key',default='',
+                help='set API key for sources that require it'
+)
+
+ap.add_argument('--blskey',default='',
+                help='set BLS key for fetching CPI data'
+)
+
+
+args = ap.parse_args()  
+currency_file = args.currency_file
+cpi_file = args.cpi_file
+verbose = args.verbose
+source = args.source
+base = args.base
+apikey = args.key
+cpikey = args.blskey
+
+if base not in currency.keys():
+  stderr.write('Base currency {} is not a known currency code.\n'.format(base))
+  exit(1)
+
+########################################################
+#
+# Fetch currency data from specified curerncy source
+#
+
+sourcename = sources[source](verbose,base,apikey)
+            
+# Delete currencies where we have no rate data
+for code in list(currency.keys()):
+  if not currency[code][rate_index]:
+    if verbose:
+      stderr.write('No data for {}\n'.format(code))
+    del currency[code]
+
+cnames = [currency[code][0] for code in currency.keys()]
+crates = [currency[code][1] for code in currency.keys()]
+
+codestr = '\n'.join('{:23}{}'.
+   format(code, name) for (code,name) in zip(currency.keys(), cnames))
+
+maxlen = max(len(name) for name in cnames) + 2
+
+ratestr = '\n'.join(
+    '{:{}}{}'.format(name, maxlen, rate) for (name, rate) in zip(cnames, crates)
+    )
+
+#######################################################
+#
+# Get precious metals data and bitcoin
+#
+
+metals = getjson('https://services.packetizer.com/spotprices',{'f':'json'})
+bitcoin = getjson('https://services.packetizer.com/btc',{'f':'json'})
+
+metallist = ['']*len(validmetals)
+for metal, price in metals.items():
+  if metal in validmetals:
+    metalindex = validmetals.index(metal)
+    if validfloat(price):
+      if not metallist[metalindex]:
+        metallist[validmetals.index(metal)] = '{:19}{} US$/troyounce'.format(
+                                                         metal + 'price', price)
+      elif verbose:
+        stderr.write('Got value "{}" for metal "{}" but '
+                     'it is already defined\n'.format(price,metal))
+    else:
+      stderr.write('Got invalid rate "{}" for metal "{}"\n'.format(price,metal))
+  elif metal != 'date' and verbose:  # Don't print a message for the "date" entry
+    stderr.write('Got unknown metal "{}" with value "{}"\n'.format(metal,price))
+metalstr = '\n'.join(metallist)
+
+if validfloat(bitcoin['usd']):
+  bitcoinstr = '{:{}}{} US$ # From services.packetizer.com/btc\n'.format(
+                'bitcoin',maxlen,bitcoin['usd'])
+else:
+  stderr.write('Got invalid bitcoin rate "{}"\n', bitcoint['usd'])
+  bitcointstr=''
+
+#######################################################
+#
+# Get CPI data
+#
+
+docpi=False
+if cpi_file=='-':
+  docpi=True
+else:
+  try:
+    filedate = getmtime(cpi_file)
+    filedate = date.fromtimestamp(filedate)
+    today = date.today()
+    dmonth = (today.month-filedate.month) + 12*(today.year-filedate.year)
+    if dmonth>1 or (dmonth==1 and today.day>18):
+      docpi=True
+  except FileNotFoundError:
+    docpi=True
+  
+if docpi:
+  headers = {'Content-type': 'application/json'}
+  yearlist = list(range(date.today().year,1912,-10))
+  if yearlist[-1]>1912:
+    yearlist.append(1912)
+  cpi=[]
+  lastcpi = 0
+  query = {"seriesid": ['CUUR0000SA0']}
+  if cpikey:
+    query["registrationkey"]=cpikey
+  ########################################################################
+  # The api.bls.gov site currently (2024-02-15) resolves to an
+  # IPv4 address which works, and an IPv6 address which does
+  # not.  The urllib3 package does not currently implement the
+  # Happy Eyeballs algorithm, nor any other mechanism to re-try
+  # hung connections with alternative addresses returned by DNS.
+  # In the interest of expediency, we temporarily force the
+  # connection to api.bls.gov to only use IPv4; hopefully at
+  # some future date either urllib3 will gain the necessary
+  # features and/or the BLS will fix their configuration so
+  # that all A and AAAA records for api.bls.gov resolve to an
+  # operational server.  At that time we can remove the three
+  # references to "requests.packages.urllib3.util.connection.HAS_IPV6"
+  # in this function.
+  ########################################################################
+  save_rpuucH = requests.packages.urllib3.util.connection.HAS_IPV6
+  requests.packages.urllib3.util.connection.HAS_IPV6 = False
+  for endyear in range(len(yearlist)-1):
+    query["startyear"]=str(yearlist[endyear+1]+1)
+    query["endyear"]=str(yearlist[endyear])
+    data = json.dumps(query)
+    p = requests.post('https://api.bls.gov/publicAPI/v2/timeseries/data/', data=data, headers=headers)
+    json_data = json.loads(p.text)
+    if json_data['status']=="REQUEST_NOT_PROCESSED":
+      docpi=False
+      stderr.write("Unable to update CPI data: Exceeded daily threshold for BLS requests\n")
+      break
+    for series in json_data['Results']['series']:
+        for item in series['data']:
+            if not ('M01' <= item['period'] <= 'M12'):
+                continue
+            year = int(item['year']) + int(item['period'][1:])/12
+            value = item['value']
+            cpi.append(' '*10 + "{} {} \\".format(year,value))
+            if lastcpi==0:
+              lastcpi=value
+              lastyear=year
+            firstcpi=value
+            firstyear=year
+  requests.packages.urllib3.util.connection.HAS_IPV6 = save_rpuucH              
+if docpi:   # Check again because request may have failed
+  cpi.reverse()
+  cpistr = '\n'.join(cpi)
+
+  cpi_text = ("""!message Consumer price index data from US BLS, {datestr}
+
+UScpi[1] noerror \\
+{cpistr}
+
+UScpi_now          {lastcpi}
+UScpi_lastdate     {lastyear}
+
+USdollars_in(date) units=[1;$] domain=[{firstyear},{lastyear}] \\
+                               range=[1,{maxinfl}] \\
+                               US$ UScpi_now / UScpi(date) ;\\
+                               ~UScpi(US$ UScpi_now / USdollars_in)
+USinflation_since(date) units=[1;1] domain=[{firstyear},{lastyear}] \\
+                               range=[1,{maxinfl}] \\
+                                UScpi_now / UScpi(date) ;\\
+                               ~UScpi(UScpi_now / USinflation_since)
+
+""".format(datestr=datestr,cpistr=cpistr,maxinfl=float(lastcpi)/float(firstcpi),lastcpi=lastcpi,
+           firstyear=firstyear,lastyear=lastyear)
+  ).replace('\n', linesep)  
+  
+#######################################################
+#
+# Format output and write the currency file
+#
+
+currency_text = (
+"""# ISO Currency Codes
+
+{codestr}
+
+# Currency exchange rates source 
+
+!message Currency exchange rates from {sourcename} on {datestr}
+
+{ratestr}
+{bitcoinstr}
+
+# Precious metals prices from Packetizer (services.packetizer.com/spotprices)
+
+{metalstr}
+
+""".format(codestr=codestr, datestr=datestr, ratestr=ratestr, metalstr=metalstr,
+           bitcoinstr=bitcoinstr, sourcename=sourcename)
+).replace('\n', linesep)
+
+
+######################################
+
+try:
+    if currency_file == '-':
+        codecs.StreamReader(stdout, codecs.getreader('utf8')).write(currency_text)
+    else:    
+        with codecs.open(currency_file, 'w', 'utf8') as of:
+            of.write(currency_text)
+except IOError as e:
+    stderr.write('Unable to write to output file:\n{}\n'.format(e))
+    exit(1)
+
+if docpi:    
+  try:
+      if cpi_file == '-':
+          codecs.StreamReader(stdout, codecs.getreader('utf8')).write(cpi_text)
+      else:    
+          with codecs.open(cpi_file, 'w', 'utf8') as of:
+              of.write(cpi_text)
+  except IOError as e:
+      stderr.write('Unable to write to output file:\n{}\n'.format(e))
+      exit(1)
+    
diff --git a/unitsfile.ico b/unitsfile.ico
new file mode 100644
index 0000000..37388df
--- /dev/null
+++ b/unitsfile.ico
Binary files differ
diff --git a/unitsprog.ico b/unitsprog.ico
new file mode 100644
index 0000000..66a619e
--- /dev/null
+++ b/unitsprog.ico
Binary files differ