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(/ /, "")
+ 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