[geographiclib] 01/06: Imported Upstream version 1.45

Ross Gammon ross-guest at moszumanska.debian.org
Wed Sep 30 19:29:28 UTC 2015


This is an automated email from the git hooks/post-receive script.

ross-guest pushed a commit to branch master
in repository geographiclib.

commit 9cd6d03828c5bac4d38a83395b5601279cd1a5fe
Author: Ross Gammon <rossgammon at mail.dk>
Date:   Wed Sep 30 20:08:37 2015 +0200

    Imported Upstream version 1.45
---
 AUTHORS                                            |    2 +
 CMakeLists.txt                                     |   18 +-
 LICENSE.txt                                        |    2 +-
 Makefile.am                                        |   10 +-
 Makefile.in                                        |   10 +-
 Makefile.mk                                        |   21 +-
 NEWS                                               |   66 +-
 configure                                          |   30 +-
 configure.ac                                       |   10 +-
 doc/CMakeLists.txt                                 |   22 +-
 doc/GeographicLib.dox.in                           |  125 ++-
 doc/Makefile.am                                    |   73 +-
 doc/Makefile.in                                    |   73 +-
 doc/Makefile.mk                                    |    8 -
 doc/geodesic-c.dox                                 |   25 +-
 doc/geodesic-for.dox                               |   27 +-
 doc/scripts/GeographicLib/Interface.js             |  239 -----
 doc/scripts/GeographicLib/Math.js                  |  230 -----
 doc/scripts/GeographicLib/PolygonArea.js           |  253 -----
 doc/scripts/geod-google.html                       |  231 -----
 dotnet/NETGeographicLib/AlbersEqualArea.h          |    9 +-
 dotnet/NETGeographicLib/Ellipsoid.h                |    3 +-
 dotnet/NETGeographicLib/Geocentric.h               |    3 +-
 dotnet/NETGeographicLib/Geodesic.h                 |   23 +-
 dotnet/NETGeographicLib/GeodesicExact.h            |    3 +-
 dotnet/NETGeographicLib/LambertConformalConic.h    |    9 +-
 dotnet/NETGeographicLib/PolarStereographic.h       |    3 +-
 dotnet/NETGeographicLib/Rhumb.h                    |    3 +-
 dotnet/NETGeographicLib/TransverseMercator.h       |    3 +-
 dotnet/NETGeographicLib/TransverseMercatorExact.h  |    3 +-
 examples/JacobiConformal.cpp                       |   11 +
 include/GeographicLib/Accumulator.hpp              |    2 +-
 include/GeographicLib/AlbersEqualArea.hpp          |    9 +-
 include/GeographicLib/Config.h                     |    4 +-
 include/GeographicLib/Config.h.in                  |    2 +-
 include/GeographicLib/DMS.hpp                      |    4 +-
 include/GeographicLib/Ellipsoid.hpp                |    3 +-
 include/GeographicLib/Geocentric.hpp               |    3 +-
 include/GeographicLib/Geodesic.hpp                 |   23 +-
 include/GeographicLib/GeodesicExact.hpp            |    3 +-
 include/GeographicLib/Gnomonic.hpp                 |    6 +-
 include/GeographicLib/LambertConformalConic.hpp    |    9 +-
 include/GeographicLib/PolarStereographic.hpp       |    3 +-
 include/GeographicLib/PolygonArea.hpp              |    2 +
 include/GeographicLib/Rhumb.hpp                    |    3 +-
 include/GeographicLib/TransverseMercator.hpp       |    3 +-
 include/GeographicLib/TransverseMercatorExact.hpp  |    3 +-
 include/GeographicLib/Utility.hpp                  |   27 +-
 java/direct/pom.xml                                |    4 +-
 java/inverse/pom.xml                               |    4 +-
 java/planimeter/pom.xml                            |    4 +-
 java/pom.xml                                       |    9 +-
 .../main/java/net/sf/geographiclib/GeoMath.java    |    9 +-
 .../main/java/net/sf/geographiclib/Geodesic.java   |   55 +-
 .../java/net/sf/geographiclib/GeodesicLine.java    |   14 +-
 .../java/net/sf/geographiclib/GeodesicMask.java    |   15 +-
 .../main/java/net/sf/geographiclib/Gnomonic.java   |  281 ++++++
 .../java/net/sf/geographiclib/GnomonicData.java    |   98 ++
 .../java/net/sf/geographiclib/PolygonArea.java     |    2 +
 .../java/net/sf/geographiclib/package-info.java    |   37 +-
 .../java/net/sf/geographiclib/GeodesicTest.java    |  509 ++++++++++
 js/CMakeLists.txt                                  |  140 +++
 js/GeographicLib.md                                |   60 ++
 js/HEADER.js                                       |   22 +
 js/Makefile.am                                     |   44 +
 {python => js}/Makefile.in                         |   80 +-
 js/Makefile.mk                                     |   36 +
 js/README.md                                       |   47 +
 js/conf.json                                       |   17 +
 js/doc/1-geodesics.md                              |  166 ++++
 js/doc/2-interface.md                              |  111 +++
 js/doc/3-examples.md                               |  278 ++++++
 js/doc/tutorials.json                              |   11 +
 js/js-cat.sh                                       |   37 +
 js/js-compress.sh                                  |   38 +
 js/package.json                                    |   37 +
 {doc/scripts => js/samples}/geod-calc.html         |  156 ++-
 .../samples}/geod-google-instructions.html         |   10 +-
 js/samples/geod-google.html                        |  340 +++++++
 {doc/scripts/GeographicLib => js/src}/DMS.js       |  324 +++---
 {doc/scripts/GeographicLib => js/src}/Geodesic.js  |  907 +++++++++--------
 .../GeographicLib => js/src}/GeodesicLine.js       |  159 ++-
 js/src/Math.js                                     |  414 ++++++++
 js/src/PolygonArea.js                              |  318 ++++++
 js/test/geodesictest.js                            |  514 ++++++++++
 legacy/C/CMakeLists.txt                            |   13 +-
 legacy/C/geodesic.c                                |   28 +-
 legacy/C/geodesic.h                                |   50 +-
 legacy/C/geodtest.c                                |  635 ++++++++++++
 legacy/Fortran/CMakeLists.txt                      |    8 +-
 legacy/Fortran/geoddirect.for                      |    5 -
 legacy/Fortran/geodesic.for                        |  243 +++--
 legacy/Fortran/geodesic.inc                        |    3 +
 legacy/Fortran/geodinverse.for                     |    5 -
 legacy/Fortran/geodtest.for                        | 1046 ++++++++++++++++++++
 man/CMakeLists.txt                                 |    4 +-
 man/CartConvert.1                                  |    7 +-
 man/CartConvert.1.html                             |    2 +-
 man/CartConvert.pod                                |    5 +-
 man/CartConvert.usage                              |    7 +-
 man/ConicProj.1                                    |    7 +-
 man/ConicProj.1.html                               |    2 +-
 man/ConicProj.pod                                  |    5 +-
 man/ConicProj.usage                                |    7 +-
 man/GeoConvert.1                                   |    2 +-
 man/GeoConvert.usage                               |    2 +-
 man/GeodSolve.1                                    |    7 +-
 man/GeodSolve.1.html                               |    2 +-
 man/GeodSolve.pod                                  |    5 +-
 man/GeodSolve.usage                                |    7 +-
 man/GeodesicProj.1                                 |    7 +-
 man/GeodesicProj.1.html                            |    2 +-
 man/GeodesicProj.pod                               |    5 +-
 man/GeodesicProj.usage                             |    7 +-
 man/GeoidEval.1                                    |    2 +-
 man/GeoidEval.usage                                |    2 +-
 man/Gravity.1                                      |    2 +-
 man/Gravity.usage                                  |    2 +-
 man/MagneticField.1                                |    2 +-
 man/MagneticField.usage                            |    2 +-
 man/Planimeter.1                                   |   12 +-
 man/Planimeter.1.html                              |    2 +-
 man/Planimeter.pod                                 |   10 +-
 man/Planimeter.usage                               |   13 +-
 man/RhumbSolve.1                                   |    7 +-
 man/RhumbSolve.1.html                              |    2 +-
 man/RhumbSolve.pod                                 |    5 +-
 man/RhumbSolve.usage                               |    7 +-
 man/TransverseMercatorProj.1                       |    9 +-
 man/TransverseMercatorProj.1.html                  |    2 +-
 man/TransverseMercatorProj.pod                     |    7 +-
 man/TransverseMercatorProj.usage                   |    9 +-
 matlab/Makefile.am                                 |    1 +
 matlab/Makefile.in                                 |    1 +
 matlab/geographiclib-legacy/Contents.m             |    2 -
 matlab/geographiclib-legacy/geocentricforward.m    |    2 -
 matlab/geographiclib-legacy/geocentricreverse.m    |    2 -
 matlab/geographiclib-legacy/geodesicdirect.m       |    2 -
 matlab/geographiclib-legacy/geodesicinverse.m      |    2 -
 matlab/geographiclib-legacy/geodesicline.m         |    2 -
 matlab/geographiclib-legacy/geoidheight.m          |    2 -
 .../geographiclib-legacy/localcartesianforward.m   |    2 -
 .../geographiclib-legacy/localcartesianreverse.m   |    2 -
 matlab/geographiclib-legacy/mgrsforward.m          |    2 -
 matlab/geographiclib-legacy/mgrsreverse.m          |    2 -
 matlab/geographiclib-legacy/polygonarea.m          |    2 -
 matlab/geographiclib-legacy/utmupsforward.m        |    2 -
 matlab/geographiclib-legacy/utmupsreverse.m        |    2 -
 matlab/geographiclib/Contents.m                    |    5 +-
 matlab/geographiclib/cassini_fwd.m                 |    2 -
 matlab/geographiclib/cassini_inv.m                 |    2 -
 matlab/geographiclib/eqdazim_fwd.m                 |    2 -
 matlab/geographiclib/eqdazim_inv.m                 |    2 -
 matlab/geographiclib/gedistance.m                  |    3 +-
 matlab/geographiclib/gedoc.m                       |    2 -
 matlab/geographiclib/geocent_fwd.m                 |    2 -
 matlab/geographiclib/geocent_inv.m                 |    2 -
 matlab/geographiclib/geodarea.m                    |    2 -
 matlab/geographiclib/geoddistance.m                |  141 +--
 matlab/geographiclib/geoddoc.m                     |    6 +-
 matlab/geographiclib/geodreckon.m                  |    2 -
 matlab/geographiclib/geographiclib_test.m          |  554 +++++++++++
 matlab/geographiclib/geoid_height.m                |    2 -
 matlab/geographiclib/geoid_load.m                  |    2 -
 matlab/geographiclib/gereckon.m                    |    2 -
 matlab/geographiclib/gnomonic_fwd.m                |    2 -
 matlab/geographiclib/gnomonic_inv.m                |    2 -
 matlab/geographiclib/loccart_fwd.m                 |    2 -
 matlab/geographiclib/loccart_inv.m                 |    2 -
 matlab/geographiclib/mgrs_fwd.m                    |    6 +-
 matlab/geographiclib/mgrs_inv.m                    |    2 -
 matlab/geographiclib/polarst_fwd.m                 |    2 -
 matlab/geographiclib/polarst_inv.m                 |    2 -
 matlab/geographiclib/private/atan2dx.m             |    3 +-
 matlab/geographiclib/private/sincosdx.m            |    3 +-
 matlab/geographiclib/projdoc.m                     |    2 -
 matlab/geographiclib/tranmerc_fwd.m                |   22 +-
 matlab/geographiclib/tranmerc_inv.m                |   19 +-
 matlab/geographiclib/utmups_fwd.m                  |    2 -
 matlab/geographiclib/utmups_inv.m                  |    2 -
 maxima/geodesic.mac                                |   12 +-
 pom.xml                                            |    2 +-
 python/Makefile.am                                 |    2 +-
 python/Makefile.in                                 |    2 +-
 python/geographiclib/__init__.py                   |    4 +-
 python/geographiclib/geodesic.py                   |   45 +-
 python/geographiclib/geodesiccapability.py         |    1 +
 python/geographiclib/geodesicline.py               |   17 +-
 python/geographiclib/geomath.py                    |    9 +-
 python/geographiclib/polygonarea.py                |    2 +-
 python/setup.py                                    |   23 +-
 python/test/__init__.py                            |   12 +
 python/test/test_geodesic.py                       |  432 ++++++++
 src/AlbersEqualArea.cpp                            |    6 +-
 src/DMS.cpp                                        |    1 -
 src/Ellipsoid.cpp                                  |    2 +-
 src/Geocentric.cpp                                 |    4 +-
 src/Geodesic.cpp                                   |   19 +-
 src/GeodesicExact.cpp                              |   18 +-
 src/GeographicLib.pro                              |    2 +-
 src/Gnomonic.cpp                                   |    7 +-
 src/LambertConformalConic.cpp                      |    6 +-
 src/PolarStereographic.cpp                         |    2 +-
 src/TransverseMercator.cpp                         |    2 +-
 src/TransverseMercatorExact.cpp                    |    2 +-
 tools/tests.cmake                                  |  112 ++-
 206 files changed, 8268 insertions(+), 2503 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 179456e..01395c2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -5,3 +5,5 @@ Andrew MacIntyre <Andrew.MacIntyre at acma.gov.au> (python/setup.py)
 Skip Breidbach <skip at waywally.com> (maven support for Java)
 Scott Heiman <mrmtdew2 at outlook.com> (.NET wrappers + C# examples)
 Chris Bennight <chris at bennight.com> (deploying Java library)
+Sebastian Mattheis <Sebastian.Mattheis at bmw.de> (gnomonic projection in Java)
+Yurij Mikhalevich <0 at 39.yt> (node.js port)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b51b720..09d18a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ project (GeographicLib)
 
 # Version information
 set (PROJECT_VERSION_MAJOR 1)
-set (PROJECT_VERSION_MINOR 44)
+set (PROJECT_VERSION_MINOR 45)
 set (PROJECT_VERSION_PATCH 0)
 set (PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
 if (PROJECT_VERSION_PATCH GREATER 0)
@@ -43,7 +43,7 @@ endif ()
 # The library version tracks the numbering given by libtool in the
 # autoconf set up.
 set (LIBVERSION_API 14)
-set (LIBVERSION_BUILD 14.2.0)
+set (LIBVERSION_BUILD 14.2.1)
 string (TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
 string (TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER)
 
@@ -410,6 +410,7 @@ if (GEOGRAPHICLIB_DOCUMENTATION)
   set (DOXYGEN_SKIP_DOT ON)
   # Version 1.8.7 or later needed for …
   find_package (Doxygen 1.8.7)
+  find_program (JSDOC jsdoc)
 endif ()
 
 # The man pages are written as pod files and converted to nroff format,
@@ -467,6 +468,7 @@ add_subdirectory (include/GeographicLib)
 add_subdirectory (tools)
 add_subdirectory (man)
 add_subdirectory (doc)
+add_subdirectory (js)
 add_subdirectory (matlab)
 add_subdirectory (python/geographiclib)
 add_subdirectory (examples)
@@ -516,7 +518,7 @@ set (CPACK_SOURCE_IGNORE_FILES
   "${PROJECT_SOURCE_DIR}/(tests|testdata|cgi-bin|.*\\\\.cache)/"
   "${PROJECT_SOURCE_DIR}/(distrib|.*-distrib|.*-installer|geodesic-papers)/"
   "${PROJECT_SOURCE_DIR}/[^/]*\\\\.(html|kmz|pdf)\$"
-  "${PROJECT_SOURCE_DIR}/(autogen|biblio|js-compress)\\\\.sh\$"
+  "${PROJECT_SOURCE_DIR}/(autogen|biblio)\\\\.sh\$"
   "${PROJECT_SOURCE_DIR}/(geodesic-biblio.txt|makefile-admin|[^/]*\\\\.png)\$"
   "${PROJECT_SOURCE_DIR}/matlab/.*blurb.txt\$" )
 set (CPACK_SOURCE_GENERATOR TGZ)
@@ -532,15 +534,15 @@ if (WIN32)
   set (CPACK_NSIS_URL_INFO_ABOUT "http://geographiclib.sf.net")
   set (CPACK_NSIS_HELP_LINK "mailto:charles at karney.com")
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
-    # Hardcode the prefix for Visual Studio 10
-    set (CPACK_NSIS_INSTALL_ROOT "C:\\\\pkg-vc10-x64")
+    # Hardcode the prefix for Visual Studio 11
+    set (CPACK_NSIS_INSTALL_ROOT "C:\\\\pkg-vc11-x64")
     set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}-win64")
     set (CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME} x64 ${PROJECT_VERSION}")
     set (CPACK_PACKAGE_INSTALL_REGISTRY_KEY
       "${PROJECT_NAME}-x64-${PROJECT_VERSION}")
   else ()
-    # Hardcode the prefix for Visual Studio 10
-    set (CPACK_NSIS_INSTALL_ROOT "C:\\\\pkg-vc10")
+    # Hardcode the prefix for Visual Studio 11
+    set (CPACK_NSIS_INSTALL_ROOT "C:\\\\pkg-vc11")
     set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}-win32")
     set (CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME} ${PROJECT_VERSION}")
     set (CPACK_PACKAGE_INSTALL_REGISTRY_KEY
@@ -604,7 +606,7 @@ if (MAINTAINER)
       _CPack_Packages/Linux-Source/TGZ.DOS/ &&
       cd _CPack_Packages/Linux-Source/TGZ.DOS &&
       find . -type f |
-      egrep  '/\(doxyfile.*\\.in|MANIFEST.in|NEWS|AUTHORS|INSTALL|pom\\.xml|dummy.*\\.in|.*\\.\(cpp|hpp|h\\.in|txt|pro|usage|pod|py|m|mac|cmake\\.in|cmake|h|js|c|for|dox|cs|vb|inc|java|html\\.in\)\)$$' |
+      egrep -v '/\(compile|config[^/]*|depcomp|install-sh|missing|[Mm]akefile[^/]*|[^/]*\\.\(ac|am|csproj|eps|kmz|m4|pdf|png|resx|settings|sh|sln|vcproj|vcxproj\)\)$$' |
       xargs unix2dos -q -k &&
       find ${CPACK_SOURCE_PACKAGE_FILE_NAME} -type f |
       zip -q ${CMAKE_BINARY_DIR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip -@
diff --git a/LICENSE.txt b/LICENSE.txt
index 47af155..1b2387d 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
 This license applies to GeographicLib, versions 1.12 and later.
 
-Copyright (c) 2008-2014, Charles Karney
+Copyright (c) 2008-2015, Charles Karney
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
diff --git a/Makefile.am b/Makefile.am
index b637a97..25ba129 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,10 +7,10 @@ AUTOMAKE_OPTIONS = foreign
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = src man tools doc include matlab python cmake examples
+SUBDIRS = src man tools doc js include matlab python cmake examples
 
 EXTRA_DIST = AUTHORS 00README.txt LICENSE.txt NEWS INSTALL README.md pom.xml \
-	Makefile.mk CMakeLists.txt windows maxima doc legacy java dotnet
+	Makefile.mk CMakeLists.txt windows maxima doc legacy java js dotnet
 
 dist-hook:
 	rm -rf $(distdir)/doc/html $(distdir)/doc/manpages \
@@ -32,10 +32,11 @@ dist-hook:
 		-e "s/PATCH .*/PATCH ${GEOGRAPHICLIB_VERSION_PATCH}/" \
 		$(top_srcdir)/include/GeographicLib/Config.h > \
 		$(distdir)/include/GeographicLib/Config.h
+
 # Custom rules
 
 all-local: man doc
-install-data-local: install-doc # install-matlab
+install-data-local: install-doc
 
 doc: man
 	$(MAKE) -C doc doc
@@ -46,7 +47,4 @@ install-doc:
 man:
 	$(MAKE) -C man man
 
-# install-matlab:
-# 	$(MAKE) -C matlab install-matlab
-
 .PHONY: doc install-doc man install-matlab install-python
diff --git a/Makefile.in b/Makefile.in
index 5f56b65..a2879a6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -336,9 +336,9 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src man tools doc include matlab python cmake examples
+SUBDIRS = src man tools doc js include matlab python cmake examples
 EXTRA_DIST = AUTHORS 00README.txt LICENSE.txt NEWS INSTALL README.md pom.xml \
-	Makefile.mk CMakeLists.txt windows maxima doc legacy java dotnet
+	Makefile.mk CMakeLists.txt windows maxima doc legacy java js dotnet
 
 all: all-recursive
 
@@ -844,10 +844,11 @@ dist-hook:
 		-e "s/PATCH .*/PATCH ${GEOGRAPHICLIB_VERSION_PATCH}/" \
 		$(top_srcdir)/include/GeographicLib/Config.h > \
 		$(distdir)/include/GeographicLib/Config.h
+
 # Custom rules
 
 all-local: man doc
-install-data-local: install-doc # install-matlab
+install-data-local: install-doc
 
 doc: man
 	$(MAKE) -C doc doc
@@ -858,9 +859,6 @@ install-doc:
 man:
 	$(MAKE) -C man man
 
-# install-matlab:
-# 	$(MAKE) -C matlab install-matlab
-
 .PHONY: doc install-doc man install-matlab install-python
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/Makefile.mk b/Makefile.mk
index e9d2b63..90fb16f 100644
--- a/Makefile.mk
+++ b/Makefile.mk
@@ -1,17 +1,18 @@
 MAKEFILE := $(lastword $(MAKEFILE_LIST))
 MAKE := $(MAKE) -f $(MAKEFILE)
-SUBDIRS = src man tools doc
+SUBDIRS = src man tools doc js
 ALLDIRS = include $(SUBDIRS) maxima matlab python cmake
 
-all: src man tools
+all: src man tools js
 
 $(SUBDIRS):
 	$(MAKE) -C $@
 
 tools: src
 install: install-headers install-lib install-tools install-man install-cmake \
-	install-doc install-matlab install-python
-clean: clean-src clean-tools clean-doc clean-man clean-matlab clean-python
+	install-doc install-js install-matlab install-python
+clean: clean-src clean-tools clean-doc clean-js clean-man clean-matlab \
+	clean-python
 
 install-headers:
 	$(MAKE) -C include install
@@ -23,6 +24,8 @@ install-cmake:
 	$(MAKE) -C cmake install
 install-doc: doc
 	$(MAKE) -C doc install
+install-js: js
+	$(MAKE) -C js install
 install-man: man
 	$(MAKE) -C man install
 install-matlab: matlab
@@ -35,6 +38,8 @@ clean-tools:
 	$(MAKE) -C tools clean
 clean-doc:
 	$(MAKE) -C doc clean
+clean-js:
+	$(MAKE) -C js clean
 clean-man:
 	$(MAKE) -C man clean
 clean-matlab: matlab
@@ -44,8 +49,8 @@ clean-python: python
 
 VERSION:=$(shell grep '\bVERSION=' configure | cut -f2 -d\' | head -1)
 
-.PHONY: all $(SUBDIRS) install \
-	install-headers install-lib install-tools install-cmake install-man \
-	install-matlab install-python \
-	clean clean-src clean-tools clean-doc clean-man clean-matlab \
+.PHONY: all $(SUBDIRS) install clean \
+	install-headers install-lib install-tools install-man install-cmake \
+	install-doc install-js install-matlab install-python \
+	clean-src clean-tools clean-doc clean-js clean-man clean-matlab \
 	clean-python
diff --git a/NEWS b/NEWS
index b4e3c16..b44ca10 100644
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,67 @@ For more information, see
 
     http://geographiclib.sourceforge.net/
 
-The current version of the library is 1.44.
+The current version of the library is 1.45.
+
+Changes between 1.45 (released 2015-09-30) and 1.44 versions:
+
+  * Fix BUG in solution of inverse geodesic caused by misbehavior of
+    some versions of Visual Studio on Windows (fmod(-0.0, 360.0) returns
+    +0.0 instead of -0.0) and Octave (sind(-0.0) returns +0.0 instead of
+    -0.0).  These bugs were exposed because max(-0.0, +0.0) returns -0.0
+    for some languages.
+
+  * Geodesic::Inverse now correctly returns NaNs if one of the latitudes
+    is a NaN.
+
+  * Changes to JavaScript package:
+    + thanks to help from Yurij Mikhalevich, it is a now a node package
+      that can be installed with npm;
+    + make install now installs the node package in
+      lib/node_modules/geographiclib;
+    + add unit tests using mocha;
+    + add documentation via JSDoc;
+    + fix bug Geodesic.GenInverse (this bug, introduced in version 1.44,
+      resulted in the wrong azimuth being reported for points at the
+      pole).
+
+  * Changes to Java package:
+    + add implementation of ellipsoidal Gnomonic projection (courtesy of
+      Sebastian Mattheis);
+    + add unit tests using JUnit;
+    + Math.toRadians and Math.toDegrees are used instead of
+      GeoMath.degree (which is now removed), as a result...
+    + Java version 1.2 (released 1998-12) or later is now required.
+
+  * Changes to Python package:
+    + add unit tests using the unittest framework;
+    + fixed bug in normalization of the area.
+
+  * Changes to MATLAB package:
+    + fix array size mismatch in geoddistance by avoiding calls to
+      subfunctions with zero-length arrays;
+    + fix tranmerc_{fwd,inv} so that they work with arrays and mixed
+      array/scalar arguments;
+    + work around Octave problem which causes mgrs_fwd to return garbage
+      with prec = 10 or 11;
+    + add geographiclib_test.m to run a test suite.
+
+  * Behavior of substituting 1/f for f if f > 1 is now deprecated.  This
+    behavior has been removed from the JavaScript, C, and Python
+    implementations (it was never documented).  Maxima, MATLAB, and
+    Fortran implementations never included this behavior.
+
+  * Other changes:
+    + fix bug, introduced in version 1.42, in the C++ implementation to
+      the computation of area which causes NaNs to be returned in the
+      case of a sphere;
+    + fixed bug, introduced in version 1.44, in the detection of C++11
+      math functions in configure.ac;
+    + throw error on non-convergence in Gnomonic::Reverse if
+      GEOGRAPHICLIB_PRECISION > 3;
+    + add geod_polygon_clear to C library;
+    + turn illegal latitudes into NaNs for Fortran library;
+    + add test suites for the C and Fortran libraries.
 
 Changes between 1.44 (released 2015-08-14) and 1.43 versions:
 
@@ -13,8 +73,8 @@ Changes between 1.44 (released 2015-08-14) and 1.43 versions:
     + Add Math::sincosd, Math::sind, Math::cosd which take their
       arguments in degrees.  These functions do exact range reduction
       and thus they obey exactly the elementary properties of the
-      trigonometric functions, e.g., sin 9d = cos 81d =
-      - sin 123456789d.
+      trigonometric functions, e.g., sin 9d = cos 81d
+      = - sin 123456789d.
     + Math::AngNormalize now works for any angles, instead of angles in
       the range [-540d, 540d); the function Math::AngNormalize2 is now
       deprecated.
diff --git a/configure b/configure
index a630f37..5815a26 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for GeographicLib 1.44.
+# Generated by GNU Autoconf 2.69 for GeographicLib 1.45.
 #
 # Report bugs to <charles at karney.com>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='GeographicLib'
 PACKAGE_TARNAME='geographiclib'
-PACKAGE_VERSION='1.44'
-PACKAGE_STRING='GeographicLib 1.44'
+PACKAGE_VERSION='1.45'
+PACKAGE_STRING='GeographicLib 1.45'
 PACKAGE_BUGREPORT='charles at karney.com'
 PACKAGE_URL=''
 
@@ -1343,7 +1343,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures GeographicLib 1.44 to adapt to many kinds of systems.
+\`configure' configures GeographicLib 1.45 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1414,7 +1414,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of GeographicLib 1.44:";;
+     short | recursive ) echo "Configuration of GeographicLib 1.45:";;
    esac
   cat <<\_ACEOF
 
@@ -1525,7 +1525,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-GeographicLib configure 1.44
+GeographicLib configure 1.45
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1966,7 +1966,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by GeographicLib $as_me 1.44, which was
+It was created by GeographicLib $as_me 1.45, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2943,7 +2943,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='geographiclib'
- VERSION='1.44'
+ VERSION='1.45'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -3037,7 +3037,7 @@ END
 fi
 
 GEOGRAPHICLIB_VERSION_MAJOR=1
-GEOGRAPHICLIB_VERSION_MINOR=44
+GEOGRAPHICLIB_VERSION_MINOR=45
 GEOGRAPHICLIB_VERSION_PATCH=0
 
 cat >>confdefs.h <<_ACEOF
@@ -3086,7 +3086,7 @@ ac_config_headers="$ac_config_headers include/GeographicLib/Config-ac.h"
 
 
 LT_CURRENT=16
-LT_REVISION=0
+LT_REVISION=1
 LT_AGE=2
 
 
@@ -15627,7 +15627,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 int
 main ()
 {
-return int(std::hypot(3.0, 4.0) + std::expm1(0.5) +
+int q;
+         return int(std::hypot(3.0, 4.0) + std::expm1(0.5) +
                     std::log1p(2.0) + std::asinh(10.0) +
                     std::atanh(0.8) + std::cbrt(8.0) +
                     std::fma(1.0, 2.0, 3.0) + std::remquo(100.0, 90.0, &q) +
@@ -15832,7 +15833,7 @@ else
 fi
 
 
-ac_config_files="$ac_config_files Makefile src/Makefile include/Makefile tools/Makefile doc/Makefile man/Makefile matlab/Makefile python/Makefile cmake/Makefile examples/Makefile"
+ac_config_files="$ac_config_files Makefile src/Makefile include/Makefile tools/Makefile doc/Makefile js/Makefile man/Makefile matlab/Makefile python/Makefile cmake/Makefile examples/Makefile"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -16381,7 +16382,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by GeographicLib $as_me 1.44, which was
+This file was extended by GeographicLib $as_me 1.45, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -16447,7 +16448,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-GeographicLib config.status 1.44
+GeographicLib config.status 1.45
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -16959,6 +16960,7 @@ do
     "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
     "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
     "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+    "js/Makefile") CONFIG_FILES="$CONFIG_FILES js/Makefile" ;;
     "man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;;
     "matlab/Makefile") CONFIG_FILES="$CONFIG_FILES matlab/Makefile" ;;
     "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;;
diff --git a/configure.ac b/configure.ac
index 454faae..13b13d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 dnl
 dnl Copyright (C) 2009, Francesco P. Lovergine <frankie at debian.org>
 
-AC_INIT([GeographicLib],[1.44],[charles at karney.com])
+AC_INIT([GeographicLib],[1.45],[charles at karney.com])
 AC_CANONICAL_SYSTEM
 AC_PREREQ(2.61)
 AC_CONFIG_SRCDIR(src/Geodesic.cpp)
@@ -9,7 +9,7 @@ AC_CONFIG_MACRO_DIR(m4)
 AM_INIT_AUTOMAKE
 
 GEOGRAPHICLIB_VERSION_MAJOR=1
-GEOGRAPHICLIB_VERSION_MINOR=44
+GEOGRAPHICLIB_VERSION_MINOR=45
 GEOGRAPHICLIB_VERSION_PATCH=0
 AC_DEFINE_UNQUOTED([GEOGRAPHICLIB_VERSION_MAJOR],
         [$GEOGRAPHICLIB_VERSION_MAJOR],[major version number])
@@ -35,7 +35,7 @@ dnl Interfaces changed/added/removed:   CURRENT++ REVISION=0
 dnl Interfaces added:                   AGE++
 dnl Interfaces removed:                 AGE=0
 LT_CURRENT=16
-LT_REVISION=0
+LT_REVISION=1
 LT_AGE=2
 AC_SUBST(LT_CURRENT)
 AC_SUBST(LT_REVISION)
@@ -62,7 +62,8 @@ AX_CHECK_COMPILE_FLAG([-std=c++11],
                 [CXXFLAGS="$CXXFLAGS -std=c++0x"])])
 # Check for C++11 math functions
 AC_TRY_COMPILE([#include <cmath>],
-        [return int(std::hypot(3.0, 4.0) + std::expm1(0.5) +
+        [int q;
+         return int(std::hypot(3.0, 4.0) + std::expm1(0.5) +
                     std::log1p(2.0) + std::asinh(10.0) +
                     std::atanh(0.8) + std::cbrt(8.0) +
                     std::fma(1.0, 2.0, 3.0) + std::remquo(100.0, 90.0, &q) +
@@ -91,6 +92,7 @@ src/Makefile
 include/Makefile
 tools/Makefile
 doc/Makefile
+js/Makefile
 man/Makefile
 matlab/Makefile
 python/Makefile
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 7eca7f0..760e1af 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -42,6 +42,8 @@ if (DOXYGEN_FOUND)
     ../dotnet/NETGeographicLib/*.cpp ../dotnet/NETGeographicLib/*.h
     ../dotnet/examples/CS/*.cs ../dotnet/examples/ManagedCPP/*.cpp
     ../dotnet/examples/VB/*.vb)
+  file (GLOB JSSOURCES ../js/src/*.js)
+  file (GLOB JSTUTORIALS ../js/GeographicLib.md  ../js/doc/*.md)
   file (GLOB EXTRA_FILES ../maxima/[A-Za-z]*.mac
     tmseries30.html geodseries30.html ../LICENSE.txt)
   file (GLOB FIGURES *.png)
@@ -49,6 +51,11 @@ if (DOXYGEN_FOUND)
   add_custom_target (doc ALL
     DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/html/index.html)
   add_dependencies (doc htmlman)
+  if (JSDOC)
+    set (JSDOC_CMD "${JSDOC}")
+  else ()
+    set (JSDOC_CMD "echo")
+  endif ()
   add_custom_command (OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/html/index.html
     DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doxyfile
       ${CMAKE_CURRENT_BINARY_DIR}/doxyfile-c
@@ -56,13 +63,19 @@ if (DOXYGEN_FOUND)
       ${CMAKE_CURRENT_BINARY_DIR}/doxyfile-net
       ${CMAKE_CURRENT_BINARY_DIR}/GeographicLib.dox
       geodesic-c.dox geodesic-for.dox NETGeographicLib.dox
-      ${SOURCES} ${EXTRA_FILES} ${FIGURES} ${HTMLMAN}
+      ${SOURCES} ${JSSOURCES} ${EXTRA_FILES} ${JSTUTORIALS}
+      ${FIGURES} ${HTMLMAN}
     COMMAND ${CMAKE_COMMAND} -E remove_directory html
     COMMAND ${CMAKE_COMMAND} -E copy_directory html-stage html
     COMMAND ${DOXYGEN_EXECUTABLE} doxyfile > doxygen.log
     COMMAND ${DOXYGEN_EXECUTABLE} doxyfile-c > doxygen-c.log
     COMMAND ${DOXYGEN_EXECUTABLE} doxyfile-for > doxygen-for.log
     COMMAND ${DOXYGEN_EXECUTABLE} doxyfile-net > doxygen-net.log
+    COMMAND ${JSDOC_CMD} --verbose -d html/js
+      -u ${PROJECT_SOURCE_DIR}/js/doc
+      -c ${PROJECT_SOURCE_DIR}/js/conf.json
+      -R ${PROJECT_SOURCE_DIR}/js/GeographicLib.md
+      ${PROJECT_SOURCE_DIR}/js/src > jsdoc.log
     COMMENT "Generating html documentation tree")
   install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
     DESTINATION ${INSTALL_DOC_DIR})
@@ -77,10 +90,3 @@ else ()
     ${CMAKE_CURRENT_BINARY_DIR}/html/utilities.html
     DESTINATION ${INSTALL_DOC_DIR}/html)
 endif ()
-
-# Finally install the JavaScript files
-file (GLOB SCRIPTDRIVERS scripts/[A-Za-z]*.html)
-file (GLOB JSSCRIPTS scripts/GeographicLib/[A-Za-z]*.js)
-install (FILES ${SCRIPTDRIVERS} DESTINATION ${INSTALL_DOC_DIR}/scripts)
-install (FILES ${JSSCRIPTS}
-  DESTINATION ${INSTALL_DOC_DIR}/scripts/GeographicLib)
diff --git a/doc/GeographicLib.dox.in b/doc/GeographicLib.dox.in
index 2f98425..62d6d2c 100644
--- a/doc/GeographicLib.dox.in
+++ b/doc/GeographicLib.dox.in
@@ -12,7 +12,7 @@ namespace GeographicLib {
 \mainpage GeographicLib library
 \author Charles F. F. Karney (charles at karney.com)
 \version @PROJECT_VERSION@
-\date 2015-08-14
+\date 2015-09-30
 
 \section abstract Abstract
 
@@ -295,13 +295,14 @@ Here are the steps to compile and install GeographicLib:
     is to a common directory, e.g., /usr/local.  If it is OFF (the
     Windows default), the installation directory contains the package
     name, e.g., C:/pkg/GeographicLib- at PROJECT_VERSION@.  The installation
-    directories for the documentation, cmake configuration, python and
-    matlab interfaces all depend on the variable with deeper paths
+    directories for the documentation, cmake configuration, Python and
+    MATLAB interfaces all depend on the variable with deeper paths
     relative to CMAKE_INSTALL_PREFIX being used when it's ON:
-    - documentation: OFF: doc/html; ON: share/doc/GeographicLib/html;
     - cmake configuration: OFF cmake; ON: lib/cmake/GeographicLib;
-    - python interface: OFF: python; ON: lib/python/site-packages;
-    - matlab interface: OFF: matlab; ON: share/matlab;
+    - documentation: OFF: doc/html; ON: share/doc/GeographicLib/html;
+    - JavaScript interface: OFF: node_modules; ON: lib/node_modules;
+    - Python interface: OFF: python; ON: lib/python/site-packages;
+    - MATLAB interface: OFF: matlab; ON: share/matlab.
     .
   - <code>CMAKE_INSTALL_PREFIX</code> (default: <code>/usr/local</code>
     on non-Windows systems, <code>C:/Program Files/GeographicLib</code>
@@ -1192,7 +1193,7 @@ dependency \verbatim
   <dependency>
     <groupId>net.sf.geographiclib</groupId>
     <artifactId>GeographicLib-Java</artifactId>
-    <version>1.43</version>
+    <version>1.45</version>
   </dependency>
   \endverbatim
 in your <code>pom.xml</code>.
@@ -1202,7 +1203,7 @@ For documentation, see
 
 \section javascript JavaScript implementation
 
-The directory doc/scripts/GeographicLib contains the classes
+The directory js/src contains the classes
 - Math
 - Accumulator
 - Geodesic
@@ -1210,8 +1211,31 @@ The directory doc/scripts/GeographicLib contains the classes
 - PolygonAreaT
 - DMS
 .
-translated into JavaScript.  See Interface.js for a simple JavaScript
-interface to these routines (documented near the top of the file).
+translated into JavaScript.  When GeographicLib is built the JavaScript
+source files are concatenated and compressed into a single file
+<code>geographiclib.min.js</code>.  This can be included into your
+document with \code
+<script type="text/javascript"
+        src="http://geographiclib.sf.net/scripts/geographiclib.min.js">
+</script> \endcode
+This file gives you the latest version of the library.  If you need to
+use a specific version of this file, include a version number as in
+\code
+<script type="text/javascript"
+        src="http://geographiclib.sf.net/scripts/geographiclib-1.45.min.js">
+</script> \endcode
+You can visit http://geographiclib.sf.net/scripts/ to see which versions
+are available.
+
+Starting with version 1.45, this package is available as an
+<a href="https://nodejs.org">node</a> package that can be installed with
+<a href="https://www.npmjs.com">npm</a> (thanks to Yurij Mikhalevich for
+help with this work).  To install this as a node package, use \verbatim
+  npm install geographiclib \endverbatim
+
+For documentation, see
+ - <a href="js/index.html">JavaScript library for geodesics</a>.
+
 Examples of using this interface are
 - a <a href="../scripts/geod-calc.html">geodesic calculator</a> showing
   the solution of direct and inverse geodesic problem, finding
@@ -1220,23 +1244,10 @@ Examples of using this interface are
 - <a href="../scripts/geod-google.html">displaying geodesics in Google
   Maps</a> which shows the geodesic, the geodesic circle, and various
   geodesic envelopes.
-.
-These examples include a "stripped" version of the JavaScript code, \code
-<script type="text/javascript"
-        src="http://geographiclib.sf.net/scripts/geographiclib.js">
-</script> \endcode
-which loads faster.  This file gives you the latest version of the
-library.  If you need to use a specific version of this file, include a
-version number as in \code
-<script type="text/javascript"
-        src="http://geographiclib.sf.net/scripts/geographiclib-1.43.js">
-</script> \endcode
-You can visit http://geographiclib.sf.net/scripts/ to see which versions
-are available.
 
 \section python Python implementation
 
-A python implementation of the geodesic routines from GeographicLib
+A Python implementation of the geodesic routines from GeographicLib
 are provided in the python/geographiclib directory (which is installed
 as PREFIX/lib/python/site-packages/geographiclib, if
 COMMON_INSTALL_PATH is ON, and as PREFIX/python/geographiclib,
@@ -1247,7 +1258,7 @@ otherwise).  This contains implementations of the classes
 - GeodesicLine
 - PolygonAreaT
 
-You can install the python interface independent of the rest of
+You can install the Python interface independent of the rest of
 GeographicLib using pip \verbatim
   pip install geographiclib \endverbatim
 This requires root privileges and you might first need to install the
@@ -1263,7 +1274,7 @@ so that they are in python's default path with, for example \verbatim
   python setup.py install
 \endverbatim
 (this will require root privileges).  Or else you can set the path
-within python using \code{.py}
+within Python using \code{.py}
 >>> import sys
 >>> sys.path.append("/usr/local/lib/python/site-packages")
 \endcode
@@ -4066,7 +4077,7 @@ Here is the solution, exactly as given by Jacobi
 \f]
 As Jacobi notes "a function of the angle \f$\beta\f$ equals a
 function of the angle \f$\omega\f$.  These two functions are just
-Abelian integrals..." Two constants \f$\delta\f$ and \f$\gamma\f$
+Abelian integrals…" Two constants \f$\delta\f$ and \f$\gamma\f$
 appear in the solution.  Typically \f$\delta\f$ is zero if the lower
 limits of the integrals are taken to be the starting point of the geodesic
 and the direction of the geodesics is determined by \f$\gamma\f$.
@@ -5560,7 +5571,7 @@ G4[9] = + 2431/104595456 * eps^9;
 Some papers advocating the use of great ellipses for navigation exhibit
 a prejudice against the use of geodesics.  These excerpts from
 Pallikaris, Tsoulos, & Paradissis (2009) give the flavor
- - ... it is required to adopt realistic accuracy standards in order not
+ - … it is required to adopt realistic accuracy standards in order not
    only to eliminate the significant errors of the spherical model but
    also to avoid the exaggerated and unrealistic requirements of sub
    meter accuracy.
@@ -7003,6 +7014,62 @@ of the
 <a href="https://sourceforge.net/p/geographiclib/code/ci/release/tree/">
 git repository for GeographicLib</a>.
 
+ - <a href="http://geographiclib.sf.net/1.45">Version 1.45</a>
+   (released 2015-09-30)
+   - Fix BUG in solution of inverse geodesic caused by misbehavior of
+     some versions of Visual Studio on Windows (fmod(−0.0, 360.0)
+     returns +0.0 instead of −0.0) and Octave (sind(−0.0)
+     returns +0.0 instead of −0.0).  These bugs were exposed
+     because max(−0.0, +0.0) returns −0.0 for some
+     languages.
+   - Geodesic::Inverse now correctly returns NaNs if one of the
+     latitudes is a NaN.
+   - Changes to JavaScript package:
+     - thanks to help from Yurij Mikhalevich, it is a now a
+       <a href="https://nodejs.org">node</a> package that can be
+       installed with <a href="https://www.npmjs.com">npm</a>;
+     - make install now installs the node package in
+       <code>lib/node_modules/geographiclib</code>;
+     - add unit tests using mocha;
+     - add documentation via JSDoc;
+     - fix bug Geodesic.GenInverse (this bug, introduced in version
+       1.44, resulted in the wrong azimuth being reported for points at
+       the pole).
+   - Changes to Java package:
+     - add implementation of ellipsoidal Gnomonic projection (courtesy
+       of Sebastian Mattheis);
+     - add unit tests using JUnit;
+     - Math.toRadians and Math.toDegrees are used instead of
+       GeoMath.degree (which is now removed), as a result…
+     - Java version 1.2 (released 1998-12) or later is now required.
+   - Changes to Python package:
+     - add unit tests using the unittest framework;
+     - fixed bug in normalization of the area.
+   - Changes to MATLAB package:
+     - fix array size mismatch in geoddistance by avoiding calls to
+       subfunctions with zero-length arrays;
+     - fix tranmerc_{fwd,inv} so that they work with arrays and
+       mixed array/scalar arguments;
+     - work around Octave problem which causes mgrs_fwd to return
+       garbage with prec = 10 or 11;
+     - add geographiclib_test.m to run a test suite.
+   - Behavior of substituting 1/\e f for \e f if \e f > 1 is now
+     <b>deprecated</b>.  This behavior has been removed from the
+     JavaScript, C, and Python implementations (it was never
+     documented).  Maxima, MATLAB, and Fortran implementations never
+     included this behavior.
+   - Other changes:
+     - fix bug, introduced in version 1.42, in the C++ implementation to
+       the computation of area which causes NaNs to be returned in the
+       case of a sphere;
+     - fixed bug, introduced in version 1.44, in the detection of C++11
+       math functions in configure.ac;
+     - throw error on non-convergence in Gnomonic::Reverse if
+       GEOGRAPHICLIB_PRECISION > 3;
+     - add geod_polygon_clear to C library;
+     - turn illegal latitudes into NaNs for Fortran library;
+     - add test suites for the C and Fortran libraries.
+
  - <a href="http://geographiclib.sf.net/1.44">Version 1.44</a>
    (released 2015-08-14)
    - Various changes to improve accuracy, e.g., by minimizing round-off
@@ -7469,7 +7536,7 @@ git repository for GeographicLib</a>.
        select the library to use;
      - find_package version checking allows nmake and Visual Studio
        generators to interoperate on Windows;
-     - find_package (GeographicLib ...) requires that GeographicLib be
+     - find_package (GeographicLib …) requires that GeographicLib be
        capitalized correctly;
      - on Unix/Linux, don't include the version number in directory for
        the cmake configuration files;
diff --git a/doc/Makefile.am b/doc/Makefile.am
index fc3e397..8bdb88f 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,6 +1,7 @@
 EXTRAFILES = $(srcdir)/tmseries30.html $(srcdir)/geodseries30.html
 
-FIGURES = $(srcdir)/gauss-krueger-graticule.png \
+FIGURES = \
+	$(srcdir)/gauss-krueger-graticule.png \
 	$(srcdir)/thompson-tm-graticule.png \
 	$(srcdir)/gauss-krueger-convergence-scale.png \
 	$(srcdir)/gauss-schreiber-graticule-a.png \
@@ -9,19 +10,6 @@ FIGURES = $(srcdir)/gauss-krueger-graticule.png \
 	$(srcdir)/gauss-krueger-error.png \
 	$(srcdir)/meridian-measures.png
 
-SCRIPTDRIVERS = \
-	$(srcdir)/scripts/geod-calc.html \
-	$(srcdir)/scripts/geod-google.html \
-	$(srcdir)/scripts/geod-google-instructions.html
-
-JSSCRIPTS = \
-	$(srcdir)/scripts/GeographicLib/Math.js \
-	$(srcdir)/scripts/GeographicLib/Geodesic.js \
-	$(srcdir)/scripts/GeographicLib/GeodesicLine.js \
-	$(srcdir)/scripts/GeographicLib/PolygonArea.js \
-	$(srcdir)/scripts/GeographicLib/DMS.js \
-	$(srcdir)/scripts/GeographicLib/Interface.js
-
 HPPFILES = \
 	$(top_srcdir)/include/GeographicLib/Accumulator.hpp \
 	$(top_srcdir)/include/GeographicLib/AlbersEqualArea.hpp \
@@ -101,21 +89,6 @@ MANPAGES = \
 	../man/RhumbSolve.1.html \
 	../man/TransverseMercatorProj.1.html
 
-LEGACYFILES = \
-	$(top_srcdir)/legacy/C/geodesic.c \
-	$(top_srcdir)/legacy/C/geodesic.h \
-	$(top_srcdir)/legacy/C/direct.c \
-	$(top_srcdir)/legacy/C/inverse.c \
-	$(top_srcdir)/legacy/C/planimeter.c \
-	$(top_srcdir)/legacy/Fortran/geodesic.for \
-	$(top_srcdir)/legacy/Fortran/geodesic.inc \
-	$(top_srcdir)/legacy/Fortran/geoddirect.for \
-	$(top_srcdir)/legacy/Fortran/geodinverse.for \
-	$(top_srcdir)/legacy/Fortran/ngscommon.for \
-	$(top_srcdir)/legacy/Fortran/ngsforward.for \
-	$(top_srcdir)/legacy/Fortran/ngsinverse.for \
-	$(top_srcdir)/legacy/Fortran/planimeter.for
-
 doc: html/index.html
 
 if HAVE_DOXYGEN
@@ -125,9 +98,7 @@ manpages: $(MANPAGES)
 	touch $@
 
 html/index.html: manpages doxyfile.in GeographicLib.dox.in \
-	$(HPPFILES) $(ALLSOURCES) $(EXTRAFILES) $(FIGURES) \
-	doxyfile-c.in geodesic-c.dox doxyfile-for.in geodesic-for.dox \
-	$(LEGACYFILES)
+	$(HPPFILES) $(ALLSOURCES) $(EXTRAFILES) $(FIGURES)
 	cp -p $(EXTRAFILES) $(top_srcdir)/maxima/*.mac \
 	$(top_srcdir)/LICENSE.txt html/
 	sed -e "s%@PROJECT_VERSION@%$(VERSION)%g" \
@@ -136,14 +107,6 @@ html/index.html: manpages doxyfile.in GeographicLib.dox.in \
 	-e "s%@PROJECT_BINARY_DIR@%..%g" \
 	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
 	$(srcdir)/doxyfile.in | $(DOXYGEN) -
-	sed -e "s%@PROJECT_SOURCE_DIR@%$(top_srcdir)%g" \
-	-e "s%@PROJECT_BINARY_DIR@%..%g" \
-	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
-	$(srcdir)/doxyfile-c.in | $(DOXYGEN) -
-	sed -e "s%@PROJECT_SOURCE_DIR@%$(top_srcdir)%g" \
-	-e "s%@PROJECT_BINARY_DIR@%..%g" \
-	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
-	$(srcdir)/doxyfile-for.in | $(DOXYGEN) -
 else
 html/index.html: index.html.in utilities.html.in
 	if test -d html; then rm -rf html/*; else mkdir html; fi
@@ -157,29 +120,11 @@ endif
 maintainer-clean-local:
 	rm -rf html manpages
 
+htmldir=$(DESTDIR)$(docdir)/html
+
 install-doc: html/index.html
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html
-	$(INSTALL) -m 644 `dirname $<`/*.* $(DESTDIR)$(docdir)/html
+	$(INSTALL) -d $(htmldir)
+	$(INSTALL) -m 644 `dirname $<`/*.* $(htmldir)
 	-test -f `dirname $<`/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/search && \
-	$(INSTALL) -m 644 `dirname $<`/search/*.* \
-	$(DESTDIR)$(docdir)/html/search
-	-test -f `dirname $<`/C/index.html && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/C && \
-	$(INSTALL) -m 644 `dirname $<`/C/*.* $(DESTDIR)$(docdir)/html/C
-	-test -f `dirname $<`/C/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/C/search && \
-	$(INSTALL) -m 644 `dirname $<`/C/search/*.* \
-	$(DESTDIR)$(docdir)/html/C/search
-	-test -f `dirname $<`/Fortran/index.html && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/Fortran && \
-	$(INSTALL) -m 644 `dirname $<`/Fortran/*.* \
-	$(DESTDIR)$(docdir)/html/Fortran
-	-test -f `dirname $<`/Fortran/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/Fortran/search && \
-	$(INSTALL) -m 644 `dirname $<`/Fortran/search/*.* \
-	$(DESTDIR)$(docdir)/html/Fortran/search
-	$(INSTALL) -d $(DESTDIR)$(docdir)/scripts
-	$(INSTALL) -m 644 $(SCRIPTDRIVERS) $(DESTDIR)$(docdir)/scripts
-	$(INSTALL) -d $(DESTDIR)$(docdir)/scripts/GeographicLib
-	$(INSTALL) -m 644 $(JSSCRIPTS) $(DESTDIR)$(docdir)/scripts/GeographicLib
+	$(INSTALL) -d $(htmldir)/search && \
+	$(INSTALL) -m 644 `dirname $<`/search/*.* $(htmldir)/search || true
diff --git a/doc/Makefile.in b/doc/Makefile.in
index a8af3a2..b2a3048 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -219,7 +219,7 @@ host_alias = @host_alias@
 host_cpu = @host_cpu@
 host_os = @host_os@
 host_vendor = @host_vendor@
-htmldir = @htmldir@
+htmldir = $(DESTDIR)$(docdir)/html
 includedir = @includedir@
 infodir = @infodir@
 install_sh = @install_sh@
@@ -247,7 +247,8 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 EXTRAFILES = $(srcdir)/tmseries30.html $(srcdir)/geodseries30.html
-FIGURES = $(srcdir)/gauss-krueger-graticule.png \
+FIGURES = \
+	$(srcdir)/gauss-krueger-graticule.png \
 	$(srcdir)/thompson-tm-graticule.png \
 	$(srcdir)/gauss-krueger-convergence-scale.png \
 	$(srcdir)/gauss-schreiber-graticule-a.png \
@@ -256,19 +257,6 @@ FIGURES = $(srcdir)/gauss-krueger-graticule.png \
 	$(srcdir)/gauss-krueger-error.png \
 	$(srcdir)/meridian-measures.png
 
-SCRIPTDRIVERS = \
-	$(srcdir)/scripts/geod-calc.html \
-	$(srcdir)/scripts/geod-google.html \
-	$(srcdir)/scripts/geod-google-instructions.html
-
-JSSCRIPTS = \
-	$(srcdir)/scripts/GeographicLib/Math.js \
-	$(srcdir)/scripts/GeographicLib/Geodesic.js \
-	$(srcdir)/scripts/GeographicLib/GeodesicLine.js \
-	$(srcdir)/scripts/GeographicLib/PolygonArea.js \
-	$(srcdir)/scripts/GeographicLib/DMS.js \
-	$(srcdir)/scripts/GeographicLib/Interface.js
-
 HPPFILES = \
 	$(top_srcdir)/include/GeographicLib/Accumulator.hpp \
 	$(top_srcdir)/include/GeographicLib/AlbersEqualArea.hpp \
@@ -348,21 +336,6 @@ MANPAGES = \
 	../man/RhumbSolve.1.html \
 	../man/TransverseMercatorProj.1.html
 
-LEGACYFILES = \
-	$(top_srcdir)/legacy/C/geodesic.c \
-	$(top_srcdir)/legacy/C/geodesic.h \
-	$(top_srcdir)/legacy/C/direct.c \
-	$(top_srcdir)/legacy/C/inverse.c \
-	$(top_srcdir)/legacy/C/planimeter.c \
-	$(top_srcdir)/legacy/Fortran/geodesic.for \
-	$(top_srcdir)/legacy/Fortran/geodesic.inc \
-	$(top_srcdir)/legacy/Fortran/geoddirect.for \
-	$(top_srcdir)/legacy/Fortran/geodinverse.for \
-	$(top_srcdir)/legacy/Fortran/ngscommon.for \
-	$(top_srcdir)/legacy/Fortran/ngsforward.for \
-	$(top_srcdir)/legacy/Fortran/ngsinverse.for \
-	$(top_srcdir)/legacy/Fortran/planimeter.for
-
 all: all-am
 
 .SUFFIXES:
@@ -564,9 +537,7 @@ doc: html/index.html
 @HAVE_DOXYGEN_TRUE@	touch $@
 
 @HAVE_DOXYGEN_TRUE at html/index.html: manpages doxyfile.in GeographicLib.dox.in \
- at HAVE_DOXYGEN_TRUE@	$(HPPFILES) $(ALLSOURCES) $(EXTRAFILES) $(FIGURES) \
- at HAVE_DOXYGEN_TRUE@	doxyfile-c.in geodesic-c.dox doxyfile-for.in geodesic-for.dox \
- at HAVE_DOXYGEN_TRUE@	$(LEGACYFILES)
+ at HAVE_DOXYGEN_TRUE@	$(HPPFILES) $(ALLSOURCES) $(EXTRAFILES) $(FIGURES)
 @HAVE_DOXYGEN_TRUE@	cp -p $(EXTRAFILES) $(top_srcdir)/maxima/*.mac \
 @HAVE_DOXYGEN_TRUE@	$(top_srcdir)/LICENSE.txt html/
 @HAVE_DOXYGEN_TRUE@	sed -e "s%@PROJECT_VERSION@%$(VERSION)%g" \
@@ -575,14 +546,6 @@ doc: html/index.html
 @HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_BINARY_DIR@%..%g" \
 @HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
 @HAVE_DOXYGEN_TRUE@	$(srcdir)/doxyfile.in | $(DOXYGEN) -
- at HAVE_DOXYGEN_TRUE@	sed -e "s%@PROJECT_SOURCE_DIR@%$(top_srcdir)%g" \
- at HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_BINARY_DIR@%..%g" \
- at HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
- at HAVE_DOXYGEN_TRUE@	$(srcdir)/doxyfile-c.in | $(DOXYGEN) -
- at HAVE_DOXYGEN_TRUE@	sed -e "s%@PROJECT_SOURCE_DIR@%$(top_srcdir)%g" \
- at HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_BINARY_DIR@%..%g" \
- at HAVE_DOXYGEN_TRUE@	-e "s%@PROJECT_VERSION@%$(VERSION)%g" \
- at HAVE_DOXYGEN_TRUE@	$(srcdir)/doxyfile-for.in | $(DOXYGEN) -
 @HAVE_DOXYGEN_FALSE at html/index.html: index.html.in utilities.html.in
 @HAVE_DOXYGEN_FALSE@	if test -d html; then rm -rf html/*; else mkdir html; fi
 @HAVE_DOXYGEN_FALSE@	cp $(top_srcdir)/LICENSE.txt html/
@@ -595,31 +558,11 @@ maintainer-clean-local:
 	rm -rf html manpages
 
 install-doc: html/index.html
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html
-	$(INSTALL) -m 644 `dirname $<`/*.* $(DESTDIR)$(docdir)/html
+	$(INSTALL) -d $(htmldir)
+	$(INSTALL) -m 644 `dirname $<`/*.* $(htmldir)
 	-test -f `dirname $<`/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/search && \
-	$(INSTALL) -m 644 `dirname $<`/search/*.* \
-	$(DESTDIR)$(docdir)/html/search
-	-test -f `dirname $<`/C/index.html && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/C && \
-	$(INSTALL) -m 644 `dirname $<`/C/*.* $(DESTDIR)$(docdir)/html/C
-	-test -f `dirname $<`/C/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/C/search && \
-	$(INSTALL) -m 644 `dirname $<`/C/search/*.* \
-	$(DESTDIR)$(docdir)/html/C/search
-	-test -f `dirname $<`/Fortran/index.html && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/Fortran && \
-	$(INSTALL) -m 644 `dirname $<`/Fortran/*.* \
-	$(DESTDIR)$(docdir)/html/Fortran
-	-test -f `dirname $<`/Fortran/search/search.js && \
-	$(INSTALL) -d $(DESTDIR)$(docdir)/html/Fortran/search && \
-	$(INSTALL) -m 644 `dirname $<`/Fortran/search/*.* \
-	$(DESTDIR)$(docdir)/html/Fortran/search
-	$(INSTALL) -d $(DESTDIR)$(docdir)/scripts
-	$(INSTALL) -m 644 $(SCRIPTDRIVERS) $(DESTDIR)$(docdir)/scripts
-	$(INSTALL) -d $(DESTDIR)$(docdir)/scripts/GeographicLib
-	$(INSTALL) -m 644 $(JSSCRIPTS) $(DESTDIR)$(docdir)/scripts/GeographicLib
+	$(INSTALL) -d $(htmldir)/search && \
+	$(INSTALL) -m 644 `dirname $<`/search/*.* $(htmldir)/search || true
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/doc/Makefile.mk b/doc/Makefile.mk
index ca852d9..e6373ce 100644
--- a/doc/Makefile.mk
+++ b/doc/Makefile.mk
@@ -1,6 +1,3 @@
-SCRIPTDRIVERS = $(wildcard scripts/[A-Za-z]*.html)
-JSSCRIPTS = $(wildcard scripts/GeographicLib/[A-Za-z]*.js)
-
 VERSION:=$(shell grep '\bVERSION=' ../configure | cut -f2 -d\' | head -1)
 
 doc: html/index.html
@@ -16,15 +13,10 @@ html/index.html: index.html.in utilities.html.in
 PREFIX = /usr/local
 DEST = $(PREFIX)/share/doc/GeographicLib
 DOCDEST = $(DEST)/html
-SCRIPTDEST = $(DEST)/scripts
 INSTALL = install -b
 
 install: html/index.html
 	test -d $(DOCDEST) || mkdir -p $(DOCDEST)
 	$(INSTALL) -m 644 html/* $(DOCDEST)/
-	test -d $(SCRIPTDEST)/GeographicLib || \
-	mkdir -p $(SCRIPTDEST)/GeographicLib
-	$(INSTALL) -m 644 $(SCRIPTDRIVERS) $(SCRIPTDEST)/
-	$(INSTALL) -m 644 $(JSSCRIPTS) $(SCRIPTDEST)/GeographicLib/
 
 .PHONY: doc install clean
diff --git a/doc/geodesic-c.dox b/doc/geodesic-c.dox
index 7f7bbee..b268364 100644
--- a/doc/geodesic-c.dox
+++ b/doc/geodesic-c.dox
@@ -11,7 +11,7 @@
 /**
 \mainpage Geodesic routines implemented in C
 \author Charles F. F. Karney (charles at karney.com)
-\version 1.44
+\version 1.45
 
 \section abstract-c Abstract
 
@@ -27,13 +27,13 @@ about any C compiler.
 \section download-c Downloading the source
 
 The C library is part of %GeographicLib which available for download at
-- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.tar.gz">
-  GeographicLib-1.44.tar.gz</a>
-- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.zip">
-  GeographicLib-1.44.zip</a>
+- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.tar.gz">
+  GeographicLib-1.45.tar.gz</a>
+- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.zip">
+  GeographicLib-1.45.zip</a>
 .
 as either a compressed tar file (tar.gz) or a zip file.  After unpacking
-the source, the C library can be found in GeographicLib-1.44/legacy/C.
+the source, the C library can be found in GeographicLib-1.45/legacy/C.
 The library consists of two files geodesic.c and geodesic.h.
 
 The library is also included as part of
@@ -68,6 +68,7 @@ mkdir BUILD
 cd BUILD
 cmake ..
 make
+make test
 echo 30 0 29.5 179.5 | ./inverse \endverbatim
 
 Alternatively, if you have proj.4 installed, you can compile and link
@@ -125,6 +126,7 @@ echo 30 0 29.5 179.5 | ./inverse \endverbatim
 - <a href="http://geographiclib.sf.net">The GeographicLib web site</a>.
 - <a href="../index.html">The C++ library</a>.
 - <a href="../java/index.html">The Java library</a>.
+- <a href="../js/index.html">The JavaScript library</a>.
 - <a href="../Fortran/index.html">The Fortran library</a>.
 - Documentation on the C++ classes: GeographicLib::Geodesic,
   GeographicLib::GeodesicLine, GeographicLib::PolygonAreaT.
@@ -138,9 +140,16 @@ echo 30 0 29.5 179.5 | ./inverse \endverbatim
 
 \section changes-c Change log
 
- - <a href="http://geographiclib.sf.net/1.44">Version 1.44</a>
-   (released 2015-08-14)
+ - <a href="http://geographiclib.sf.net/1.45/C">Version 1.45</a>
+   (released 2015-09-30)
+   - The solution of the inverse problem now correctly returns NaNs if
+     one of the latitudes is a NaN.
+   - Include a test suite that can be run with "make test" after
+     configuring with cmake.
+   - Add geod_polygon_clear().
 
+ - <a href="http://geographiclib.sf.net/1.44/C">Version 1.44</a>
+   (released 2015-08-14)
    - Improve accuracy of calculations by evaluating trigonometric
      functions more carefully and replacing the series for the reduced
      length with one with a smaller truncation error.
diff --git a/doc/geodesic-for.dox b/doc/geodesic-for.dox
index bd002eb..25437f2 100644
--- a/doc/geodesic-for.dox
+++ b/doc/geodesic-for.dox
@@ -11,7 +11,7 @@
 /**
 \mainpage Geodesic routines implemented in Fortran
 \author Charles F. F. Karney (charles at karney.com)
-\version 1.44
+\version 1.45
 
 \section abstract-for Abstract
 
@@ -25,14 +25,14 @@ compile correctly with just about any Fortran compiler.
 \section download-for Downloading the source
 
 The Fortran library is part of %GeographicLib which available for download at
-- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.tar.gz">
-  GeographicLib-1.44.tar.gz</a>
-- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.zip">
-  GeographicLib-1.44.zip</a>
+- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.tar.gz">
+  GeographicLib-1.45.tar.gz</a>
+- <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.zip">
+  GeographicLib-1.45.zip</a>
 .
 as either a compressed tar file (tar.gz) or a zip file.  After unpacking
 the source, the Fortran library can be found in
-GeographicLib-1.44/legacy/Fortran.  The library consists of the file
+GeographicLib-1.45/legacy/Fortran.  The library consists of the file
 geodesic.for.
 
 \section doc-for Library documentation
@@ -61,6 +61,7 @@ mkdir BUILD
 cd BUILD
 cmake ..
 make
+make test
 echo 30 0 29.5 179.5 | ./geodinverse \endverbatim
 
 Finally, the two programs
@@ -112,6 +113,7 @@ and are therefore in the public domain.
 - <a href="../index.html">The C++ library</a>.
 - <a href="../C/index.html">The C library</a>.
 - <a href="../java/index.html">The Java library</a>.
+- <a href="../js/index.html">The JavaScript library</a>.
 - Documentation on the C++ classes: GeographicLib::Geodesic,
   GeographicLib::GeodesicLine, GeographicLib::PolygonAreaT.
 - The section in the %GeographicLib documentation on geodesics: \ref
@@ -124,9 +126,18 @@ and are therefore in the public domain.
 
 \section changes-for Change log
 
- - <a href="http://geographiclib.sf.net/1.44">Version 1.44</a>
+ - <a href="http://geographiclib.sf.net/1.45/Fortran">Version 1.45</a>
+   (released 2015-09-30)
+   - The solution of the inverse problem now correctly returns NaNs if
+     one of the latitudes is a NaN.
+   - Include a test suite that can be run with "make test" after
+     configuring with cmake.
+   - The library now treats latitudes outside the range [−90°,
+     90°] as NaNs; so the sample programs no longer check for legal
+     values of latitude.
+
+ - <a href="http://geographiclib.sf.net/1.44/Fortran">Version 1.44</a>
    (released 2015-08-14)
-
    - Improve accuracy of calculations by evaluating trigonometric
      functions more carefully and replacing the series for the reduced
      length with one with a smaller truncation error.
diff --git a/doc/scripts/GeographicLib/Interface.js b/doc/scripts/GeographicLib/Interface.js
deleted file mode 100644
index e671bd8..0000000
--- a/doc/scripts/GeographicLib/Interface.js
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * Interface.js
- * JavaScript interface routines for the geodesic routines GeographicLib.
- *
- * This provides JavaScript-style interfaces to Math.js, Geodesic.js,
- * GeodesicLine.js, and PolygonArea.js which, in turn, are rather
- * literal translations of the following classes from GeographicLib:
- * Math, Accumulator, Geodesic, GeodesicLine, PolygonArea.  See the
- * documentation for the C++ class for more information at
- *
- *    http://geographiclib.sourceforge.net/html/annotated.html
- *
- * The algorithms are derived in
- *
- *    Charles F. F. Karney,
- *    Algorithms for geodesics, J. Geodesy 87, 43-55 (2013);
- *    https://dx.doi.org/10.1007/s00190-012-0578-z
- *    Addenda: http://geographiclib.sf.net/geod-addenda.html
- *
- * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
- * under the MIT/X11 License.  For more information, see
- * http://geographiclib.sourceforge.net/
- **********************************************************************
- * GeographicLib.Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2, outmask);
- * GeographicLib.Geodesic.WGS84.Direct(lat1, lon1, azi1, s12, outmask);
- *
- * perform the basic geodesic calculations.  These return an object with
- * (some) of the following fields:
- *
- *   lat1 latitude of point 1
- *   lon1 longitude of point 1
- *   azi1 azimuth of line at point 1
- *   lat2 latitude of point 2
- *   lon2 longitude of point 2
- *   azi2 azimuth of line at point 2
- *   s12 distance from 1 to 2
- *   a12 arc length on auxiliary sphere from 1 to 2
- *   m12 reduced length of geodesic
- *   M12 geodesic scale 2 relative to 1
- *   M21 geodesic scale 1 relative to 2
- *   S12 area between geodesic and equator
- *
- * outmask determines which fields get included and if outmask is
- * omitted, then only the basic geodesic fields are computed.  The mask
- * is an or'ed combination of the following values
- *
- *   GeographicLib.Geodesic.LATITUDE
- *   GeographicLib.Geodesic.LONGITUDE
- *   GeographicLib.Geodesic.AZIMUTH
- *   GeographicLib.Geodesic.DISTANCE
- *   GeographicLib.Geodesic.REDUCEDLENGTH
- *   GeographicLib.Geodesic.GEODESICSCALE
- *   GeographicLib.Geodesic.AREA
- *   GeographicLib.Geodesic.ALL (all of the above)
- *   GeographicLib.Geodesic.LONG_UNROLL
- *
- **********************************************************************
- * GeographicLib.Geodesic.WGS84.InversePath(lat1, lon1, lat2, lon2, ds12, maxk);
- * GeographicLib.Geodesic.WGS84.DirectPath(lat1, lon1, azi1, s12, ds12, maxk);
- *
- * splits a geodesic line into k equal pieces which are no longer than
- * about ds12 (but k cannot exceed maxk, default 20), and returns an
- * array of length k + 1 of objects with fields
- *
- *   lat, lon, azi
- *
- **********************************************************************
- * GeographicLib.Geodesic.Envelope(lat1, lon1, azi1, s12, k)
- *
- * return the geodesic circle of radius s12 centered on the point lat1,
- * lon1.  k equally spaced azimuths starting at azi1 are computed.  This
- * returns an array of k + 1 points with fields lat and lon (with the
- * first and last points matching).
- *
- **********************************************************************
- * GeographicLib.Geodesic.Envelope(lat1, lon1, k, ord);
- *
- * returns the ord'th order envelope for geodesics emanating from lat1,
- * lon1.  This is located approximately circumference * ord/2 from the
- * point.  Thus odd numbered envelopes are centered at the antipodal
- * point and even number envelopes are centered on the point itself.
- * This returns an array of k + 1 points with fields lat and lon (with
- * the first and last points matching).
- *
- **********************************************************************
- * GeographicLib.Geodesic.WGS84.Area(points, polyline);
- *
- * computes the area of a polygon with vertices given by an array
- * points, each of whose elements contains lat and lon fields.  The
- * function returns an object with fields.
- *
- *   number, perimeter, area
- *
- * There is no need to "close" the polygon.  If polyline (default =
- * false) is true, then the points denote a polyline and its length is
- * returned as the perimeter (and the area is not calculated).
- **********************************************************************/
-
-(function() {
-  var m = GeographicLib.Math;
-  var g = GeographicLib.Geodesic;
-  var l = GeographicLib.GeodesicLine;
-
-  g.Geodesic.CheckPosition = function(lat, lon) {
-    if (!(Math.abs(lat) <= 90))
-      throw new Error("latitude " + lat + " not in [-90, 90]");
-  };
-
-  g.Geodesic.CheckAzimuth = function(azi) {
-    if (!(isFinite(azi)))
-      throw new Error("azimuth " + azi + " not a finite number");
-    return m.AngNormalize(azi);
-  };
-
-  g.Geodesic.CheckDistance = function(s) {
-    if (!(isFinite(s)))
-      throw new Error("distance " + s + " not a finite number");
-  };
-
-  g.Geodesic.prototype.Inverse = function(lat1, lon1, lat2, lon2, outmask) {
-    if (!outmask) outmask = g.DISTANCE | g.AZIMUTH;
-    g.Geodesic.CheckPosition(lat1, lon1);
-    g.Geodesic.CheckPosition(lat2, lon2);
-    return this.GenInverse(lat1, lon1, lat2, lon2, outmask);
-  };
-
-  g.Geodesic.prototype.Direct = function(lat1, lon1, azi1, s12, outmask) {
-    if (!outmask) outmask = g.LATITUDE | g.LONGITUDE | g.AZIMUTH;
-    g.Geodesic.CheckPosition(lat1, lon1);
-    g.Geodesic.CheckAzimuth(azi1);
-    g.Geodesic.CheckDistance(s12);
-    return this.GenDirect(lat1, lon1, azi1, false, s12, outmask);
-  };
-
-  g.Geodesic.prototype.InversePath =
-    function(lat1, lon1, lat2, lon2, ds12, maxk) {
-    var t = this.Inverse(lat1, lon1, lat2, lon2);
-    if (!maxk) maxk = 20;
-    if (!(ds12 > 0))
-      throw new Error("ds12 must be a positive number");
-    var
-    k = Math.max(1, Math.min(maxk, Math.ceil(t.s12/ds12))),
-    points = new Array(k + 1);
-    points[0] = {lat: t.lat1, lon: t.lon1, azi: t.azi1};
-    points[k] = {lat: t.lat2, lon: t.lon2, azi: t.azi2};
-    if (k > 1) {
-      var line = new l.GeodesicLine(this, t.lat1, t.lon1, t.azi1,
-                                    g.LATITUDE | g.LONGITUDE | g.AZIMUTH),
-      da12 = t.a12/k;
-      var vals;
-      for (var i = 1; i < k; ++i) {
-        vals =
-        line.GenPosition(true, i * da12, g.LATITUDE | g.LONGITUDE | g.AZIMUTH);
-        points[i] = {lat: vals.lat2, lon: vals.lon2, azi: vals.azi2};
-      }
-    }
-    return points;
-  };
-
-  g.Geodesic.prototype.DirectPath =
-    function(lat1, lon1, azi1, s12, ds12, maxk) {
-    var t = this.Direct(lat1, lon1, azi1, s12);
-    if (!maxk) maxk = 20;
-    if (!(ds12 > 0))
-      throw new Error("ds12 must be a positive number");
-    var
-    k = Math.max(1, Math.min(maxk, Math.ceil(Math.abs(t.s12)/ds12))),
-    points = new Array(k + 1);
-    points[0] = {lat: t.lat1, lon: t.lon1, azi: t.azi1};
-    points[k] = {lat: t.lat2, lon: t.lon2, azi: t.azi2};
-    if (k > 1) {
-      var line = new l.GeodesicLine(this, t.lat1, t.lon1, t.azi1,
-                                    g.LATITUDE | g.LONGITUDE | g.AZIMUTH),
-      da12 = t.a12/k;
-      var vals;
-      for (var i = 1; i < k; ++i) {
-        vals =
-        line.GenPosition(true, i * da12, g.LATITUDE | g.LONGITUDE | g.AZIMUTH);
-        points[i] = {lat: vals.lat2, lon: vals.lon2, azi: vals.azi2};
-      }
-    }
-    return points;
-  };
-
-  g.Geodesic.prototype.Circle = function(lat1, lon1, azi1, s12, k) {
-    if (!(Math.abs(lat1) <= 90))
-      throw new Error("lat1 must be in [-90, 90]");
-    if (!(isFinite(s12)))
-      throw new Error("s12 must be a finite number");
-    lon1 = m.AngNormalize(lon1);
-    azi1 = m.AngNormalize(azi1);
-    if (!k || k < 4) k = 24;
-    var points = new Array(k + 1);
-    var vals;
-    for (var i = 0; i <= k; ++i) {
-      var azi1a = azi1 + (k - i) * 360 / k; // Traverse circle counter-clocwise
-      if (azi1a >= 180) azi1a -= 360;
-      vals =
-        this.GenDirect(lat1, lon1, azi1a, false, s12, g.LATITUDE | g.LONGITUDE);
-      points[i] = {lat: vals.lat2, lon: vals.lon2};
-    }
-    return points;
-  };
-
-  g.Geodesic.prototype.Envelope = function(lat1, lon1, k, ord) {
-    if (!(Math.abs(lat1) <= 90))
-      throw new Error("lat1 must be in [-90, 90]");
-    lon1 = m.AngNormalize(lon1);
-    if (!k || k < 4) k = 24;
-    if (!ord) ord = 1;
-    var points = new Array(k + 1);
-    var vals, line, s12, j;
-    for (var i = 0; i <= k; ++i) {
-      var azi1 = -180 + i * 360 / k;
-      line = new l.GeodesicLine(this, lat1, lon1, azi1,
-                                g.LATITUDE | g.LONGITUDE | g.DISTANCE_IN |
-                                g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
-      vals = line.GenPosition(true, 180 * ord,
-                              g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
-      j = 0;
-      while (true) {
-        // Solve m12(s12) = 0 by Newton's method using dm12/ds12 = M21
-        s12 = vals.s12 - vals.m12/vals.M21;
-        if (Math.abs(vals.m12) < line._a * g.tol2_ * 0.1 || ++j > 10)
-          break;
-        vals = line.GenPosition(false, s12,
-                                g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
-      }
-      vals = line.GenPosition(false, s12, g.LATITUDE | g.LONGITUDE);
-      points[i] = {lat: vals.lat2, lon: vals.lon2};
-    }
-    return points;
-  };
-
-  g.Geodesic.prototype.Area = function(points, polyline) {
-    return GeographicLib.PolygonArea.Area(this, points, polyline);
-  };
-
-})();
diff --git a/doc/scripts/GeographicLib/Math.js b/doc/scripts/GeographicLib/Math.js
deleted file mode 100644
index 7db9ee4..0000000
--- a/doc/scripts/GeographicLib/Math.js
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * Math.js
- * Transcription of Math.hpp, Constants.hpp, and Accumulator.hpp into
- * JavaScript.
- *
- * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
- * under the MIT/X11 License.  For more information, see
- * http://geographiclib.sourceforge.net/
- **********************************************************************/
-
-var GeographicLib; if (!GeographicLib) GeographicLib = {};
-
-GeographicLib.Math = {};
-
-GeographicLib.Math.sq = function(x) { return x * x; };
-
-GeographicLib.Math.hypot = function(x, y) {
-  x = Math.abs(x);
-  y = Math.abs(y);
-  var a = Math.max(x, y), b = Math.min(x, y) / (a ? a : 1);
-  return a * Math.sqrt(1 + b * b);
-};
-
-GeographicLib.Math.cbrt = function(x) {
-  var y = Math.pow(Math.abs(x), 1/3);
-  return x < 0 ? -y : y;
-};
-
-GeographicLib.Math.log1p = function(x) {
-  var
-  y = 1 + x,
-  z = y - 1;
-  // Here's the explanation for this magic: y = 1 + z, exactly, and z
-  // approx x, thus log(y)/z (which is nearly constant near z = 0) returns
-  // a good approximation to the true log(1 + x)/x.  The multiplication x *
-  // (log(y)/z) introduces little additional error.
-  return z === 0 ? x : x * Math.log(y) / z;
-};
-
-GeographicLib.Math.atanh = function(x) {
-  var y = Math.abs(x);          // Enforce odd parity
-  y = GeographicLib.Math.log1p(2 * y/(1 - y))/2;
-  return x < 0 ? -y : y;
-};
-
-GeographicLib.Math.sum = function(u, v) {
-  var
-  s = u + v,
-  up = s - v,
-  vpp = s - up;
-  up -= u;
-  vpp -= v;
-  t = -(up + vpp);
-  // u + v =       s      + t
-  //       = round(u + v) + t
-  return {s: s, t: t};
-};
-
-GeographicLib.Math.polyval = function(N, p, s, x) {
-  var y = N < 0 ? 0 : p[s++];
-  while (--N >= 0) y = y * x + p[s++];
-  return y;
-}
-
-GeographicLib.Math.AngRound = function(x) {
-  // The makes the smallest gap in x = 1/16 - nextafter(1/16, 0) = 1/2^57 for
-  // reals = 0.7 pm on the earth if x is an angle in degrees.  (This is about
-  // 1000 times more resolution than we get with angles around 90 degrees.)  We
-  // use this to avoid having to deal with near singular cases when x is
-  // non-zero but tiny (e.g., 1.0e-200).  This also converts -0 to +0.
-  var z = 1/16;
-  var y = Math.abs(x);
-  // The compiler mustn't "simplify" z - (z - y) to y
-  y = y < z ? z - (z - y) : y;
-  return x < 0 ? 0 - y : y;
-};
-
-GeographicLib.Math.AngNormalize = function(x) {
-  // Place angle in [-180, 180).
-  x = x % 360.0;
-  return x < -180 ? x + 360 : (x < 180 ? x : x - 360);
-};
-
-GeographicLib.Math.LatFix = function(x) {
-  // Place angle with NaN if outside [-90, 90].
-  return Math.abs(x) > 90 ? Number.NaN : x;
-};
-
-GeographicLib.Math.AngDiff = function(x, y) {
-  // Compute y - x and reduce to [-180,180] accurately.
-  var
-  r = GeographicLib.Math.sum(GeographicLib.Math.AngNormalize(x),
-                             GeographicLib.Math.AngNormalize(-y)),
-  d = - GeographicLib.Math.AngNormalize(r.s);
-  t = r.t;
-  return (d == 180 && t < 0 ? -180 : d) - t;
-};
-
-GeographicLib.Math.sincosd = function(x) {
-  // In order to minimize round-off errors, this function exactly reduces
-  // the argument to the range [-45, 45] before converting it to radians.
-  var r, q;
-  r = x % 360.0;
-  q = Math.floor(r / 90 + 0.5);
-  r -= 90 * q;
-  // now abs(r) <= 45
-  r *= this.degree;
-  // Possibly could call the gnu extension sincos
-  var s = Math.sin(r), c = Math.cos(r);
-  var sinx, cosx;
-  switch (q & 3) {
-  case  0: sinx =     s; cosx =     c; break;
-  case  1: sinx =     c; cosx = 0 - s; break;
-  case  2: sinx = 0 - s; cosx = 0 - c; break;
-  default: sinx = 0 - c; cosx =     s; break; // case 3
-  }
-  return {s: sinx, c: cosx};
-};
-
-GeographicLib.Math.atan2d = function(y, x) {
-  // In order to minimize round-off errors, this function rearranges the
-  // arguments so that result of atan2 is in the range [-pi/4, pi/4] before
-  // converting it to degrees and mapping the result to the correct
-  // quadrant.
-  var q = 0;
-  if (Math.abs(y) > Math.abs(x)) { var t; t = x; x = y; y = t; q = 2; }
-  if (x < 0) { x = -x; ++q; }
-  // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
-  var ang = Math.atan2(y, x) / this.degree;
-  switch (q) {
-    // Note that atan2d(-0.0, 1.0) will return -0.  However, we expect that
-    // atan2d will not be called with y = -0.  If need be, include
-    //
-    //   case 0: ang = 0 + ang; break;
-    //
-    // and handle mpfr as in AngRound.
-  case 1: ang = (y > 0 ? 180 : -180) - ang; break;
-  case 2: ang =  90 - ang; break;
-  case 3: ang = -90 + ang; break;
-  }
-  return ang;
-};
-
-GeographicLib.Math.epsilon = Math.pow(0.5, 52);
-GeographicLib.Math.degree = Math.PI/180;
-GeographicLib.Math.digits = 53;
-
-GeographicLib.Constants = {};
-GeographicLib.Constants.WGS84 = { a: 6378137, f: 1/298.257223563 };
-GeographicLib.Constants.version = { major: 1, minor: 44, patch: 0 };
-GeographicLib.Constants.version_string = "1.44";
-
-GeographicLib.Accumulator = {};
-(function() {
-  a = GeographicLib.Accumulator;
-  var m = GeographicLib.Math;
-
-  a.Accumulator = function(y) {
-    this.Set(y);
-  };
-
-  a.Accumulator.prototype.Set = function(y) {
-    if (!y) y = 0;
-    if (y.constructor === a.Accumulator) {
-      this._s = y._s;
-      this._t = y._t;
-    } else {
-      this._s = y;
-      this._t = 0;
-    }
-  };
-
-  a.Accumulator.prototype.Add = function(y) {
-    // Here's Shewchuk's solution...
-    // Accumulate starting at least significant end
-    var u = m.sum(y, this._t);
-    var v = m.sum(u.s, this._s);
-    u = u.t;
-    this._s = v.s;
-    this._t = v.t;
-    // Start is _s, _t decreasing and non-adjacent.  Sum is now (s + t + u)
-    // exactly with s, t, u non-adjacent and in decreasing order (except
-    // for possible zeros).  The following code tries to normalize the
-    // result.  Ideally, we want _s = round(s+t+u) and _u = round(s+t+u -
-    // _s).  The follow does an approximate job (and maintains the
-    // decreasing non-adjacent property).  Here are two "failures" using
-    // 3-bit floats:
-    //
-    // Case 1: _s is not equal to round(s+t+u) -- off by 1 ulp
-    // [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3
-    //
-    // Case 2: _s+_t is not as close to s+t+u as it shold be
-    // [64, 5] + 4 -> [64, 8, 1] -> [64,  8] = 72 (off by 1)
-    //                    should be [80, -7] = 73 (exact)
-    //
-    // "Fixing" these problems is probably not worth the expense.  The
-    // representation inevitably leads to small errors in the accumulated
-    // values.  The additional errors illustrated here amount to 1 ulp of
-    // the less significant word during each addition to the Accumulator
-    // and an additional possible error of 1 ulp in the reported sum.
-    //
-    // Incidentally, the "ideal" representation described above is not
-    // canonical, because _s = round(_s + _t) may not be true.  For
-    // example, with 3-bit floats:
-    //
-    // [128, 16] + 1 -> [160, -16] -- 160 = round(145).
-    // But [160, 0] - 16 -> [128, 16] -- 128 = round(144).
-    //
-    if (this._s === 0)           // This implies t == 0,
-      this._s = u;              // so result is u
-    else
-      this._t += u;             // otherwise just accumulate u to t.
-  };
-
-  a.Accumulator.prototype.Sum = function(y) {
-    if (!y)
-      return this._s;
-    else {
-      var b = new a.Accumulator(this);
-      b.Add(y);
-      return b._s;
-    }
-  };
-
-  a.Accumulator.prototype.Negate = function() {
-    this._s *= -1;
-    this._t *= -1;
-  };
-
-})();
diff --git a/doc/scripts/GeographicLib/PolygonArea.js b/doc/scripts/GeographicLib/PolygonArea.js
deleted file mode 100644
index 73e85e8..0000000
--- a/doc/scripts/GeographicLib/PolygonArea.js
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * PolygonArea.js
- * Transcription of PolygonArea.[ch]pp into JavaScript.
- *
- * See the documentation for the C++ class.  The conversion is a literal
- * conversion from C++.
- *
- * The algorithms are derived in
- *
- *    Charles F. F. Karney,
- *    Algorithms for geodesics, J. Geodesy 87, 43-55 (2013);
- *    https://dx.doi.org/10.1007/s00190-012-0578-z
- *    Addenda: http://geographiclib.sf.net/geod-addenda.html
- *
- * Copyright (c) Charles Karney (2011-2014) <charles at karney.com> and licensed
- * under the MIT/X11 License.  For more information, see
- * http://geographiclib.sourceforge.net/
- **********************************************************************/
-
-// Load AFTER GeographicLib/Math.js and GeographicLib/Geodesic.js
-
-GeographicLib.PolygonArea = {};
-
-(function() {
-  var m = GeographicLib.Math;
-  var a = GeographicLib.Accumulator;
-  var g = GeographicLib.Geodesic;
-  var p = GeographicLib.PolygonArea;
-
-  p.transit = function(lon1, lon2) {
-    // Return 1 or -1 if crossing prime meridian in east or west direction.
-    // Otherwise return zero.
-    // Compute lon12 the same way as Geodesic::Inverse.
-    lon1 = m.AngNormalize(lon1);
-    lon2 = m.AngNormalize(lon2);
-    var lon12 = m.AngDiff(lon1, lon2);
-    var cross =
-      lon1 < 0 && lon2 >= 0 && lon12 > 0 ? 1 :
-      (lon2 < 0 && lon1 >= 0 && lon12 < 0 ? -1 : 0);
-    return cross;
-  };
-
-  // an alternate version of transit to deal with longitudes in the direct
-  // problem.
-  p.transitdirect = function(lon1, lon2) {
-    // We want to compute exactly
-    //   int(floor(lon2 / 360)) - int(floor(lon1 / 360))
-    // Since we only need the parity of the result we can use std::remquo but
-    // this is buggy with g++ 4.8.3 and requires C++11.  So instead we do
-    lon1 = lon1 % 720.0; lon2 = lon2 % 720.0;
-    return ( ((lon2 >= 0 && lon2 < 360) || lon2 < -360 ? 0 : 1) -
-             ((lon1 >= 0 && lon1 < 360) || lon1 < -360 ? 0 : 1) );
-  };
-
-  p.PolygonArea = function(earth, polyline) {
-    this._earth = earth;
-    this._area0 = 4 * Math.PI * earth._c2;
-    this._polyline = !polyline ? false : polyline;
-    this._mask = g.LATITUDE | g.LONGITUDE | g.DISTANCE |
-          (this._polyline ? g.NONE : g.AREA | g.LONG_UNROLL);
-    if (!this._polyline)
-      this._areasum = new a.Accumulator(0);
-    this._perimetersum = new a.Accumulator(0);
-    this.Clear();
-  };
-
-  p.PolygonArea.prototype.Clear = function() {
-    this._num = 0;
-    this._crossings = 0;
-    if (!this._polyline)
-      this._areasum.Set(0);
-    this._perimetersum.Set(0);
-    this._lat0 = this._lon0 = this._lat1 = this._lon1 = Number.NaN;
-  };
-
-  p.PolygonArea.prototype.AddPoint = function(lat, lon) {
-    if (this._num === 0) {
-      this._lat0 = this._lat1 = lat;
-      this._lon0 = this._lon1 = lon;
-    } else {
-      var t = this._earth.Inverse(this._lat1, this._lon1, lat, lon, this._mask);
-      this._perimetersum.Add(t.s12);
-      if (!this._polyline) {
-        this._areasum.Add(t.S12);
-        this._crossings += p.transit(this._lon1, lon);
-      }
-      this._lat1 = lat;
-      this._lon1 = lon;
-    }
-    ++this._num;
-  };
-
-  p.PolygonArea.prototype.AddEdge = function(azi, s) {
-    if (this._num) {
-      var t = this._earth.Direct(this._lat1, this._lon1, azi, s, this._mask);
-      this._perimetersum.Add(s);
-      if (!this._polyline) {
-        this._areasum.Add(t.S12);
-        this._crossings += p.transitdirect(this._lon1, t.lon2);
-      }
-      this._lat1 = t.lat2;
-      this._lon1 = t.lon2;
-    }
-    ++this._num;
-  };
-
-  // return number, perimeter, area
-  p.PolygonArea.prototype.Compute = function(reverse, sign) {
-    var vals = {number: this._num};
-    if (this._num < 2) {
-      vals.perimeter = 0;
-      if (!this._polyline)
-        vals.area = 0;
-      return vals;
-    }
-    if (this._polyline) {
-      vals.perimeter = this._perimetersum.Sum();
-      return vals;
-    }
-    var t = this._earth.Inverse(this._lat1, this._lon1, this._lat0, this._lon0,
-                                this._mask);
-    vals.perimeter = this._perimetersum.Sum(t.s12);
-    var tempsum = new a.Accumulator(this._areasum);
-    tempsum.Add(t.S12);
-    var crossings = this._crossings + p.transit(this._lon1, this._lon0);
-    if (crossings & 1)
-      tempsum.Add( (tempsum.Sum() < 0 ? 1 : -1) * this._area0/2 );
-    // area is with the clockwise sense.  If !reverse convert to
-    // counter-clockwise convention.
-    if (!reverse)
-      tempsum.Negate();
-    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
-    if (sign) {
-      if (tempsum.Sum() > this._area0/2)
-        tempsum.Add( -this._area0 );
-      else if (tempsum.Sum() <= -this._area0/2)
-        tempsum.Add( +this._area0 );
-    } else {
-      if (tempsum.Sum() >= this._area0)
-        tempsum.Add( -this._area0 );
-      else if (tempsum < 0)
-        tempsum.Add( -this._area0 );
-    }
-    vals.area = tempsum.Sum();
-    return vals;
-  };
-
-  // return number, perimeter, area
-  p.PolygonArea.prototype.TestPoint = function(lat, lon, reverse, sign) {
-    var vals = {number: this._num + 1};
-    if (this._num === 0) {
-      vals.perimeter = 0;
-      if (!this._polyline)
-        vals.area = 0;
-      return vals;
-    }
-    vals.perimeter = this._perimetersum.Sum();
-    var tempsum = this._polyline ? 0 : this._areasum.Sum();
-    var crossings = this._crossings;
-    var t;
-    for (var i = 0; i < (this._polyline ? 1 : 2); ++i) {
-      t = this._earth.Inverse(
-       i === 0 ? this._lat1 : lat, i === 0 ? this._lon1 : lon,
-       i !== 0 ? this._lat0 : lat, i !== 0 ? this._lon0 : lon,
-       this._mask);
-      vals.perimeter += t.s12;
-      if (!this._polyline) {
-        tempsum += t.S12;
-        crossings += p.transit(i === 0 ? this._lon1 : lon,
-                               i !== 0 ? this._lon0 : lon);
-      }
-    }
-
-    if (this._polyline)
-      return vals;
-
-    if (crossings & 1)
-      tempsum += (tempsum < 0 ? 1 : -1) * this._area0/2;
-    // area is with the clockwise sense.  If !reverse convert to
-    // counter-clockwise convention.
-    if (!reverse)
-      tempsum *= -1;
-    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
-    if (sign) {
-      if (tempsum > this._area0/2)
-        tempsum -= this._area0;
-      else if (tempsum <= -this._area0/2)
-        tempsum += this._area0;
-    } else {
-      if (tempsum >= this._area0)
-        tempsum -= this._area0;
-      else if (tempsum < 0)
-        tempsum += this._area0;
-    }
-    vals.area = tempsum;
-    return vals;
-  };
-
-  // return number, perimeter, area
-  p.PolygonArea.prototype.TestEdge = function(azi, s, reverse, sign) {
-    var vals = {number: this._num ? this._num + 1 : 0};
-    if (this._num === 0)
-      return vals;
-    vals.perimeter = this._perimetersum.Sum() + s;
-    if (this._polyline)
-      return vals;
-
-    var tempsum = this._areasum.Sum();
-    var crossings = this._crossings;
-    var t;
-    t = this._earth.Direct(this._lat1, this._lon1, azi, s, this._mask);
-    tempsum += t.S12;
-    crossings += p.transitdirect(this._lon1, t.lon2);
-    t = this._earth(t.lat2, t.lon2, this._lat0, this._lon0, this._mask);
-    perimeter += t.s12;
-    tempsum += t.S12;
-    crossings += p.transit(t.lon2, this._lon0);
-
-    if (crossings & 1)
-      tempsum += (tempsum < 0 ? 1 : -1) * this._area0/2;
-    // area is with the clockwise sense.  If !reverse convert to
-    // counter-clockwise convention.
-    if (!reverse)
-      tempsum *= -1;
-    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
-    if (sign) {
-      if (tempsum > this._area0/2)
-        tempsum -= this._area0;
-      else if (tempsum <= -this._area0/2)
-        tempsum += this._area0;
-    } else {
-      if (tempsum >= this._area0)
-        tempsum -= this._area0;
-      else if (tempsum < 0)
-        tempsum += this._area0;
-    }
-    vals.area = tempsum;
-    return vals;
-  };
-
-  p.PolygonArea.prototype.CurrentPoint = function() {
-    var vals = {lat: this._lat1, lon: this._lon1};
-    return vals;
-  };
-
-  p.Area = function(earth, points, polyline) {
-    var poly = new p.PolygonArea(earth, polyline);
-    for (var i = 0; i < points.length; ++i)
-      poly.AddPoint(points[i].lat, points[i].lon);
-    return poly.Compute(false, true);
-  };
-
-})();
diff --git a/doc/scripts/geod-google.html b/doc/scripts/geod-google.html
deleted file mode 100644
index d6d4b67..0000000
--- a/doc/scripts/geod-google.html
+++ /dev/null
@@ -1,231 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
-    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
-    <title>Geodesic lines, circles, envelopes in Google Maps</title>
-    <meta name="description"
-	  content="Geodesic lines, circles, envelopes in Google Maps" />
-    <meta name="author" content="Charles F. F. Karney">
-    <meta name="keywords"
-	  content="geodesics,
-		   geodesic distance,
-		   geographic distance,
-		   shortest path,
-		   direct geodesic problem,
-		   inverse geodesic problem,
-		   distance and azimuth,
-		   distance and heading,
-		   range and bearing,
-		   geographic circle,
-		   geodesic envelope,
-		   geodesic astroid,
-		   latitude and longitude,
-		   Google Maps,
-		   WGS84 ellipsoid,
-		   GeographicLib" />
-    <meta http-equiv="Content-Type"
-	  content="text/html; charset=ISO-8859-1">
-    <style>
-      html, body, #map-canvas {
-        height: 100%;
-        margin: 0px;
-        padding: 0px
-      }
-    </style>
-    <script type="text/javascript"
-	    src="http://geographiclib.sf.net/scripts/geographiclib.js">
-    </script>
-    <script type="text/javascript"
-	    src="https://maps.googleapis.com/maps/api/js?sensor=false">
-    </script>
-    <script type="text/javascript">
-var geod = GeographicLib.Geodesic.WGS84;
-var dms = GeographicLib.DMS;
-var geodesic, circle, envelope;
-
-function initialize() {
-  var mapOptions = {
-    zoom: 8,
-    center: new google.maps.LatLng(41.3, -5.5),
-    mapTypeId: google.maps.MapTypeId.ROADMAP
-  };
-  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
-
-  var geodesicOptions = {
-    strokeColor: '#0000FF',
-    strokeOpacity: 1,
-    strokeWeight: 3,
-    geodesic: true
-  }
-  geodesic = new google.maps.Polyline(geodesicOptions);
-  geodesic.setMap(map);
-
-  var circleOptions = {
-    strokeColor: '#00FF00',
-    strokeOpacity: 1,
-    strokeWeight: 3,
-    geodesic: true
-  }
-  circle = new google.maps.Polyline(circleOptions);
-  circle.setMap(map);
-
-  var envelopeOptions = {
-    strokeColor: '#FF0000',
-    strokeOpacity: 1,
-    strokeWeight: 3,
-    geodesic: true
-  }
-  envelope = new Array(4);
-  for (var i = 0; i < 4; ++i) {
-    envelope[i] = new google.maps.Polyline(envelopeOptions);
-    envelope[i].setMap(map);
-  }
-}
-
-function formatpoint(lat, lon, azi, dmsformat, prec) {
-  prec += 5;
-  if (dmsformat) {
-    var trail = prec < 2 ? dms.DEGREE :
-      (prec < 4 ? dms.MINUTE : dms.SECOND);
-    prec = prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4);
-    return (dms.Encode(lat, trail, prec, dms.LATITUDE) + " " +
-	    dms.Encode(lon, trail, prec, dms.LONGITUDE) + " " +
-	    dms.Encode(azi, trail, prec, dms.AZIMUTH));
-  } else {
-    return (lat.toFixed(prec) + " " +
-	    lon.toFixed(prec) + " " +
-	    azi .toFixed(prec));
-  }
-}
-
-function GeodesicInverse(input) {
-  var result = {};
-  try {
-    // Input is a blank-delimited line: lat1 lon1 lat2 lon2
-    var t = new String(input);
-    t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
-    if (t.length != 4)
-      throw new Error("Need 4 input items");
-    var p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
-    var p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
-    var v = geod.Inverse(p1.lat, p1.lon, p2.lat, p2.lon);
-    result.status = "OK";
-    result.p1 = formatpoint(v.lat1, v.lon1, v.azi1, true, 0);
-    result.p2 = formatpoint(v.lat2, v.lon2, v.azi2, true, 0);
-    result.s12 = v.s12.toFixed(0);
-    draw(v);
-  }
-  catch (e) {
-    result.status = "ERROR: " + e.message;
-    result.p1 = "";
-    result.p2 = "";
-    result.s12 = "";
-  }
-  return result;
-}
-function GeodesicDirect(input) {
-  var result = {};
-  try {
-    // Input is a blank-delimited line: lat1 lon1 azi1 s12
-    var t = new String(input);
-   t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
-    if (t.length != 4)
-      throw new Error("Need 4 input items");
-    var p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
-    var azi1 = GeographicLib.DMS.DecodeAzimuth(t[2]);
-    var s12 = parseFloat(t[3]);
-    var v =  geod.Direct(p1.lat, p1.lon, azi1, s12);
-    result.status = "OK";
-    result.p1 = formatpoint(v.lat1, v.lon1, v.azi1, true, 0);
-    result.p2 = formatpoint(v.lat2, v.lon2, v.azi2, true, 0);
-    result.s12 = v.s12.toFixed(0);
-    draw(v);
-  }
-  catch (e) {
-    result.status = "ERROR: " + e.message;
-    result.p1 = "";
-    result.p2 = "";
-    result.s12 = "";
-  }
-  return result;
-}
-
-function draw(v) {
-  var points = geod.DirectPath(v.lat1, v.lon1, v.azi1, v.s12,
-			100000, 100);
-  clearPaths();
-  var i, k, path;
-
-  path = geodesic.getPath();
-  for (k = 0; k < points.length; ++k)
-    path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
-  points = geod.Circle(v.lat1, v.lon1, v.azi1, v.s12, 72);
-  path = circle.getPath();
-  for (k = 0; k < points.length; ++k)
-    path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
-  for (i = 0; i < 4; ++i) {
-    points = geod.Envelope(v.lat1, v.lon1, 72, i+1);
-    path = envelope[i].getPath();
-    for (k = 0; k < points.length; ++k)
-      path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
-  }
-  map.panTo(new google.maps.LatLng(v.lat2, v.lon2));
-}
-
-function clearPaths() {
-  cPath = geodesic.getPath();
-  while (cPath.getLength()) cPath.pop();
-  cPath = circle.getPath();
-  while (cPath.getLength()) cPath.pop();
-  for (var i = 0; i < 4; ++i) {
-    cPath = envelope[i].getPath();
-    while (cPath.getLength()) cPath.pop();
-  }
-}
-    </script>
-  </head>
-  <body onload="initialize()">
-    <div id="map-canvas"
-	 style="position:relative; border: 1px solid black; width:99.5%;
-	 height:72%;">
-    </div>
-    <div>
-      <p>
-	 Direct:  
-	<input id="inputb" size=60 value="41°19'S 174°49'E 135° 60000e3" />
-	<input type="button" value="compute"
-	       onclick="var t = GeodesicDirect(document.getElementById('inputb').value);
-			document.getElementById('status').value = t.status;
-			document.getElementById('p1').value = t.p1;
-			document.getElementById('p2').value = t.p2;
-			document.getElementById('s12').value = t.s12;" />
-      </p>
-      <p>
-	 Inverse:
-	<input id="inputa" size=60 value="41°19'S 174°49'E 40°58'N 5°30'W" />
-	<input type="button" value="compute"
-	       onclick="var t = GeodesicInverse(document.getElementById('inputa').value);
-			document.getElementById('status').value = t.status;
-			document.getElementById('p1').value = t.p1;
-			document.getElementById('p2').value = t.p2;
-			document.getElementById('s12').value = t.s12;" />
-      </p>
-      <p>
-	 lat1 lon1 azi1: <input type="text" "readonly" id="p1" size=60>
-      </p>
-      <p>
-	 lat2 lon2 azi2: <input type="text" "readonly" id="p2" size=60>
-      </p>
-      <p>
-	 s12: <input type="text" "readonly" id="s12" size=20>
-	   status: <input "readonly" id="status" size=30>
-	  
-	<a href="geod-google-instructions.html"><b>INSTRUCTIONS</b></a>
-	(v<script type="text/javascript">
-	  document.write(GeographicLib.Constants.version_string);
-	</script>)
-      </p>
-  </div>
-  </body>
-</html>
diff --git a/dotnet/NETGeographicLib/AlbersEqualArea.h b/dotnet/NETGeographicLib/AlbersEqualArea.h
index 15bb419..58cce2c 100644
--- a/dotnet/NETGeographicLib/AlbersEqualArea.h
+++ b/dotnet/NETGeographicLib/AlbersEqualArea.h
@@ -97,8 +97,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] stdlat standard parallel (degrees), the circle of tangency.
          * @param[in] k0 azimuthal scale on the standard parallel.
          * @exception GeographicErr if \e a, (1 − \e f ) \e a, or \e k0 is
@@ -113,8 +112,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] stdlat1 first standard parallel (degrees).
          * @param[in] stdlat2 second standard parallel (degrees).
          * @param[in] k1 azimuthal scale on the standard parallels.
@@ -131,8 +129,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] sinlat1 sine of first standard parallel.
          * @param[in] coslat1 cosine of first standard parallel.
          * @param[in] sinlat2 sine of second standard parallel.
diff --git a/dotnet/NETGeographicLib/Ellipsoid.h b/dotnet/NETGeographicLib/Ellipsoid.h
index cae9107..5e9f121 100644
--- a/dotnet/NETGeographicLib/Ellipsoid.h
+++ b/dotnet/NETGeographicLib/Ellipsoid.h
@@ -67,8 +67,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @exception GeographicErr if \e a or (1 − \e f ) \e a is not
          *   positive.
          **********************************************************************/
diff --git a/dotnet/NETGeographicLib/Geocentric.h b/dotnet/NETGeographicLib/Geocentric.h
index 404d3b3..b078ce3 100644
--- a/dotnet/NETGeographicLib/Geocentric.h
+++ b/dotnet/NETGeographicLib/Geocentric.h
@@ -79,8 +79,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @exception GeographicErr if \e a or (1 − \e f ) \e a is not
          *   positive.
          **********************************************************************/
diff --git a/dotnet/NETGeographicLib/Geodesic.h b/dotnet/NETGeographicLib/Geodesic.h
index 886a78b..ebec5c9 100644
--- a/dotnet/NETGeographicLib/Geodesic.h
+++ b/dotnet/NETGeographicLib/Geodesic.h
@@ -101,23 +101,23 @@ namespace NETGeographicLib
    * catalog of those cases:
    * - \e lat1 = −\e lat2 (with neither point at a pole).  If \e azi1 =
    *   \e azi2, the geodesic is unique.  Otherwise there are two geodesics and
-   *   the second one is obtained by setting [\e azi1, \e azi2] = [\e azi2, \e
-   *   azi1], [\e M12, \e M21] = [\e M21, \e M12], \e S12 = −\e S12.
-   *   (This occurs when the longitude difference is near ±180° for
-   *   oblate ellipsoids.)
+   *   the second one is obtained by setting [\e azi1, \e azi2] → [\e
+   *   azi2, \e azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 →
+   *   −\e S12.  (This occurs when the longitude difference is near
+   *   ±180° for oblate ellipsoids.)
    * - \e lon2 = \e lon1 ± 180° (with neither point at a pole).  If
    *   \e azi1 = 0° or ±180°, the geodesic is unique.  Otherwise
    *   there are two geodesics and the second one is obtained by setting [\e
-   *   azi1, \e azi2] = [−\e azi1, −\e azi2], \e S12 = −\e
-   *   S12.  (This occurs when \e lat2 is near −\e lat1 for prolate
-   *   ellipsoids.)
+   *   azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 →
+   *   −\e S12.  (This occurs when \e lat2 is near −\e lat1 for
+   *   prolate ellipsoids.)
    * - Points 1 and 2 at opposite poles.  There are infinitely many geodesics
-   *   which can be generated by setting [\e azi1, \e azi2] = [\e azi1, \e
+   *   which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e
    *   azi2] + [\e d, −\e d], for arbitrary \e d.  (For spheres, this
    *   prescription applies when points 1 and 2 are antipodal.)
    * - s12 = 0 (coincident points).  There are infinitely many geodesics which
-   *   can be generated by setting [\e azi1, \e azi2] = [\e azi1, \e azi2] +
-   *   [\e d, \e d], for arbitrary \e d.
+   *   can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e azi2]
+   *   + [\e d, \e d], for arbitrary \e d.
    *
    * The calculations are accurate to better than 15 nm (15 nanometers) for the
    * WGS84 ellipsoid.  See Sec. 9 of
@@ -256,8 +256,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @exception GeographicErr if \e a or (1 − \e f ) \e a is not
          *   positive.
          **********************************************************************/
diff --git a/dotnet/NETGeographicLib/GeodesicExact.h b/dotnet/NETGeographicLib/GeodesicExact.h
index 640e696..b1cb6a8 100644
--- a/dotnet/NETGeographicLib/GeodesicExact.h
+++ b/dotnet/NETGeographicLib/GeodesicExact.h
@@ -191,8 +191,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @exception GeographicErr if \e a or (1 − \e f ) \e a is not
          *   positive.
          **********************************************************************/
diff --git a/dotnet/NETGeographicLib/LambertConformalConic.h b/dotnet/NETGeographicLib/LambertConformalConic.h
index a23f135..cd491a1 100644
--- a/dotnet/NETGeographicLib/LambertConformalConic.h
+++ b/dotnet/NETGeographicLib/LambertConformalConic.h
@@ -77,8 +77,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] stdlat standard parallel (degrees), the circle of tangency.
          * @param[in] k0 scale on the standard parallel.
          * @exception GeographicErr if \e a, (1 − \e f ) \e a, or \e k0 is
@@ -93,8 +92,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] stdlat1 first standard parallel (degrees).
          * @param[in] stdlat2 second standard parallel (degrees).
          * @param[in] k1 scale on the standard parallels.
@@ -111,8 +109,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius of ellipsoid (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] sinlat1 sine of first standard parallel.
          * @param[in] coslat1 cosine of first standard parallel.
          * @param[in] sinlat2 sine of second standard parallel.
diff --git a/dotnet/NETGeographicLib/PolarStereographic.h b/dotnet/NETGeographicLib/PolarStereographic.h
index 2240fac..52f43e9 100644
--- a/dotnet/NETGeographicLib/PolarStereographic.h
+++ b/dotnet/NETGeographicLib/PolarStereographic.h
@@ -60,8 +60,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] k0 central scale factor.
          * @exception GeographicErr if \e a, (1 − \e f ) \e a, or \e k0 is
          *   not positive.
diff --git a/dotnet/NETGeographicLib/Rhumb.h b/dotnet/NETGeographicLib/Rhumb.h
index 06fdd66..a27a450 100644
--- a/dotnet/NETGeographicLib/Rhumb.h
+++ b/dotnet/NETGeographicLib/Rhumb.h
@@ -125,8 +125,7 @@ namespace NETGeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] exact if true (the default) use an addition theorem for
      *   elliptic integrals to compute divided differences; otherwise use
      *   series expansion (accurate for |<i>f</i>| < 0.01).
diff --git a/dotnet/NETGeographicLib/TransverseMercator.h b/dotnet/NETGeographicLib/TransverseMercator.h
index 0ebbff4..f8d6c14 100644
--- a/dotnet/NETGeographicLib/TransverseMercator.h
+++ b/dotnet/NETGeographicLib/TransverseMercator.h
@@ -87,8 +87,7 @@ namespace NETGeographicLib
          *
          * @param[in] a equatorial radius (meters).
          * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-         *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         *   Negative \e f gives a prolate ellipsoid.
          * @param[in] k0 central scale factor.
          * @exception GeographicErr if \e a, (1 − \e f ) \e a, or \e k0 is
          *   not positive.
diff --git a/dotnet/NETGeographicLib/TransverseMercatorExact.h b/dotnet/NETGeographicLib/TransverseMercatorExact.h
index 5175948..7874cf1 100644
--- a/dotnet/NETGeographicLib/TransverseMercatorExact.h
+++ b/dotnet/NETGeographicLib/TransverseMercatorExact.h
@@ -93,8 +93,7 @@ namespace NETGeographicLib
          * Constructor for a ellipsoid with
          *
          * @param[in] a equatorial radius (meters).
-         * @param[in] f flattening of ellipsoid.  If \e f > 1, set flattening
-         *   to 1/\e f.
+         * @param[in] f flattening of ellipsoid.
          * @param[in] k0 central scale factor.
          * @param[in] extendp use extended domain.
          * @exception GeographicErr if \e a, \e f, or \e k0 is not positive.
diff --git a/examples/JacobiConformal.cpp b/examples/JacobiConformal.cpp
index 37bf20b..8033bdd 100644
--- a/examples/JacobiConformal.cpp
+++ b/examples/JacobiConformal.cpp
@@ -8,6 +8,17 @@ using namespace GeographicLib;
 
 int main() {
   Utility::set_digits();
+  // These parameters were derived from the EGM2008 geoid; see 2011-07-04
+  // E-mail to PROJ.4 list, "Analyzing the bumps in the EGM2008 geoid".  The
+  // longitude of the major axis is -15.  These are close to the values given
+  // by Milan Bursa, Vladimira Fialova, "Parameters of the Earth's tri-axial
+  // level ellipsoid", Studia Geophysica et Geodaetica 37(1), 1-13 (1993):
+  //
+  //    longitude of major axis = -14.93 +/- 0.05
+  //    a = 6378171.36 +/- 0.30
+  //    a/(a-c) = 297.7738 +/- 0.0003
+  //    a/(a-b) = 91449 +/- 60
+  // which gives: a = 6378171.36, b = 6378101.61, c = 6356751.84
   Math::real a = 6378137+35, b = 6378137-35, c = 6356752;
   JacobiConformal jc(a, b, c, a-b, b-c);
   cout  << fixed << setprecision(1)
diff --git a/include/GeographicLib/Accumulator.hpp b/include/GeographicLib/Accumulator.hpp
index b90f410..c0502a9 100644
--- a/include/GeographicLib/Accumulator.hpp
+++ b/include/GeographicLib/Accumulator.hpp
@@ -17,7 +17,7 @@ namespace GeographicLib {
   /**
    * \brief An accumulator for sums
    *
-   * This allow many numbers of floating point type \e T to be added together
+   * This allows many numbers of floating point type \e T to be added together
    * with twice the normal precision.  Thus if \e T is double, the effective
    * precision of the sum is 106 bits or about 32 decimal places.
    *
diff --git a/include/GeographicLib/AlbersEqualArea.hpp b/include/GeographicLib/AlbersEqualArea.hpp
index b4ca0fb..146162a 100644
--- a/include/GeographicLib/AlbersEqualArea.hpp
+++ b/include/GeographicLib/AlbersEqualArea.hpp
@@ -124,8 +124,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] stdlat standard parallel (degrees), the circle of tangency.
      * @param[in] k0 azimuthal scale on the standard parallel.
      * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
@@ -140,8 +139,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] stdlat1 first standard parallel (degrees).
      * @param[in] stdlat2 second standard parallel (degrees).
      * @param[in] k1 azimuthal scale on the standard parallels.
@@ -158,8 +156,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] sinlat1 sine of first standard parallel.
      * @param[in] coslat1 cosine of first standard parallel.
      * @param[in] sinlat2 sine of second standard parallel.
diff --git a/include/GeographicLib/Config.h b/include/GeographicLib/Config.h
index ca5f29c..0405505 100644
--- a/include/GeographicLib/Config.h
+++ b/include/GeographicLib/Config.h
@@ -1,8 +1,8 @@
 // This will be overwritten by ./configure
 
-#define GEOGRAPHICLIB_VERSION_STRING "1.44"
+#define GEOGRAPHICLIB_VERSION_STRING "1.45"
 #define GEOGRAPHICLIB_VERSION_MAJOR 1
-#define GEOGRAPHICLIB_VERSION_MINOR 44
+#define GEOGRAPHICLIB_VERSION_MINOR 45
 #define GEOGRAPHICLIB_VERSION_PATCH 0
 
 // Undefine HAVE_LONG_DOUBLE if this type is unknown to the compiler
diff --git a/include/GeographicLib/Config.h.in b/include/GeographicLib/Config.h.in
index c192c94..909cdc0 100644
--- a/include/GeographicLib/Config.h.in
+++ b/include/GeographicLib/Config.h.in
@@ -15,7 +15,7 @@
 // cmake will correctly define as 0 or 1 when only one type of library is in
 // the package.  If both shared and static libraries are available,
 // GEOGRAPHICLIB_SHARED_LIB is set to 2 which triggers a preprocessor error in
-// Geographic.hpp.  In this case, the appropriate value (0 or 1) for
+// Constants.hpp.  In this case, the appropriate value (0 or 1) for
 // GEOGRAPHICLIB_SHARED_LIB must be specified when compiling any program that
 // includes Geographic.hpp.  This is done automatically if GeographicLib and
 // the the user's code were built with cmake version 2.8.11 (which introduced
diff --git a/include/GeographicLib/DMS.hpp b/include/GeographicLib/DMS.hpp
index 0043cbd..8671f2c 100644
--- a/include/GeographicLib/DMS.hpp
+++ b/include/GeographicLib/DMS.hpp
@@ -311,7 +311,7 @@ namespace GeographicLib {
      *   necessary).
      * @param[in] prec the number of digits after the decimal point for the
      *   trailing component.
-     * @param[in] ind DMS::flag value indicated additional formatting.
+     * @param[in] ind DMS::flag value indicating additional formatting.
      * @param[in] dmssep if non-null, use as the DMS separator character
      *   (instead of d, ', " delimiters).
      * @exception std::bad_alloc if memory for the string can't be allocated.
@@ -325,7 +325,7 @@ namespace GeographicLib {
      * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no
      *   sign, pad degrees to 3 digits, e.g., 008d03'W.
      * - ind == DMS::AZIMUTH, convert to the range [0, 360°), no
-     *   sign, pad degrees to 3 digits, , e.g., 351d57'.
+     *   sign, pad degrees to 3 digits, e.g., 351d57'.
      * .
      * The integer parts of the minutes and seconds components are always given
      * with 2 digits.
diff --git a/include/GeographicLib/Ellipsoid.hpp b/include/GeographicLib/Ellipsoid.hpp
index 9b16d49..2939504 100644
--- a/include/GeographicLib/Ellipsoid.hpp
+++ b/include/GeographicLib/Ellipsoid.hpp
@@ -62,8 +62,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @exception GeographicErr if \e a or (1 − \e f) \e a is not
      *   positive.
      **********************************************************************/
diff --git a/include/GeographicLib/Geocentric.hpp b/include/GeographicLib/Geocentric.hpp
index d2bbd6a..26c4b1f 100644
--- a/include/GeographicLib/Geocentric.hpp
+++ b/include/GeographicLib/Geocentric.hpp
@@ -106,8 +106,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @exception GeographicErr if \e a or (1 − \e f) \e a is not
      *   positive.
      **********************************************************************/
diff --git a/include/GeographicLib/Geodesic.hpp b/include/GeographicLib/Geodesic.hpp
index ee312f3..1eae78d 100644
--- a/include/GeographicLib/Geodesic.hpp
+++ b/include/GeographicLib/Geodesic.hpp
@@ -112,23 +112,23 @@ namespace GeographicLib {
    * catalog of those cases:
    * - \e lat1 = −\e lat2 (with neither point at a pole).  If \e azi1 =
    *   \e azi2, the geodesic is unique.  Otherwise there are two geodesics and
-   *   the second one is obtained by setting [\e azi1, \e azi2] = [\e azi2, \e
-   *   azi1], [\e M12, \e M21] = [\e M21, \e M12], \e S12 = −\e S12.
-   *   (This occurs when the longitude difference is near ±180° for
-   *   oblate ellipsoids.)
+   *   the second one is obtained by setting [\e azi1, \e azi2] → [\e
+   *   azi2, \e azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 →
+   *   −\e S12.  (This occurs when the longitude difference is near
+   *   ±180° for oblate ellipsoids.)
    * - \e lon2 = \e lon1 ± 180° (with neither point at a pole).  If
    *   \e azi1 = 0° or ±180°, the geodesic is unique.  Otherwise
    *   there are two geodesics and the second one is obtained by setting [\e
-   *   azi1, \e azi2] = [−\e azi1, −\e azi2], \e S12 = −\e
-   *   S12.  (This occurs when \e lat2 is near −\e lat1 for prolate
-   *   ellipsoids.)
+   *   azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 →
+   *   −\e S12.  (This occurs when \e lat2 is near −\e lat1 for
+   *   prolate ellipsoids.)
    * - Points 1 and 2 at opposite poles.  There are infinitely many geodesics
-   *   which can be generated by setting [\e azi1, \e azi2] = [\e azi1, \e
+   *   which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e
    *   azi2] + [\e d, −\e d], for arbitrary \e d.  (For spheres, this
    *   prescription applies when points 1 and 2 are antipodal.)
    * - s12 = 0 (coincident points).  There are infinitely many geodesics which
-   *   can be generated by setting [\e azi1, \e azi2] = [\e azi1, \e azi2] +
-   *   [\e d, \e d], for arbitrary \e d.
+   *   can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e azi2]
+   *   + [\e d, \e d], for arbitrary \e d.
    *
    * The calculations are accurate to better than 15 nm (15 nanometers) for the
    * WGS84 ellipsoid.  See Sec. 9 of
@@ -331,8 +331,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @exception GeographicErr if \e a or (1 − \e f) \e a is not
      *   positive.
      **********************************************************************/
diff --git a/include/GeographicLib/GeodesicExact.hpp b/include/GeographicLib/GeodesicExact.hpp
index 3472ebe..b625b38 100644
--- a/include/GeographicLib/GeodesicExact.hpp
+++ b/include/GeographicLib/GeodesicExact.hpp
@@ -226,8 +226,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @exception GeographicErr if \e a or (1 − \e f) \e a is not
      *   positive.
      **********************************************************************/
diff --git a/include/GeographicLib/Gnomonic.hpp b/include/GeographicLib/Gnomonic.hpp
index 5b4596e..be3645c 100644
--- a/include/GeographicLib/Gnomonic.hpp
+++ b/include/GeographicLib/Gnomonic.hpp
@@ -82,9 +82,9 @@ namespace GeographicLib {
    *   sphere at the center point.  This causes normal sections through the
    *   center point to appear as straight lines in the projection; i.e., it
    *   generalizes the spherical great circle to a normal section.  This was
-   *   proposed by I. G. Letoval'tsev, Generalization of the %Gnomonic
-   *   Projection for a Spheroid and the Principal Geodetic Problems Involved
-   *   in the Alignment of Surface Routes, Geodesy and Aerophotography (5),
+   *   proposed by I. G. Letoval'tsev, Generalization of the gnomonic
+   *   projection for a spheroid and the principal geodetic problems involved
+   *   in the alignment of surface routes, Geodesy and Aerophotography (5),
    *   271--274 (1963).
    * - The projection given here.  This causes geodesics close to the center
    *   point to appear as straight lines in the projection; i.e., it
diff --git a/include/GeographicLib/LambertConformalConic.hpp b/include/GeographicLib/LambertConformalConic.hpp
index 06f3d7d..977eff8 100644
--- a/include/GeographicLib/LambertConformalConic.hpp
+++ b/include/GeographicLib/LambertConformalConic.hpp
@@ -136,8 +136,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] stdlat standard parallel (degrees), the circle of tangency.
      * @param[in] k0 scale on the standard parallel.
      * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
@@ -152,8 +151,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] stdlat1 first standard parallel (degrees).
      * @param[in] stdlat2 second standard parallel (degrees).
      * @param[in] k1 scale on the standard parallels.
@@ -170,8 +168,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius of ellipsoid (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] sinlat1 sine of first standard parallel.
      * @param[in] coslat1 cosine of first standard parallel.
      * @param[in] sinlat2 sine of second standard parallel.
diff --git a/include/GeographicLib/PolarStereographic.hpp b/include/GeographicLib/PolarStereographic.hpp
index fd9e90f..0e7213f 100644
--- a/include/GeographicLib/PolarStereographic.hpp
+++ b/include/GeographicLib/PolarStereographic.hpp
@@ -41,8 +41,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] k0 central scale factor.
      * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
      *   not positive.
diff --git a/include/GeographicLib/PolygonArea.hpp b/include/GeographicLib/PolygonArea.hpp
index e3cf32f..f21f7e8 100644
--- a/include/GeographicLib/PolygonArea.hpp
+++ b/include/GeographicLib/PolygonArea.hpp
@@ -168,6 +168,8 @@ namespace GeographicLib {
      * @param[out] area the area of the polygon (meters<sup>2</sup>); only set
      *   if \e polyline is false in the constructor.
      * @return the number of points.
+     *
+     * More points can be added to the polygon after this call.
      **********************************************************************/
     unsigned Compute(bool reverse, bool sign,
                      real& perimeter, real& area) const;
diff --git a/include/GeographicLib/Rhumb.hpp b/include/GeographicLib/Rhumb.hpp
index 58c4db8..feacb2a 100644
--- a/include/GeographicLib/Rhumb.hpp
+++ b/include/GeographicLib/Rhumb.hpp
@@ -238,8 +238,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] exact if true (the default) use an addition theorem for
      *   elliptic integrals to compute divided differences; otherwise use
      *   series expansion (accurate for |<i>f</i>| < 0.01).
diff --git a/include/GeographicLib/TransverseMercator.hpp b/include/GeographicLib/TransverseMercator.hpp
index b92b611..6f95f0c 100644
--- a/include/GeographicLib/TransverseMercator.hpp
+++ b/include/GeographicLib/TransverseMercator.hpp
@@ -102,8 +102,7 @@ namespace GeographicLib {
      *
      * @param[in] a equatorial radius (meters).
      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
-     *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set
-     *   flattening to 1/\e f.
+     *   Negative \e f gives a prolate ellipsoid.
      * @param[in] k0 central scale factor.
      * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
      *   not positive.
diff --git a/include/GeographicLib/TransverseMercatorExact.hpp b/include/GeographicLib/TransverseMercatorExact.hpp
index 8d4d546..584d3e6 100644
--- a/include/GeographicLib/TransverseMercatorExact.hpp
+++ b/include/GeographicLib/TransverseMercatorExact.hpp
@@ -118,8 +118,7 @@ namespace GeographicLib {
      * Constructor for a ellipsoid with
      *
      * @param[in] a equatorial radius (meters).
-     * @param[in] f flattening of ellipsoid.  If \e f > 1, set flattening
-     *   to 1/\e f.
+     * @param[in] f flattening of ellipsoid.
      * @param[in] k0 central scale factor.
      * @param[in] extendp use extended domain.
      * @exception GeographicErr if \e a, \e f, or \e k0 is not positive.
diff --git a/include/GeographicLib/Utility.hpp b/include/GeographicLib/Utility.hpp
index a015632..1466886 100644
--- a/include/GeographicLib/Utility.hpp
+++ b/include/GeographicLib/Utility.hpp
@@ -322,24 +322,34 @@ namespace GeographicLib {
      * @param[in] s the string to be converted.
      * @exception GeographicErr is \e s is not readable as a T.
      * @return object of type T
+     *
+     * White space at the beginning and end of \e s is ignored.
      **********************************************************************/
     template<typename T> static T num(const std::string& s) {
       T x;
       std::string errmsg;
+      std::string::size_type
+        beg = 0,
+        end = unsigned(s.size());
+      while (beg < end && isspace(s[beg]))
+        ++beg;
+      while (beg < end && isspace(s[end - 1]))
+        --end;
+      std::string t = s.substr(beg, end-beg);
       do {                     // Executed once (provides the ability to break)
-        std::istringstream is(s);
+        std::istringstream is(t);
         if (!(is >> x)) {
-          errmsg = "Cannot decode " + s;
+          errmsg = "Cannot decode " + t;
           break;
         }
         int pos = int(is.tellg()); // Returns -1 at end of string?
-        if (!(pos < 0 || pos == int(s.size()))) {
-          errmsg = "Extra text " + s.substr(pos) + " at end of " + s;
+        if (!(pos < 0 || pos == int(t.size()))) {
+          errmsg = "Extra text " + t.substr(pos) + " at end of " + t;
           break;
         }
         return x;
       } while (false);
-      x = std::numeric_limits<T>::is_integer ? 0 : nummatch<T>(s);
+      x = std::numeric_limits<T>::is_integer ? 0 : nummatch<T>(t);
       if (x == 0)
         throw GeographicErr(errmsg);
       return x;
@@ -352,6 +362,8 @@ namespace GeographicLib {
      * @param[in] s the string to be matched.
      * @return appropriate special value (±∞, nan) or 0 if none is
      *   found.
+     *
+     * White space is not allowed at the beginning or end of \e s.
      **********************************************************************/
     template<typename T> static T nummatch(const std::string& s) {
       if (s.length() < 3)
@@ -383,6 +395,11 @@ namespace GeographicLib {
      * @param[in] s the string to be converted.
      * @exception GeographicErr is \e s is not readable as a fraction of type T.
      * @return object of type T
+     *
+     * <b>NOTE</b>: The msys shell under Windows converts arguments which look
+     * like pathnames into their Windows equivalents.  As a result the argument
+     * "-1/300" gets mangled into something unrecognizable.  A workaround is to
+     * use a floating point number in the numerator, i.e., "-1.0/300".
      **********************************************************************/
     template<typename T> static T fract(const std::string& s) {
       std::string::size_type delim = s.find('/');
diff --git a/java/direct/pom.xml b/java/direct/pom.xml
index 297706d..2c229f9 100644
--- a/java/direct/pom.xml
+++ b/java/direct/pom.xml
@@ -9,7 +9,7 @@
   <groupId>net.sf.geographiclib.example</groupId>
   <artifactId>Direct</artifactId>
   <name>Direct</name>
-  <version>1.44-SNAPSHOT</version>
+  <version>1.45-SNAPSHOT</version>
 
   <packaging>jar</packaging>
 
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>net.sf.geographiclib</groupId>
       <artifactId>GeographicLib-Java</artifactId>
-      <version>1.44</version>
+      <version>1.45</version>
     </dependency>
   </dependencies>
 
diff --git a/java/inverse/pom.xml b/java/inverse/pom.xml
index aa231a4..78bcceb 100644
--- a/java/inverse/pom.xml
+++ b/java/inverse/pom.xml
@@ -9,7 +9,7 @@
   <groupId>net.sf.geographiclib.example</groupId>
   <artifactId>Inverse</artifactId>
   <name>Inverse</name>
-  <version>1.44-SNAPSHOT</version>
+  <version>1.45-SNAPSHOT</version>
 
   <packaging>jar</packaging>
 
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>net.sf.geographiclib</groupId>
       <artifactId>GeographicLib-Java</artifactId>
-      <version>1.44</version>
+      <version>1.45</version>
     </dependency>
   </dependencies>
 
diff --git a/java/planimeter/pom.xml b/java/planimeter/pom.xml
index 6aee27c..6f66d45 100644
--- a/java/planimeter/pom.xml
+++ b/java/planimeter/pom.xml
@@ -9,7 +9,7 @@
   <groupId>net.sf.geographiclib.example</groupId>
   <artifactId>Planimeter</artifactId>
   <name>Planimeter</name>
-  <version>1.44-SNAPSHOT</version>
+  <version>1.45-SNAPSHOT</version>
 
   <packaging>jar</packaging>
 
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>net.sf.geographiclib</groupId>
       <artifactId>GeographicLib-Java</artifactId>
-      <version>1.44</version>
+      <version>1.45</version>
     </dependency>
   </dependencies>
 
diff --git a/java/pom.xml b/java/pom.xml
index 151aa1b..684aa59 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -8,7 +8,7 @@
 
   <groupId>net.sf.geographiclib</groupId>
   <artifactId>GeographicLib-Java</artifactId>
-  <version>1.44</version>
+  <version>1.45</version>
 
   <packaging>jar</packaging>
 
@@ -167,4 +167,11 @@
     </url>
   </scm>
 
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+    </dependency>
+  </dependencies>
 </project>
diff --git a/java/src/main/java/net/sf/geographiclib/GeoMath.java b/java/src/main/java/net/sf/geographiclib/GeoMath.java
index 60113ca..8c2ac6f 100644
--- a/java/src/main/java/net/sf/geographiclib/GeoMath.java
+++ b/java/src/main/java/net/sf/geographiclib/GeoMath.java
@@ -29,11 +29,6 @@ public class GeoMath {
    * version 1.6 and later, Double.MIN_NORMAL can be used.
    **********************************************************************/
   public static final double min = Math.pow(0.5, 1022);
-  /**
-   * The number of radians in a degree.  In Java version 1.2 and later,
-   * Math.toRadians and Math.toDegrees can be used.
-   **********************************************************************/
-  public static final double degree = Math.PI / 180;
 
   /**
    * Square a number.
@@ -237,7 +232,7 @@ public class GeoMath {
     q = (int)Math.floor(r / 90 + 0.5);
     r -= 90 * q;
     // now abs(r) <= 45
-    r *= degree;
+    r = Math.toRadians(r);
     // Possibly could call the gnu extension sincos
     double s = Math.sin(r), c = Math.cos(r);
     double sinx, cosx;
@@ -271,7 +266,7 @@ public class GeoMath {
     if (Math.abs(y) > Math.abs(x)) { double t; t = x; x = y; y = t; q = 2; }
     if (x < 0) { x = -x; ++q; }
     // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
-    double ang = Math.atan2(y, x) / degree;
+    double ang = Math.toDegrees(Math.atan2(y, x));
     switch (q) {
       // Note that atan2d(-0.0, 1.0) will return -0.  However, we expect that
       // atan2d will not be called with y = -0.  If need be, include
diff --git a/java/src/main/java/net/sf/geographiclib/Geodesic.java b/java/src/main/java/net/sf/geographiclib/Geodesic.java
index 3457745..d684e37 100644
--- a/java/src/main/java/net/sf/geographiclib/Geodesic.java
+++ b/java/src/main/java/net/sf/geographiclib/Geodesic.java
@@ -124,27 +124,27 @@ package net.sf.geographiclib;
  *   <i>lat1</i> = −<i>lat2</i> (with neither point at a pole).  If
  *   <i>azi1</i> = <i>azi2</i>, the geodesic is unique.  Otherwise there are
  *   two geodesics and the second one is obtained by setting [<i>azi1</i>,
- *   <i>azi2</i>] = [<i>azi2</i>, <i>azi1</i>], [<i>M12</i>, <i>M21</i>] =
- *   [<i>M21</i>, <i>M12</i>], <i>S12</i> = −<i>S12</i>.  (This occurs
- *   when the longitude difference is near ±180° for oblate
- *   ellipsoids.)
+ *   <i>azi2</i>] → [<i>azi2</i>, <i>azi1</i>], [<i>M12</i>, <i>M21</i>]
+ *   → [<i>M21</i>, <i>M12</i>], <i>S12</i> → −<i>S12</i>.
+ *   (This occurs when the longitude difference is near ±180° for
+ *   oblate ellipsoids.)
  * <li>
  *   <i>lon2</i> = <i>lon1</i> ± 180° (with neither point at a
  *   pole).  If <i>azi1</i> = 0° or ±180°, the geodesic is
  *   unique.  Otherwise there are two geodesics and the second one is obtained
- *   by setting [ <i>azi1</i>, <i>azi2</i>] = [−<i>azi1</i>,
- *   −<i>azi2</i>], <i>S12</i> = − <i>S12</i>.  (This occurs when
- *   <i>lat2</i> is near −<i>lat1</i> for prolate ellipsoids.)
+ *   by setting [ <i>azi1</i>, <i>azi2</i>] → [−<i>azi1</i>,
+ *   −<i>azi2</i>], <i>S12</i> → − <i>S12</i>.  (This occurs
+ *   when <i>lat2</i> is near −<i>lat1</i> for prolate ellipsoids.)
  * <li>
  *   Points 1 and 2 at opposite poles.  There are infinitely many geodesics
- *   which can be generated by setting [<i>azi1</i>, <i>azi2</i>] =
+ *   which can be generated by setting [<i>azi1</i>, <i>azi2</i>] →
  *   [<i>azi1</i>, <i>azi2</i>] + [<i>d</i>, −<i>d</i>], for arbitrary
  *   <i>d</i>.  (For spheres, this prescription applies when points 1 and 2 are
  *   antipodal.)
  * <li>
  *   s12 = 0 (coincident points).  There are infinitely many geodesics which
- *   can be generated by setting [<i>azi1</i>, <i>azi2</i>] = [<i>azi1</i>,
- *   <i>azi2</i>] + [<i>d</i>, <i>d</i>], for arbitrary <i>d</i>.
+ *   can be generated by setting [<i>azi1</i>, <i>azi2</i>] →
+ *   [<i>azi1</i>, <i>azi2</i>] + [<i>d</i>, <i>d</i>], for arbitrary <i>d</i>.
  * </ul>
  * <p>
  * The calculations are accurate to better than 15 nm (15 nanometers) for the
@@ -243,17 +243,16 @@ public class Geodesic {
    * <p>
    * @param a equatorial radius (meters).
    * @param f flattening of ellipsoid.  Setting <i>f</i> = 0 gives a sphere.
-   *   Negative <i>f</i> gives a prolate ellipsoid.  If <i>f</i> > 1, set
-   *   flattening to 1/<i>f</i>.
+   *   Negative <i>f</i> gives a prolate ellipsoid.
    * @exception GeographicErr if <i>a</i> or (1 − <i>f</i> ) <i>a</i> is
    *   not positive.
    **********************************************************************/
   public Geodesic(double a, double f) {
     _a = a;
-    _f = f <= 1 ? f : 1/f;
+    _f = f <= 1 ? f : 1/f;      // f > 1 behavior is DEPRECATED
     _f1 = 1 - _f;
     _e2 = _f * (2 - _f);
-    _ep2 = _e2 / GeoMath.sq(_f1);       // e2 / (1 - e2)
+    _ep2 = _e2 / GeoMath.sq(_f1); // e2 / (1 - e2)
     _n = _f / ( 2 - _f);
     _b = _a * _f1;
     _c2 = (GeoMath.sq(_a) + GeoMath.sq(_b) *
@@ -313,9 +312,7 @@ public class Geodesic {
    **********************************************************************/
   public GeodesicData Direct(double lat1, double lon1,
                              double azi1, double s12) {
-    return Direct(lat1, lon1, azi1, false, s12,
-                  GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE |
-                  GeodesicMask.AZIMUTH);
+    return Direct(lat1, lon1, azi1, false, s12, GeodesicMask.STANDARD);
   }
   /**
    * Solve the direct geodesic problem where the length of the geodesic is
@@ -368,9 +365,7 @@ public class Geodesic {
    **********************************************************************/
   public GeodesicData ArcDirect(double lat1, double lon1,
                                 double azi1, double a12) {
-    return Direct(lat1, lon1, azi1, true, a12,
-                  GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE |
-                  GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE);
+    return Direct(lat1, lon1, azi1, true, a12, GeodesicMask.STANDARD);
   }
 
   /**
@@ -486,8 +481,7 @@ public class Geodesic {
    **********************************************************************/
   public GeodesicData Inverse(double lat1, double lon1,
                               double lat2, double lon2) {
-    return Inverse(lat1, lon1, lat2, lon2,
-                   GeodesicMask.DISTANCE | GeodesicMask.AZIMUTH);
+    return Inverse(lat1, lon1, lat2, lon2, GeodesicMask.STANDARD);
   }
 
   /**
@@ -552,7 +546,8 @@ public class Geodesic {
     int lonsign = lon12 >= 0 ? 1 : -1;
     lon12 *= lonsign;
     // Swap points so that point with higher (abs) latitude is point 1
-    int swapp = Math.abs(lat1) >= Math.abs(lat2) ? 1 : -1;
+    // If one latitude is a nan, then it becomes lat1.
+    int swapp = Math.abs(lat1) < Math.abs(lat2) ? -1 : 1;
     if (swapp < 0) {
       lonsign *= -1;
       { double t = lat1; lat1 = lat2; lat2 = t; }
@@ -608,7 +603,7 @@ public class Geodesic {
       dn2 = Math.sqrt(1 + _ep2 * GeoMath.sq(sbet2));
 
     double
-      lam12 = lon12 * GeoMath.degree, slam12, clam12;
+      lam12 = Math.toRadians(lon12), slam12, clam12;
     { Pair p = GeoMath.sincosd(lon12); slam12 = p.first; clam12 = p.second; }
 
     double a12, sig12, calp1, salp1, calp2, salp2;
@@ -634,7 +629,7 @@ public class Geodesic {
         ssig2 = sbet2, csig2 = calp2 * cbet2;
 
       // sig12 = sig2 - sig1
-      sig12 = Math.atan2(Math.max(csig1 * ssig2 - ssig1 * csig2, 0.0),
+      sig12 = Math.atan2(Math.max(0.0, csig1 * ssig2 - ssig1 * csig2),
                     csig1 * csig2 + ssig1 * ssig2);
       {
         LengthsV v =
@@ -659,7 +654,7 @@ public class Geodesic {
           sig12 = m12x = s12x = 0;
         m12x *= _b;
         s12x *= _b;
-        a12 = sig12 / GeoMath.degree;
+        a12 = Math.toDegrees(sig12);
       } else
         // m12 < 0, i.e., prolate and too close to anti-podal
         meridian = false;
@@ -704,7 +699,7 @@ public class Geodesic {
         m12x = GeoMath.sq(dnm) * _b * Math.sin(sig12 / dnm);
         if ((outmask & GeodesicMask.GEODESICSCALE) != 0)
           r.M12 = r.M21 = Math.cos(sig12 / dnm);
-        a12 = sig12 / GeoMath.degree;
+        a12 = Math.toDegrees(sig12);
         omg12 = lam12 / (_f1 * dnm);
       } else {
 
@@ -800,7 +795,7 @@ public class Geodesic {
         }
         m12x *= _b;
         s12x *= _b;
-        a12 = sig12 / GeoMath.degree;
+        a12 = Math.toDegrees(sig12);
         omg12 = lam12 - omg12;
       }
     }
@@ -1381,11 +1376,11 @@ public class Geodesic {
     // GeoMath.norm(somg2, comg2); -- don't need to normalize!
 
     // sig12 = sig2 - sig1, limit to [0, pi]
-    w.sig12 = Math.atan2(Math.max(w.csig1 * w.ssig2 - w.ssig1 * w.csig2, 0.0),
+    w.sig12 = Math.atan2(Math.max(0.0, w.csig1 * w.ssig2 - w.ssig1 * w.csig2),
                   w.csig1 * w.csig2 + w.ssig1 * w.ssig2);
 
     // omg12 = omg2 - omg1, limit to [0, pi]
-    omg12 = Math.atan2(Math.max(comg1 * somg2 - somg1 * comg2, 0.0),
+    omg12 = Math.atan2(Math.max(0.0, comg1 * somg2 - somg1 * comg2),
                   comg1 * comg2 + somg1 * somg2);
     double B312, h0;
     double k2 = GeoMath.sq(calp0) * _ep2;
diff --git a/java/src/main/java/net/sf/geographiclib/GeodesicLine.java b/java/src/main/java/net/sf/geographiclib/GeodesicLine.java
index d233858..307b316 100644
--- a/java/src/main/java/net/sf/geographiclib/GeodesicLine.java
+++ b/java/src/main/java/net/sf/geographiclib/GeodesicLine.java
@@ -274,9 +274,7 @@ public class GeodesicLine {
    * |= {@link GeodesicMask#DISTANCE_IN}; otherwise no parameters are set.
    **********************************************************************/
   public GeodesicData Position(double s12) {
-    return Position(false, s12,
-                    GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE |
-                    GeodesicMask.AZIMUTH);
+    return Position(false, s12, GeodesicMask.STANDARD);
   }
   /**
    * Compute the position of point 2 which is a distance <i>s12</i> (meters)
@@ -320,9 +318,7 @@ public class GeodesicLine {
    * |= {@link GeodesicMask#DISTANCE_IN}; otherwise no parameters are set.
    **********************************************************************/
   public GeodesicData ArcPosition(double a12) {
-    return Position(true, a12,
-                    GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE |
-                    GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE);
+    return Position(true, a12, GeodesicMask.STANDARD);
   }
   /**
    * Compute the position of point 2 which is an arc length <i>a12</i>
@@ -405,7 +401,7 @@ public class GeodesicLine {
     if (arcmode) {
       // Interpret s12_a12 as spherical arc length
       r.a12 = s12_a12;
-      sig12 = s12_a12 * GeoMath.degree;
+      sig12 = Math.toRadians(s12_a12);
       { Pair p = GeoMath.sincosd(s12_a12);
         ssig12 = p.first; csig12 = p.second; }
     } else {
@@ -453,7 +449,7 @@ public class GeodesicLine {
         ssig12 = Math.sin(sig12); csig12 = Math.cos(sig12);
         // Update B12 below
       }
-      r.a12 = sig12 / GeoMath.degree;
+      r.a12 = Math.toDegrees(sig12);
     }
 
     double omg12, lam12, lon12;
@@ -495,7 +491,7 @@ public class GeodesicLine {
       lam12 = omg12 + _A3c *
         ( sig12 + (Geodesic.SinCosSeries(true, ssig2, csig2, _C3a)
                    - _B31));
-      lon12 = lam12 / GeoMath.degree;
+      lon12 = Math.toDegrees(lam12);
       r.lon2 = ((outmask & GeodesicMask.LONG_UNROLL) != 0) ? _lon1 + lon12 :
         GeoMath.AngNormalize(r.lon1 + GeoMath.AngNormalize(lon12));
     }
diff --git a/java/src/main/java/net/sf/geographiclib/GeodesicMask.java b/java/src/main/java/net/sf/geographiclib/GeodesicMask.java
index 02c9369..09d96dd 100644
--- a/java/src/main/java/net/sf/geographiclib/GeodesicMask.java
+++ b/java/src/main/java/net/sf/geographiclib/GeodesicMask.java
@@ -56,6 +56,11 @@ public class GeodesicMask {
    **********************************************************************/
   public static final int DISTANCE      = 1<<10 | CAP_C1;
   /**
+   * All of the above, the "standard" output and capabilities.
+   **********************************************************************/
+  public static final int STANDARD      = LATITUDE | LONGITUDE |
+      AZIMUTH | DISTANCE;
+  /**
    * Allow distance <i>s12</i> to be used as <i>input</i> in the direct
    * geodesic problem.
    **********************************************************************/
@@ -73,6 +78,11 @@ public class GeodesicMask {
    **********************************************************************/
   public static final int AREA          = 1<<14 | CAP_C4;
   /**
+   * All capabilities, calculate everything.  (LONG_UNROLL is not included in
+   * this mask.)
+   **********************************************************************/
+  public static final int ALL           = OUT_ALL| CAP_ALL;
+  /**
    * Unroll <i>lon2</i>.
    **********************************************************************/
   public static final int LONG_UNROLL   = 1<<15;
@@ -80,9 +90,4 @@ public class GeodesicMask {
    * For backward compatibility only; use LONG_UNROLL instead.
    **********************************************************************/
   public static final int LONG_NOWRAP   = LONG_UNROLL;
-  /**
-   * All capabilities, calculate everything.  (LONG_UNROLL is not included in
-   * this mask.)
-   **********************************************************************/
-  public static final int ALL           = OUT_ALL| CAP_ALL;
 }
diff --git a/java/src/main/java/net/sf/geographiclib/Gnomonic.java b/java/src/main/java/net/sf/geographiclib/Gnomonic.java
new file mode 100644
index 0000000..28d8e63
--- /dev/null
+++ b/java/src/main/java/net/sf/geographiclib/Gnomonic.java
@@ -0,0 +1,281 @@
+/**
+ * Implementation of the net.sf.geographiclib.Gnomonic class
+ *
+ * Copyright (c) BMW Car IT GmbH (2014) <sebastian.mattheis at bmw-carit.de> and
+ * licensed under the MIT/X11 License. For more information, see
+ * http://geographiclib.sourceforge.net/
+ **********************************************************************/
+package net.sf.geographiclib;
+
+/**
+ * Gnomonic projection.
+ * <p>
+ * <i>Note: Gnomonic.java has been ported to Java from its C++ equivalent
+ * Gnomonic.cpp, authored by C. F. F. Karney and licensed under MIT/X11
+ * license.  The following documentation is mostly the same as for its C++
+ * equivalent, but has been adopted to apply to this Java implementation.</i>
+ * <p>
+ * Gnomonic projection centered at an arbitrary position <i>C</i> on the
+ * ellipsoid. This projection is derived in Section 8 of
+ * <ul>
+ * <li>
+ * C. F. F. Karney, <a href="http://dx.doi.org/10.1007/s00190-012-0578-z">
+ * Algorithms for geodesics</a>, J. Geodesy <b>87</b>, 43–55 (2013);
+ * DOI: <a href="http://dx.doi.org/10.1007/s00190-012-0578-z">
+ * 10.1007/s00190-012-0578-z</a>; addenda:
+ * <a href="http://geographiclib.sf.net/geod-addenda.html">
+ * geod-addenda.html</a>.
+ * </li>
+ * </ul>
+ * <p>
+ * The gnomonic projection of a point <i>P</i> on the ellipsoid is defined as
+ * follows: compute the geodesic line from <i>C</i> to <i>P</i>; compute the
+ * reduced length <i>m12</i>, geodesic scale <i>M12</i>, and ρ =
+ * <i>m12</i>/<i>M12</i>; finally, this gives the coordinates <i>x</i> and
+ * <i>y</i> of <i>P</i> in gnomonic projection with <i>x</i> = ρ sin
+ * <i>azi1</i>; <i>y</i> = ρ cos <i>azi1</i>, where <i>azi1</i> is the
+ * azimuth of the geodesic at <i>C</i>. The method
+ * {@link Gnomonic#Forward(double, double, double, double)} performs the
+ * forward projection and
+ * {@link Gnomonic#Reverse(double, double, double, double)} is the
+ * inverse of the projection. The methods also return the azimuth
+ * <i>azi</i> of the geodesic at <i>P</i> and reciprocal scale
+ * <i>rk</i> in the azimuthal direction. The scale in the radial
+ * direction is 1/<i>rk</i><sup>2</sup>.
+ * <p>
+ * For a sphere, ρ reduces to <i>a</i> tan(<i>s12</i>/<i>a</i>), where
+ * <i>s12</i> is the length of the geodesic from <i>C</i> to <i>P</i>, and the
+ * gnomonic projection has the property that all geodesics appear as straight
+ * lines. For an ellipsoid, this property holds only for geodesics interesting
+ * the centers. However geodesic segments close to the center are approximately
+ * straight.
+ * <p>
+ * Consider a geodesic segment of length <i>l</i>. Let <i>T</i> be the point on
+ * the geodesic (extended if necessary) closest to <i>C</i>, the center of the
+ * projection, and <i>t</i>, be the distance <i>CT</i>. To lowest order, the
+ * maximum deviation (as a true distance) of the corresponding gnomonic line
+ * segment (i.e., with the same end points) from the geodesic is<br>
+ * <br>
+ * (<i>K</i>(<i>T</i>) - <i>K</i>(<i>C</i>)) <i>l</i><sup>2</sup> <i>t</i> / 32.
+ * <br>
+ * <br>
+ * where <i>K</i> is the Gaussian curvature.
+ * <p>
+ * This result applies for any surface. For an ellipsoid of revolution,
+ * consider all geodesics whose end points are within a distance <i>r</i> of
+ * <i>C</i>.  For a given <i>r</i>, the deviation is maximum when the latitude
+ * of <i>C</i> is 45°, when endpoints are a distance <i>r</i> away, and
+ * when their azimuths from the center are ± 45° or ±
+ * 135°. To lowest order in <i>r</i> and the flattening <i>f</i>, the
+ * deviation is <i>f</i> (<i>r</i>/2<i>a</i>)<sup>3</sup> <i>r</i>.
+ * <p>
+ * The conversions all take place using a Geodesic object (by default
+ * Geodesic::WGS84). For more information on geodesics see \ref geodesic.
+ * <p>
+ * <b>CAUTION:</b> The definition of this projection for a sphere is standard.
+ * However, there is no standard for how it should be extended to an ellipsoid.
+ * The choices are:
+ * <ul>
+ * <li>
+ * Declare that the projection is undefined for an ellipsoid.
+ * </li>
+ * <li>
+ * Project to a tangent plane from the center of the ellipsoid. This causes
+ * great ellipses to appear as straight lines in the projection; i.e., it
+ * generalizes the spherical great circle to a great ellipse. This was proposed
+ * by independently by Bowring and Williams in 1997.
+ * </li>
+ * <li>
+ * Project to the conformal sphere with the constant of integration chosen so
+ * that the values of the latitude match for the center point and perform a
+ * central projection onto the plane tangent to the conformal sphere at the
+ * center point. This causes normal sections through the center point to appear
+ * as straight lines in the projection; i.e., it generalizes the spherical
+ * great circle to a normal section. This was proposed by I. G. Letoval'tsev,
+ * Generalization of the gnomonic projection for a spheroid and the principal
+ * geodetic problems involved in the alignment of surface routes, Geodesy and
+ * Aerophotography (5), 271–274 (1963).
+ * </li>
+ * <li>
+ * The projection given here. This causes geodesics close to the center point
+ * to appear as straight lines in the projection; i.e., it generalizes the
+ * spherical great circle to a geodesic.
+ * </li>
+ * </ul>
+ * <p>
+ * Example of use:
+ *
+ * <pre>
+ * // Example of using the Gnomonic.java class
+ * import net.sf.geographiclib.Geodesic;
+ * import net.sf.geographiclib.Gnomonic;
+ * import net.sf.geographiclib.GnomonicData;
+ * public class ExampleGnomonic {
+ *   public static void main(String[] args) {
+ *     Geodesic geod = Geodesic.WGS84;
+ *     double lat0 = 48 + 50 / 60.0, lon0 = 2 + 20 / 60.0; // Paris
+ *     Gnomonic gnom = new Gnomonic(geod);
+ *     {
+ *       // Sample forward calculation
+ *       double lat = 50.9, lon = 1.8; // Calais
+ *       GnomonicData proj = gnom.Forward(lat0, lon0, lat, lon);
+ *       System.out.println(proj.x + " " + proj.y);
+ *     }
+ *     {
+ *       // Sample reverse calculation
+ *       double x = -38e3, y = 230e3;
+ *       GnomonicData proj = gnom.Reverse(lat0, lon0, x, y);
+ *       System.out.println(proj.lat + " " + proj.lon);
+ *     }
+ *   }
+ * }
+ * </pre>
+ */
+
+public class Gnomonic {
+  private static final double eps_ = 0.01 * Math.sqrt(GeoMath.epsilon);
+  private static final int numit_ = 10;
+  private Geodesic _earth;
+  private double _a, _f;
+
+  /**
+   * Constructor for Gnomonic.
+   * <p>
+   * @param earth the {@link Geodesic} object to use for geodesic
+   *   calculations. By default the WGS84 ellipsoid should be used.
+   */
+  public Gnomonic(Geodesic earth) {
+    _earth = earth;
+    _a = _earth.MajorRadius();
+    _f = _earth.Flattening();
+  }
+
+  /**
+   * Forward projection, from geographic to gnomonic.
+   * <p>
+   * @param lat0 latitude of center point of projection (degrees).
+   * @param lon0 longitude of center point of projection (degrees).
+   * @param lat latitude of point (degrees).
+   * @param lon longitude of point (degrees).
+   * @return {@link GnomonicData} object with the following fields:
+   *   <i>lat0</i>, <i>lon0</i>, <i>lat</i>, <i>lon</i>, <i>x</i>, <i>y</i>,
+   *   <i>azi</i>, <i>rk</i>.
+   * <p>
+   * <i>lat0</i> and <i>lat</i> should be in the range [−90°,
+   * 90°] and <i>lon0</i> and <i>lon</i> should be in the range
+   * [−540°, 540°). The scale of the projection is
+   * 1/<i>rk<sup>2</sup></i> in the "radial" direction, <i>azi</i> clockwise
+   * from true north, and is 1/<i>rk</i> in the direction perpendicular to
+   * this. If the point lies "over the horizon", i.e., if <i>rk</i> ≤ 0,
+   * then NaNs are returned for <i>x</i> and <i>y</i> (the correct values are
+   * returned for <i>azi</i> and <i>rk</i>). A call to Forward followed by a
+   * call to Reverse will return the original (<i>lat</i>, <i>lon</i>) (to
+   * within roundoff) provided the point in not over the horizon.
+   */
+  public GnomonicData Forward(double lat0, double lon0, double lat, double lon)
+  {
+    GeodesicData inv =
+      _earth.Inverse(lat0, lon0, lat, lon, GeodesicMask.AZIMUTH
+                     | GeodesicMask.GEODESICSCALE | GeodesicMask.REDUCEDLENGTH);
+    GnomonicData fwd =
+      new GnomonicData(lat0, lon0, lat, lon, Double.NaN, Double.NaN,
+                       inv.azi2, inv.M12);
+
+    if (inv.M12 > 0) {
+      double rho = inv.m12 / inv.M12;
+      Pair p = GeoMath.sincosd(inv.azi1);
+      fwd.x = rho * p.first;
+      fwd.y = rho * p.second;
+    }
+
+    return fwd;
+  }
+
+  /**
+   * Reverse projection, from gnomonic to geographic.
+   * <p>
+   * @param lat0 latitude of center point of projection (degrees).
+   * @param lon0 longitude of center point of projection (degrees).
+   * @param x easting of point (meters).
+   * @param y northing of point (meters).
+   * @return {@link GnomonicData} object with the following fields:
+   *   <i>lat0</i>, <i>lon0</i>, <i>lat</i>, <i>lon</i>, <i>x</i>, <i>y</i>,
+   *   <i>azi</i>, <i>rk</i>.
+   * <p>
+   * <i>lat0</i> should be in the range [−90°, 90°] and
+   * <i>lon0</i> should be in the range [−540°, 540°).
+   * <i>lat</i> will be in the range [−90°, 90°] and <i>lon</i>
+   * will be in the range [−180°, 180°). The scale of the
+   * projection is 1/<i>rk<sup>2</sup></i> in the "radial" direction,
+   * <i>azi</i> clockwise from true north, and is 1/<i>rk</i> in the direction
+   * perpendicular to this. Even though all inputs should return a valid
+   * <i>lat</i> and <i>lon</i>, it's possible that the procedure fails to
+   * converge for very large <i>x</i> or <i>y</i>; in this case NaNs are
+   * returned for all the output arguments. A call to Reverse followed by a
+   * call to Forward will return the original (<i>x</i>, <i>y</i>) (to
+   * roundoff).
+   */
+  public GnomonicData Reverse(double lat0, double lon0, double x, double y) {
+    GnomonicData rev =
+      new GnomonicData(lat0, lon0, Double.NaN, Double.NaN, x, y, Double.NaN,
+                       Double.NaN);
+
+    double azi0 = GeoMath.atan2d(x, y);
+    double rho = Math.hypot(x, y);
+    double s = _a * Math.atan(rho / _a);
+    boolean little = rho <= _a;
+
+    if (!little)
+      rho = 1 / rho;
+
+    GeodesicLine line =
+      _earth.Line(lat0, lon0, azi0, GeodesicMask.LATITUDE
+                  | GeodesicMask.LONGITUDE | GeodesicMask.AZIMUTH
+                  | GeodesicMask.DISTANCE_IN | GeodesicMask.REDUCEDLENGTH
+                  | GeodesicMask.GEODESICSCALE);
+
+    int count = numit_, trip = 0;
+    GeodesicData pos = null;
+
+    while (count-- > 0) {
+      pos =
+        line.Position(s, GeodesicMask.LONGITUDE | GeodesicMask.LATITUDE
+                      | GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE_IN
+                      | GeodesicMask.REDUCEDLENGTH
+                      | GeodesicMask.GEODESICSCALE);
+
+      if (trip > 0)
+        break;
+
+      double ds =
+        little ? ((pos.m12 / pos.M12) - rho) * pos.M12 * pos.M12
+        : (rho - (pos.M12 / pos.m12)) * pos.m12 * pos.m12;
+      s -= ds;
+
+      if (Math.abs(ds) <= eps_ * _a)
+        trip++;
+    }
+
+    if (trip == 0)
+      return rev;
+
+    rev.lat = pos.lat2;
+    rev.lon = pos.lon2;
+    rev.azi = pos.azi2;
+    rev.rk = pos.M12;
+
+    return rev;
+  }
+
+  /**
+   * @return <i>a</i> the equatorial radius of the ellipsoid (meters).  This is
+   *   the value inherited from the Geodesic object used in the constructor.
+   **********************************************************************/
+  public double MajorRadius() { return _a; }
+
+  /**
+   * @return <i>f</i> the  flattening of the ellipsoid.  This is
+   *   the value inherited from the Geodesic object used in the constructor.
+   **********************************************************************/
+  public double Flattening() { return _f; }
+}
diff --git a/java/src/main/java/net/sf/geographiclib/GnomonicData.java b/java/src/main/java/net/sf/geographiclib/GnomonicData.java
new file mode 100644
index 0000000..75fcccf
--- /dev/null
+++ b/java/src/main/java/net/sf/geographiclib/GnomonicData.java
@@ -0,0 +1,98 @@
+/**
+ * Implementation of the net.sf.geographiclib.GnomonicData class
+ *
+ * Copyright (c) BMW Car IT GmbH (2014) <sebastian.mattheis at bmw-carit.de> and
+ * licensed under the MIT/X11 License. For more information, see
+ * http://geographiclib.sourceforge.net/
+ **********************************************************************/
+package net.sf.geographiclib;
+
+/**
+ * The results of gnomonic projection.
+ * <p>
+
+ * This is used to return the results for a gnomonic projection of a point
+ * (<i>lat</i>, <i>lon</i>) given a center point of projection (<i>lat0</i>,
+ * <i>lon0</i>). The returned GnomonicData objects always include the
+ * parameters provided to
+ * {@link Gnomonic#Forward(double, double, double, double) Gnomonic.Forward}
+ * and
+ * {@link Gnomonic#Reverse(double, double, double, double) Gnomonic.Reverse}
+ * and it always includes the fields <i>x</i>, <i>y</i>, <i>azi</i>. and
+ * <i>rk</i>.
+ **********************************************************************/
+public class GnomonicData {
+  /**
+   * latitude of center point of projection (degrees).
+   **********************************************************************/
+  public double lat0;
+  /**
+   * longitude of center point of projection (degrees).
+   **********************************************************************/
+  public double lon0;
+  /**
+   * latitude of point (degrees).
+   **********************************************************************/
+  public double lat;
+  /**
+   * longitude of point (degrees).
+   **********************************************************************/
+  public double lon;
+  /**
+   * easting of point (meters).
+   **********************************************************************/
+  public double x;
+  /**
+   * northing of point (meters).
+   **********************************************************************/
+  public double y;
+  /**
+   * azimuth of geodesic at point (degrees).
+   **********************************************************************/
+  public double azi;
+  /**
+   * reciprocal of azimuthal scale at point.
+   **********************************************************************/
+  public double rk;
+
+  /**
+   * Initialize all the fields to Double.NaN.
+   **********************************************************************/
+  public GnomonicData() {
+    lat0 = lon0 = lat = lon = x = y = azi = rk = Double.NaN;
+  }
+
+  /**
+   * Constructor initializing all the fields for gnomonic projection of a point
+   * (<i>lat</i>, <i>lon</i>) given a center point of projection (<i>lat0</i>,
+   * <i>lon0</i>).
+   * <p>
+   * @param lat0
+   *          latitude of center point of projection (degrees).
+   * @param lon0
+   *          longitude of center point of projection (degrees).
+   * @param lat
+   *          latitude of point (degrees).
+   * @param lon
+   *          longitude of point (degrees).
+   * @param x
+   *          easting of point (meters).
+   * @param y
+   *          northing of point (meters).
+   * @param azi
+   *          azimuth of geodesic at point (degrees).
+   * @param rk
+   *          reciprocal of azimuthal scale at point.
+   */
+  public GnomonicData(double lat0, double lon0, double lat, double lon,
+      double x, double y, double azi, double rk) {
+    this.lat0 = lat0;
+    this.lon0 = lon0;
+    this.lat = lat;
+    this.lon = lon;
+    this.x = x;
+    this.y = y;
+    this.azi = azi;
+    this.rk = rk;
+  }
+}
diff --git a/java/src/main/java/net/sf/geographiclib/PolygonArea.java b/java/src/main/java/net/sf/geographiclib/PolygonArea.java
index 019b3bd..8938bf1 100644
--- a/java/src/main/java/net/sf/geographiclib/PolygonArea.java
+++ b/java/src/main/java/net/sf/geographiclib/PolygonArea.java
@@ -194,6 +194,8 @@ public class PolygonArea {
    *   of the polygon or the length of the polyline (meters), and <i>area</i>
    *   is the area of the polygon (meters<sup>2</sup>) or Double.NaN of
    *   <i>polyline</i> is true in the constructor.
+   * <p>
+   * More points can be added to the polygon after this call.
    **********************************************************************/
   public PolygonResult Compute(boolean reverse, boolean sign) {
     if (_num < 2)
diff --git a/java/src/main/java/net/sf/geographiclib/package-info.java b/java/src/main/java/net/sf/geographiclib/package-info.java
index 4ad3f2c..5b0aa6b 100644
--- a/java/src/main/java/net/sf/geographiclib/package-info.java
+++ b/java/src/main/java/net/sf/geographiclib/package-info.java
@@ -1,14 +1,14 @@
 /**
  * <h1>Geodesic routines from GeographicLib implemented in Java</h1>
  * @author Charles F. F. Karney (charles at karney.com)
- * @version 1.44
+ * @version 1.45
  *
  * <h2>Abstract</h2>
  * <p>
  * GeographicLib-Java is a Java implementation of the geodesic algorithms from
  * <a href="http://geographiclib.sf.net">GeographicLib</a>.  This is a
  * self-contained library which makes it easy to do geodesic computations for
- * an ellipsoid of revolution in a Java program.  It requires Java version 1.1
+ * an ellipsoid of revolution in a Java program.  It requires Java version 1.2
  * or later.
  *
  * <h2>Downloading</h2>
@@ -19,15 +19,15 @@
  * GeographicLib-Java is part of GeographicLib which available for download at
  * <ul>
  * <li>
- *   <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.tar.gz">
- *   GeographicLib-1.44.tar.gz</a>
+ *   <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.tar.gz">
+ *   GeographicLib-1.45.tar.gz</a>
  * <li>
- *   <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.44.zip">
- *   GeographicLib-1.44.zip</a>
+ *   <a href="https://sf.net/projects/geographiclib/files/distrib/GeographicLib-1.45.zip">
+ *   GeographicLib-1.45.zip</a>
  * </ul>
  * <p>
  * as either a compressed tar file (tar.gz) or a zip file.  After unpacking
- * the source, the Java library can be found in GeographicLib-1.44/java.  (This
+ * the source, the Java library can be found in GeographicLib-1.45/java.  (This
  * library is completely independent from the rest of GeodegraphicLib.)  The
  * library consists of the files in the src/main/java/net/sf/geographiclib
  * subdirectory.
@@ -40,7 +40,7 @@
  *   <dependency>
  *     <groupId>net.sf.geographiclib</groupId>
  *     <artifactId>GeographicLib-Java</artifactId>
- *     <version>1.44</version>
+ *     <version>1.45</version>
  *   </dependency> }</pre>
  * in your {@code pom.xml}.
  *
@@ -105,9 +105,9 @@
  * some additional packages to your local repository.)  Then compile and run
  * Inverse.java with <pre>
  * cd inverse/src/main/java
- * javac -cp .:../../../../target/GeographicLib-Java-1.44.jar Inverse.java
+ * javac -cp .:../../../../target/GeographicLib-Java-1.45.jar Inverse.java
  * echo -30 0 29.5 179.5 |
- *   java -cp .:../../../../target/GeographicLib-Java-1.44.jar Inverse </pre>
+ *   java -cp .:../../../../target/GeographicLib-Java-1.45.jar Inverse </pre>
  *
  * <h3>Using maven to build and run {@code Inverse.java}</h3>
  * The sample code includes a {@code pom.xml} which specifies
@@ -209,7 +209,22 @@
  * <p>
  * <ul>
  * <li>
- *   <a href="http://geographiclib.sf.net/1.44">Version 1.44</a>
+ *   <a href="http://geographiclib.sf.net/1.45/java">Version 1.45</a>
+ *   (released 2015-09-30)
+ * <ul>
+ * <li>
+ *   The solution of the inverse problem now correctly returns NaNs if
+ *   one of the latitudes is a NaN.
+ * <li>
+ *   Add implementation of the ellipsoidal
+ *   {@link net.sf.geographiclib.Gnomonic} (courtesy of Sebastian Mattheis).
+ * <li>
+ *   Math.toRadians and Math.toDegrees are used instead of GeoMath.degree
+ *   (which is now removed).  This requires Java 1.2 or later (released
+ *   1998-12).
+ * </ul>
+ * <li>
+ *   <a href="http://geographiclib.sf.net/1.44/java">Version 1.44</a>
  *   (released 2015-08-14)
  * <ul>
  * <li>
diff --git a/java/src/test/java/net/sf/geographiclib/GeodesicTest.java b/java/src/test/java/net/sf/geographiclib/GeodesicTest.java
new file mode 100644
index 0000000..109a7e7
--- /dev/null
+++ b/java/src/test/java/net/sf/geographiclib/GeodesicTest.java
@@ -0,0 +1,509 @@
+package net.sf.geographiclib.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import net.sf.geographiclib.*;
+
+public class GeodesicTest {
+
+  private static boolean isNaN(double x) { return x != x; }
+  private static final PolygonArea polygon =
+    new PolygonArea(Geodesic.WGS84, false);
+  private static final PolygonArea polyline =
+    new PolygonArea(Geodesic.WGS84, true);
+
+  private static PolygonResult Planimeter(double points[][]) {
+    polygon.Clear();
+    for (int i = 0; i < points.length; ++i) {
+      polygon.AddPoint(points[i][0], points[i][1]);
+    }
+    return polygon.Compute(false, true);
+  }
+
+  private static PolygonResult PolyLength(double points[][]) {
+    polyline.Clear();
+    for (int i = 0; i < points.length; ++i) {
+      polyline.AddPoint(points[i][0], points[i][1]);
+    }
+    return polyline.Compute(false, true);
+  }
+
+  private static final double testcases[][] = {
+    {35.60777, -139.44815, 111.098748429560326,
+     -11.17491, -69.95921, 129.289270889708762,
+     8935244.5604818305, 80.50729714281974, 6273170.2055303837,
+     0.16606318447386067, 0.16479116945612937, 12841384694976.432},
+    {55.52454, 106.05087, 22.020059880982801,
+     77.03196, 197.18234, 109.112041110671519,
+     4105086.1713924406, 36.892740690445894, 3828869.3344387607,
+     0.80076349608092607, 0.80101006984201008, 61674961290615.615},
+    {-21.97856, 142.59065, -32.44456876433189,
+     41.84138, 98.56635, -41.84359951440466,
+     8394328.894657671, 75.62930491011522, 6161154.5773110616,
+     0.24816339233950381, 0.24930251203627892, -6637997720646.717},
+    {-66.99028, 112.2363, 173.73491240878403,
+     -12.70631, 285.90344, 2.512956620913668,
+     11150344.2312080241, 100.278634181155759, 6289939.5670446687,
+     -0.17199490274700385, -0.17722569526345708, -121287239862139.744},
+    {-17.42761, 173.34268, -159.033557661192928,
+     -15.84784, 5.93557, -20.787484651536988,
+     16076603.1631180673, 144.640108810286253, 3732902.1583877189,
+     -0.81273638700070476, -0.81299800519154474, 97825992354058.708},
+    {32.84994, 48.28919, 150.492927788121982,
+     -56.28556, 202.29132, 48.113449399816759,
+     16727068.9438164461, 150.565799985466607, 3147838.1910180939,
+     -0.87334918086923126, -0.86505036767110637, -72445258525585.010},
+    {6.96833, 52.74123, 92.581585386317712,
+     -7.39675, 206.17291, 90.721692165923907,
+     17102477.2496958388, 154.147366239113561, 2772035.6169917581,
+     -0.89991282520302447, -0.89986892177110739, -1311796973197.995},
+    {-50.56724, -16.30485, -105.439679907590164,
+     -33.56571, -94.97412, -47.348547835650331,
+     6455670.5118668696, 58.083719495371259, 5409150.7979815838,
+     0.53053508035997263, 0.52988722644436602, 41071447902810.047},
+    {-58.93002, -8.90775, 140.965397902500679,
+     -8.91104, 133.13503, 19.255429433416599,
+     11756066.0219864627, 105.755691241406877, 6151101.2270708536,
+     -0.26548622269867183, -0.27068483874510741, -86143460552774.735},
+    {-68.82867, -74.28391, 93.774347763114881,
+     -50.63005, -8.36685, 34.65564085411343,
+     3956936.926063544, 35.572254987389284, 3708890.9544062657,
+     0.81443963736383502, 0.81420859815358342, -41845309450093.787},
+    {-10.62672, -32.0898, -86.426713286747751,
+     5.883, -134.31681, -80.473780971034875,
+     11470869.3864563009, 103.387395634504061, 6184411.6622659713,
+     -0.23138683500430237, -0.23155097622286792, 4198803992123.548},
+    {-21.76221, 166.90563, 29.319421206936428,
+     48.72884, 213.97627, 43.508671946410168,
+     9098627.3986554915, 81.963476716121964, 6299240.9166992283,
+     0.13965943368590333, 0.14152969707656796, 10024709850277.476},
+    {-19.79938, -174.47484, 71.167275780171533,
+     -11.99349, -154.35109, 65.589099775199228,
+     2319004.8601169389, 20.896611684802389, 2267960.8703918325,
+     0.93427001867125849, 0.93424887135032789, -3935477535005.785},
+    {-11.95887, -116.94513, 92.712619830452549,
+     4.57352, 7.16501, 78.64960934409585,
+     13834722.5801401374, 124.688684161089762, 5228093.177931598,
+     -0.56879356755666463, -0.56918731952397221, -9919582785894.853},
+    {-87.85331, 85.66836, -65.120313040242748,
+     66.48646, 16.09921, -4.888658719272296,
+     17286615.3147144645, 155.58592449699137, 2635887.4729110181,
+     -0.90697975771398578, -0.91095608883042767, 42667211366919.534},
+    {1.74708, 128.32011, -101.584843631173858,
+     -11.16617, 11.87109, -86.325793296437476,
+     12942901.1241347408, 116.650512484301857, 5682744.8413270572,
+     -0.44857868222697644, -0.44824490340007729, 10763055294345.653},
+    {-25.72959, -144.90758, -153.647468693117198,
+     -57.70581, -269.17879, -48.343983158876487,
+     9413446.7452453107, 84.664533838404295, 6356176.6898881281,
+     0.09492245755254703, 0.09737058264766572, 74515122850712.444},
+    {-41.22777, 122.32875, 14.285113402275739,
+     -7.57291, 130.37946, 10.805303085187369,
+     3812686.035106021, 34.34330804743883, 3588703.8812128856,
+     0.82605222593217889, 0.82572158200920196, -2456961531057.857},
+    {11.01307, 138.25278, 79.43682622782374,
+     6.62726, 247.05981, 103.708090215522657,
+     11911190.819018408, 107.341669954114577, 6070904.722786735,
+     -0.29767608923657404, -0.29785143390252321, 17121631423099.696},
+    {-29.47124, 95.14681, -163.779130441688382,
+     -27.46601, -69.15955, -15.909335945554969,
+     13487015.8381145492, 121.294026715742277, 5481428.9945736388,
+     -0.51527225545373252, -0.51556587964721788, 104679964020340.318}};
+
+  @Test
+  public void InverseCheck() {
+    for (int i = 0; i < testcases.length; ++i) {
+      double
+        lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
+        lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
+        s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
+        M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
+      GeodesicData inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2,
+                                                GeodesicMask.ALL |
+                                                GeodesicMask.LONG_UNROLL);
+      assertEquals(lon2, inv.lon2, 1e-13);
+      assertEquals(azi1, inv.azi1, 1e-13);
+      assertEquals(azi2, inv.azi2, 1e-13);
+      assertEquals(s12, inv.s12, 1e-8);
+      assertEquals(a12, inv.a12, 1e-13);
+      assertEquals(m12, inv.m12, 1e-8);
+      assertEquals(M12, inv.M12, 1e-15);
+      assertEquals(M21, inv.M21, 1e-15);
+      assertEquals(S12, inv.S12, 0.1);
+    }
+  }
+
+  @Test
+  public void DirectCheck() {
+    for (int i = 0; i < testcases.length; ++i) {
+      double
+        lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
+        lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
+        s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
+        M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
+      GeodesicData dir = Geodesic.WGS84.Direct(lat1, lon1, azi1, s12,
+                                               GeodesicMask.ALL |
+                                               GeodesicMask.LONG_UNROLL);
+      assertEquals(lat2, dir.lat2, 1e-13);
+      assertEquals(lon2, dir.lon2, 1e-13);
+      assertEquals(azi2, dir.azi2, 1e-13);
+      assertEquals(a12, dir.a12, 1e-13);
+      assertEquals(m12, dir.m12, 1e-8);
+      assertEquals(M12, dir.M12, 1e-15);
+      assertEquals(M21, dir.M21, 1e-15);
+      assertEquals(S12, dir.S12, 0.1);
+    }
+  }
+
+  @Test
+  public void ArcDirectCheck() {
+    for (int i = 0; i < testcases.length; ++i) {
+      double
+        lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
+        lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
+        s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
+        M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
+      GeodesicData dir = Geodesic.WGS84.ArcDirect(lat1, lon1, azi1, a12,
+                                               GeodesicMask.ALL |
+                                               GeodesicMask.LONG_UNROLL);
+      assertEquals(lat2, dir.lat2, 1e-13);
+      assertEquals(lon2, dir.lon2, 1e-13);
+      assertEquals(azi2, dir.azi2, 1e-13);
+      assertEquals(s12, dir.s12, 1e-8);
+      assertEquals(m12, dir.m12, 1e-8);
+      assertEquals(M12, dir.M12, 1e-15);
+      assertEquals(M21, dir.M21, 1e-15);
+      assertEquals(S12, dir.S12, 0.1);
+    }
+  }
+
+  @Test
+  public void GeodSolve0() {
+    GeodesicData inv = Geodesic.WGS84.Inverse(40.6, -73.8,
+                                              49.01666667, 2.55);
+    assertEquals(inv.azi1, 53.47022, 0.5e-5);
+    assertEquals(inv.azi2, 111.59367, 0.5e-5);
+    assertEquals(inv.s12, 5853226, 0.5);
+  }
+
+  @Test
+  public void GeodSolve1() {
+    GeodesicData dir = Geodesic.WGS84.Direct(40.63972222, -73.77888889,
+                                             53.5, 5850e3);
+    assertEquals(dir.lat2, 49.01467, 0.5e-5);
+    assertEquals(dir.lon2, 2.56106, 0.5e-5);
+    assertEquals(dir.azi2, 111.62947, 0.5e-5);
+  }
+
+  @Test
+  public void GeodSolve2() {
+    // Check fix for antipodal prolate bug found 2010-09-04
+    Geodesic geod = new Geodesic(6.4e6, -1/150.0);
+    GeodesicData inv = geod.Inverse(0.07476, 0, -0.07476, 180);
+    assertEquals(inv.azi1, 90.00078, 0.5e-5);
+    assertEquals(inv.azi2, 90.00078, 0.5e-5);
+    assertEquals(inv.s12, 20106193, 0.5);
+    inv = geod.Inverse(0.1, 0, -0.1, 180);
+    assertEquals(inv.azi1, 90.00105, 0.5e-5);
+    assertEquals(inv.azi2, 90.00105, 0.5e-5);
+    assertEquals(inv.s12, 20106193, 0.5);
+  }
+
+  @Test
+  public void GeodSolve4() {
+    // Check fix for short line bug found 2010-05-21
+    GeodesicData inv = Geodesic.WGS84.Inverse(36.493349428792, 0,
+                                              36.49334942879201, .0000008);
+    assertEquals(inv.s12, 0.072, 0.5e-3);
+  }
+
+  @Test
+  public void GeodSolve5() {
+    // Check fix for point2=pole bug found 2010-05-03
+    GeodesicData dir = Geodesic.WGS84.Direct(0.01777745589997, 30, 0, 10e6);
+    assertEquals(dir.lat2, 90, 0.5e-5);
+    if (dir.lon2 < 0) {
+      assertEquals(dir.lon2, -150, 0.5e-5);
+      assertEquals(dir.azi2, -180, 0.5e-5);
+    } else {
+      assertEquals(dir.lon2, 30, 0.5e-5);
+      assertEquals(dir.azi2, 0, 0.5e-5);
+    }
+  }
+
+  @Test
+  public void GeodSolve6() {
+    // Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
+    // x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1).
+    GeodesicData inv =
+      Geodesic.WGS84.Inverse(88.202499451857, 0,
+                             -88.202499451857, 179.981022032992859592);
+    assertEquals(inv.s12, 20003898.214, 0.5e-3);
+    inv = Geodesic.WGS84.Inverse(89.262080389218, 0,
+                                 -89.262080389218, 179.992207982775375662);
+    assertEquals(inv.s12, 20003925.854, 0.5e-3);
+    inv = Geodesic.WGS84.Inverse(89.333123580033, 0,
+                                 -89.333123580032997687, 179.99295812360148422);
+    assertEquals(inv.s12, 20003926.881, 0.5e-3);
+  }
+
+  @Test
+  public void GeodSolve9() {
+    // Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3)
+    GeodesicData inv =
+      Geodesic.WGS84.Inverse(56.320923501171, 0,
+                             -56.320923501171, 179.664747671772880215);
+    assertEquals(inv.s12, 19993558.287, 0.5e-3);
+  }
+
+  @Test
+  public void GeodSolve10() {
+    // Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
+    // 10 rel + debug)
+    GeodesicData inv =
+      Geodesic.WGS84.Inverse(52.784459512564, 0,
+                             -52.784459512563990912, 179.634407464943777557);
+    assertEquals(inv.s12, 19991596.095, 0.5e-3);
+  }
+
+  @Test
+  public void GeodSolve11() {
+    // Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
+    // 10 rel + debug)
+    GeodesicData inv =
+      Geodesic.WGS84.Inverse(48.522876735459, 0,
+                             -48.52287673545898293, 179.599720456223079643);
+    assertEquals(inv.s12, 19989144.774, 0.5e-3);
+  }
+
+  @Test
+  public void GeodSolve12() {
+    // Check fix for inverse geodesics on extreme prolate/oblate
+    // ellipsoids Reported 2012-08-29 Stefan Guenther
+    // <stefan.gunther at embl.de>; fixed 2012-10-07
+    Geodesic geod = new Geodesic(89.8, -1.83);
+    GeodesicData inv = geod.Inverse(0, 0, -10, 160);
+    assertEquals(inv.azi1, 120.27, 1e-2);
+    assertEquals(inv.azi2, 105.15, 1e-2);
+    assertEquals(inv.s12, 266.7, 1e-1);
+  }
+
+  @Test
+  public void GeodSolve14() {
+    // Check fix for inverse ignoring lon12 = nan
+    GeodesicData inv = Geodesic.WGS84.Inverse(0, 0, 1, Double.NaN);
+    assertTrue(isNaN(inv.azi1));
+    assertTrue(isNaN(inv.azi2));
+    assertTrue(isNaN(inv.s12));
+  }
+
+  @Test
+  public void GeodSolve15() {
+    // Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+    // checks that this is fixed.
+    Geodesic geod = new Geodesic(6.4e6, -1/150.0);
+    GeodesicData dir = geod.Direct(1, 2, 3, 4, GeodesicMask.AREA);
+    assertEquals(dir.S12, 23700, 0.5);
+  }
+
+  @Test
+  public void GeodSolve17() {
+    // Check fix for LONG_UNROLL bug found on 2015-05-07
+    GeodesicData dir =
+      Geodesic.WGS84.Direct(40, -75, -10, 2e7,
+                            GeodesicMask.STANDARD | GeodesicMask.LONG_UNROLL);
+    assertEquals(dir.lat2, -39, 1);
+    assertEquals(dir.lon2, -254, 1);
+    assertEquals(dir.azi2, -170, 1);
+    GeodesicLine line = Geodesic.WGS84.Line(40, -75, -10);
+    dir = line.Position(2e7, GeodesicMask.STANDARD | GeodesicMask.LONG_UNROLL);
+    assertEquals(dir.lat2, -39, 1);
+    assertEquals(dir.lon2, -254, 1);
+    assertEquals(dir.azi2, -170, 1);
+    dir = Geodesic.WGS84.Direct(40, -75, -10, 2e7);
+    assertEquals(dir.lat2, -39, 1);
+    assertEquals(dir.lon2, 105, 1);
+    assertEquals(dir.azi2, -170, 1);
+    dir = line.Position(2e7);
+    assertEquals(dir.lat2, -39, 1);
+    assertEquals(dir.lon2, 105, 1);
+    assertEquals(dir.azi2, -170, 1);
+  }
+
+  @Test
+  public void GeodSolve26() {
+    // Check 0/0 problem with area calculation on sphere 2015-09-08
+    Geodesic geod = new Geodesic(6.4e6, 0);
+    GeodesicData inv = geod.Inverse(1, 2, 3, 4, GeodesicMask.AREA);
+    assertEquals(inv.S12, 49911046115.0, 0.5);
+  }
+
+  @Test
+  public void GeodSolve28() {
+    // Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+    // Java implementation fixed on 2015-05-19).
+    Geodesic geod = new Geodesic(6.4e6, 0.1);
+    GeodesicData dir = geod.Direct(1, 2, 10, 5e6);
+    assertEquals(dir.a12, 48.55570690, 0.5e-8);
+  }
+
+  @Test
+  public void GeodSolve29() {
+    // Check longitude unrolling with inverse calculation 2015-09-16
+    GeodesicData dir = Geodesic.WGS84.Inverse(0, 539, 0, 181);
+    assertEquals(dir.lon1, 179, 1e-10);
+    assertEquals(dir.lon2, -179, 1e-10);
+    assertEquals(dir.s12, 222639, 0.5);
+    dir = Geodesic.WGS84.Inverse(0, 539, 0, 181,
+                                 GeodesicMask.STANDARD |
+                                 GeodesicMask.LONG_UNROLL);
+    assertEquals(dir.lon1, 539, 1e-10);
+    assertEquals(dir.lon2, 541, 1e-10);
+    assertEquals(dir.s12, 222639, 0.5);
+  }
+
+  @Test
+  public void GeodSolve33() {
+    // Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+    // sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+    // fmod(-0.0, 360.0) = +0.0.
+    GeodesicData inv = Geodesic.WGS84.Inverse(0, 0, 0, 179);
+    assertEquals(inv.azi1, 90.00000, 0.5e-5);
+    assertEquals(inv.azi2, 90.00000, 0.5e-5);
+    assertEquals(inv.s12, 19926189, 0.5);
+    inv = Geodesic.WGS84.Inverse(0, 0, 0, 179.5);
+    assertEquals(inv.azi1, 55.96650, 0.5e-5);
+    assertEquals(inv.azi2, 124.03350, 0.5e-5);
+    assertEquals(inv.s12, 19980862, 0.5);
+    inv = Geodesic.WGS84.Inverse(0, 0, 0, 180);
+    assertEquals(inv.azi1, 0.00000, 0.5e-5);
+    assertEquals(inv.azi2, -180.00000, 0.5e-5);
+    assertEquals(inv.s12, 20003931, 0.5);
+    inv = Geodesic.WGS84.Inverse(0, 0, 1, 180);
+    assertEquals(inv.azi1, 0.00000, 0.5e-5);
+    assertEquals(inv.azi2, -180.00000, 0.5e-5);
+    assertEquals(inv.s12, 19893357, 0.5);
+    Geodesic geod = new Geodesic(6.4e6, 0);
+    inv = geod.Inverse(0, 0, 0, 179);
+    assertEquals(inv.azi1, 90.00000, 0.5e-5);
+    assertEquals(inv.azi2, 90.00000, 0.5e-5);
+    assertEquals(inv.s12, 19994492, 0.5);
+    inv = geod.Inverse(0, 0, 0, 180);
+    assertEquals(inv.azi1, 0.00000, 0.5e-5);
+    assertEquals(inv.azi2, -180.00000, 0.5e-5);
+    assertEquals(inv.s12, 20106193, 0.5);
+    inv = geod.Inverse(0, 0, 1, 180);
+    assertEquals(inv.azi1, 0.00000, 0.5e-5);
+    assertEquals(inv.azi2, -180.00000, 0.5e-5);
+    assertEquals(inv.s12, 19994492, 0.5);
+    geod = new Geodesic(6.4e6, -1/300.0);
+    inv = geod.Inverse(0, 0, 0, 179);
+    assertEquals(inv.azi1, 90.00000, 0.5e-5);
+    assertEquals(inv.azi2, 90.00000, 0.5e-5);
+    assertEquals(inv.s12, 19994492, 0.5);
+    inv = geod.Inverse(0, 0, 0, 180);
+    assertEquals(inv.azi1, 90.00000, 0.5e-5);
+    assertEquals(inv.azi2, 90.00000, 0.5e-5);
+    assertEquals(inv.s12, 20106193, 0.5);
+    inv = geod.Inverse(0, 0, 0.5, 180);
+    assertEquals(inv.azi1, 33.02493, 0.5e-5);
+    assertEquals(inv.azi2, 146.97364, 0.5e-5);
+    assertEquals(inv.s12, 20082617, 0.5);
+    inv = geod.Inverse(0, 0, 1, 180);
+    assertEquals(inv.azi1, 0.00000, 0.5e-5);
+    assertEquals(inv.azi2, -180.00000, 0.5e-5);
+    assertEquals(inv.s12, 20027270, 0.5);
+  }
+
+  @Test
+  public void GeodSolve55() {
+    // Check fix for nan + point on equator or pole not returning all nans in
+    // Geodesic::Inverse, found 2015-09-23.
+    GeodesicData inv = Geodesic.WGS84.Inverse(Double.NaN, 0, 0, 90);
+    assertTrue(isNaN(inv.azi1));
+    assertTrue(isNaN(inv.azi2));
+    assertTrue(isNaN(inv.s12));
+    inv = Geodesic.WGS84.Inverse(Double.NaN, 0, 90, 3);
+    assertTrue(isNaN(inv.azi1));
+    assertTrue(isNaN(inv.azi2));
+    assertTrue(isNaN(inv.s12));
+  }
+
+  @Test
+  public void Planimeter0() {
+    // Check fix for pole-encircling bug found 2011-03-16
+    double pa[][] = {{89, 0}, {89, 90}, {89, 180}, {89, 270}};
+    PolygonResult a = Planimeter(pa);
+    assertEquals(a.perimeter, 631819.8745, 1e-4);
+    assertEquals(a.area, 24952305678.0, 1);
+
+    double pb[][] = {{-89, 0}, {-89, 90}, {-89, 180}, {-89, 270}};
+    a = Planimeter(pb);
+    assertEquals(a.perimeter, 631819.8745, 1e-4);
+    assertEquals(a.area, -24952305678.0, 1);
+
+    double pc[][] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
+    a = Planimeter(pc);
+    assertEquals(a.perimeter, 627598.2731, 1e-4);
+    assertEquals(a.area, 24619419146.0, 1);
+
+    double pd[][] = {{90, 0}, {0, 0}, {0, 90}};
+    a = Planimeter(pd);
+    assertEquals(a.perimeter, 30022685, 1);
+    assertEquals(a.area, 63758202715511.0, 1);
+    a = PolyLength(pd);
+    assertEquals(a.perimeter, 20020719, 1);
+    assertTrue(isNaN(a.area));
+  }
+
+  @Test
+  public void Planimeter5() {
+    // Check fix for Planimeter pole crossing bug found 2011-06-24
+    double points[][] = {{89, 0.1}, {89, 90.1}, {89, -179.9}};
+    PolygonResult a = Planimeter(points);
+    assertEquals(a.perimeter, 539297, 1);
+    assertEquals(a.area, 12476152838.5, 1);
+  }
+
+  @Test
+  public void Planimeter6() {
+    // Check fix for Planimeter lon12 rounding bug found 2012-12-03
+    double pa[][] = {{9, -0.00000000000001}, {9, 180}, {9, 0}};
+    PolygonResult a = Planimeter(pa);
+    assertEquals(a.perimeter, 36026861, 1);
+    assertEquals(a.area, 0, 1);
+    double pb[][] = {{9, 0.00000000000001}, {9, 0}, {9, 180}};
+    a = Planimeter(pb);
+    assertEquals(a.perimeter, 36026861, 1);
+    assertEquals(a.area, 0, 1);
+    double pc[][] = {{9, 0.00000000000001}, {9, 180}, {9, 0}};
+    a = Planimeter(pc);
+    assertEquals(a.perimeter, 36026861, 1);
+    assertEquals(a.area, 0, 1);
+    double pd[][] = {{9, -0.00000000000001}, {9, 0}, {9, 180}};
+    a = Planimeter(pd);
+    assertEquals(a.perimeter, 36026861, 1);
+    assertEquals(a.area, 0, 1);
+  }
+
+  @Test
+  public void Planimeter12() {
+    // Area of arctic circle (not really -- adjunct to rhumb-area test)
+    double points[][] = {{66.562222222, 0}, {66.562222222, 180}};
+    PolygonResult a = Planimeter(points);
+    assertEquals(a.perimeter, 10465729, 1);
+    assertEquals(a.area, 0, 1);
+  }
+
+  @Test
+  public void Planimeter13() {
+    // Check encircling pole twice
+    double points[][] = {{89,-360}, {89,-240}, {89,-120},
+                         {89,0}, {89,120}, {89,240}};
+    PolygonResult a =  Planimeter(points);
+    assertEquals(a.perimeter, 1160741, 1);
+    assertEquals(a.area, 32415230256.0, 1);
+  }
+
+}
diff --git a/js/CMakeLists.txt b/js/CMakeLists.txt
new file mode 100644
index 0000000..719c5ba
--- /dev/null
+++ b/js/CMakeLists.txt
@@ -0,0 +1,140 @@
+# This list governs the order in which the JavaScript sources are
+# concatenated.  This shouldn't be changed.
+set (JS_MODULES Math Geodesic GeodesicLine PolygonArea DMS)
+
+# Combine JavaScript into a single file if necessary
+set (JSSCRIPTS)
+set (JS_BUILD 0)
+set (JS_BUILD_MIN 0)
+set (JS_TARGET "${CMAKE_CURRENT_BINARY_DIR}/geographiclib.js")
+set (JS_TARGET_MIN "${CMAKE_CURRENT_BINARY_DIR}/geographiclib.min.js")
+set (JS_TARGETS ${JS_TARGET} ${JS_TARGET_MIN})
+set (FILE_INVENTORY "")
+foreach (_F ${JS_MODULES})
+  set (_S "src/${_F}.js")
+  set (FILE_INVENTORY "${FILE_INVENTORY} ${_F}.js")
+  list (APPEND JSSCRIPTS  ${_S})
+  if ("${CMAKE_CURRENT_SOURCE_DIR}/_S" IS_NEWER_THAN ${JS_TARGET})
+    set (JS_BUILD 1)
+  endif ()
+  if ("${CMAKE_CURRENT_SOURCE_DIR}/_S" IS_NEWER_THAN ${JS_TARGET_MIN})
+    set (JS_BUILD_MIN 1)
+  endif ()
+endforeach ()
+
+if (JS_BUILD)
+  file (STRINGS "src/Math.js" _S REGEX version_string)
+  string (REGEX REPLACE ".*\"(.*)\".*" "\\1" JS_VERSION "${_S}")
+  file (REMOVE ${JS_TARGET})
+  file (READ "HEADER.js" _S)
+  string (CONFIGURE ${_S} _S @ONLY)
+  file (APPEND ${JS_TARGET} "${_S}")
+  file (APPEND ${JS_TARGET} "\n(function(cb) {\n")
+  foreach (_F ${JSSCRIPTS})
+    get_filename_component(_N ${_F} NAME)
+    file (READ "${_F}" _S)
+    # Normalize the line endings.
+    string (REGEX REPLACE "\r" "" _S "${_S}")
+    file (APPEND ${JS_TARGET} "\n/**************** ${_N} ****************/\n")
+    file (APPEND ${JS_TARGET} "${_S}")
+  endforeach ()
+  # export GeographlicLib
+  file (APPEND ${JS_TARGET} "
+cb(GeographicLib);
+
+})(function(geo) {
+  if (typeof module === 'object' && module.exports) {
+    /******** support loading with node's require ********/
+    module.exports = geo;
+  } else if (typeof define === 'function' && define.amd) {
+    /******** support loading with AMD ********/
+    define('geographiclib', [], function() { return geo; });
+  } else {
+    /******** otherwise just pollute our global namespace ********/
+    window.GeographicLib = geo;
+  }
+});
+")
+endif ()
+
+if (JS_BUILD_MIN)
+  file (STRINGS "src/Math.js" _S REGEX version_string)
+  string (REGEX REPLACE ".*\"(.*)\".*" "\\1" JS_VERSION "${_S}")
+  file (REMOVE ${JS_TARGET_MIN})
+  file (READ "HEADER.js" _S)
+  string (CONFIGURE ${_S} _S @ONLY)
+  file (APPEND ${JS_TARGET_MIN} "${_S}")
+  file (APPEND ${JS_TARGET_MIN} "(function(cb){\n")
+  foreach (_F ${JSSCRIPTS})
+    get_filename_component(_N ${_F} NAME)
+    file (READ "${_F}" _S)
+    # Normalize the line endings.
+    string (REGEX REPLACE "\r" "\n" _S "${_S}")
+    # This matches /*...*/ style comments, where ... is any number of
+    # \*[^/] and [^*].  This has the defect that the it won't detect,
+    # e.g., **/ as the end of the comment.
+    string (REGEX REPLACE "/\\*(\\*[^/]|[^*])*\\*/" "" _S "${_S}")
+    string (REGEX REPLACE "//[^\n]*\n" "\n" _S "${_S}")
+    string (REGEX REPLACE "[ \t]+" " " _S "${_S}")
+    string (REGEX REPLACE "([^\"A-Za-z0-9_]) " "\\1" _S "${_S}")
+    string (REGEX REPLACE " ([^\\[\"A-Za-z0-9_])"  "\\1" _S "${_S}")
+    string (REGEX REPLACE "\n " "\n" _S "${_S}")
+    string (REGEX REPLACE " \n" "\n" _S "${_S}")
+    string (REGEX REPLACE "^\n" "" _S "${_S}")
+    string (REGEX REPLACE "\n+" "\n" _S "${_S}")
+    file (APPEND ${JS_TARGET_MIN} "// ${_N}\n${_S}")
+  endforeach ()
+  # export GeographlicLib
+  file (APPEND ${JS_TARGET_MIN}
+    "cb(GeographicLib);
+})(function(geo){
+if(typeof module==='object'&&module.exports){
+module.exports=geo;
+}else if(typeof define==='function'&&define.amd){
+define('geographiclib',[],function(){return geo;});
+}else{
+window.GeographicLib=geo;
+}
+});
+")
+endif ()
+
+# "make javascript" will reconfigure cmake if necessary, since
+# geographiclib.js and geographiclib.min.js are created during
+# configuration.
+add_custom_command (OUTPUT ${JS_TARGETS}
+  DEPENDS ${JSSCRIPTS} HEADER.js
+  COMMAND ${CMAKE_COMMAND} ARGS "."
+  WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
+
+file (GLOB SAMPLES samples/*.html)
+file (COPY ${SAMPLES} DESTINATION .)
+
+add_custom_target (javascript ALL DEPENDS ${JS_TARGETS})
+
+if (COMMON_INSTALL_PATH)
+  set (INSTALL_JS_DIR "lib${LIB_SUFFIX}/node_modules/geographiclib")
+else ()
+  set (INSTALL_JS_DIR "node_modules/geographiclib")
+endif ()
+
+# Install the JavaScript files
+install (FILES ../LICENSE.txt package.json README.md ${JS_TARGETS}
+  DESTINATION ${INSTALL_JS_DIR})
+install (FILES ${JSSCRIPTS} DESTINATION ${INSTALL_JS_DIR}/src)
+install (FILES test/geodesictest.js DESTINATION ${INSTALL_JS_DIR}/test)
+
+if (MAINTAINER)
+  add_custom_target (distrib-npm)
+  add_dependencies (distrib-npm javascript)
+  set (_D ${CMAKE_CURRENT_BINARY_DIR}/geographiclib)
+  add_custom_command (TARGET distrib-npm
+    COMMAND
+    mkdir -p ${_D}/src ${_D}/test &&
+    install -C -m 644 ../LICENSE.txt package.json README.md ${JS_TARGETS}
+    ${_D} &&
+    install -C -m 644 ${JSSCRIPTS} ${_D}/src &&
+    install -C -m 644 test/geodesictest.js ${_D}/test
+    COMMENT "To publish nodejs package, run\nnpm publish ${_D}"
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+endif ()
diff --git a/js/GeographicLib.md b/js/GeographicLib.md
new file mode 100644
index 0000000..298b832
--- /dev/null
+++ b/js/GeographicLib.md
@@ -0,0 +1,60 @@
+## Geodesic routines from GeographicLib
+
+This documentation applies to version 1.45.
+
+### Installation
+
+This library is a JavaScript implementation of the geodesic routines
+from [GeographicLib](http://geographiclib.sf.net).  This solves the
+direct and inverse geodesic problems for an ellipsoid of revolution.
+
+The library can be used in [node](https://nodejs.org) by first
+installing the
+[geographiclib node package](https://www.npmjs.com/package/geographiclib)
+with [npm](https://www.npmjs.com)
+```bash
+$ npm install geographiclib
+$ node
+> var GeographicLib = require("geographiclib");
+```
+Alternatively, you can use it in client-side JavaScript, by including in
+your HTML page
+```html
+<script type="text/javascript"
+        src="http://geographiclib.sf.net/scripts/geographiclib.js">
+</script>
+```
+Both of these prescriptions define a {@link GeographicLib} namespace.
+
+### Examples
+
+Now geodesic calculations can be carried out, for example,
+```javascript
+var geod = GeographicLib.Geodesic.WGS84, r;
+
+// Find the distance from Wellington, NZ (41.32S, 174.81E) to
+// Salamanca, Spain (40.96N, 5.50W)...
+r = geod.Inverse(-41.32, 174.81, 40.96, -5.50);
+console.log("The distance is " + r.s12.toFixed(3) + " m.");
+// This prints "The distance is 19959679.267 m."
+
+// Find the point 20000 km SW of Perth, Australia (32.06S, 115.74E)...
+r = geod.Direct(-32.06, 115.74, 225, 20000e3);
+console.log("The position is (" +
+            r.lat2.toFixed(8) + ", " + r.lon2.toFixed(8) + ").");
+// This prints "The position is (32.11195529, -63.95925278)."
+```
+Two examples of this library in use are
+* [A geodesic calculator](http://geographiclib.sf.net/scripts/geod-calc.html)
+* [Displaying geodesics on Google
+  Maps](http://geographiclib.sf.net/scripts/geod-google.html)
+
+### More information
+* {@tutorial 1-geodesics}
+* {@tutorial 2-interface}
+* {@tutorial 3-examples}
+
+### Authors
+
+* algorithms + js code: Charles Karney (charles at karney.com)
+* node.js port: Yurij Mikhalevich (0 at 39.yt)
diff --git a/js/HEADER.js b/js/HEADER.js
new file mode 100644
index 0000000..583afa0
--- /dev/null
+++ b/js/HEADER.js
@@ -0,0 +1,22 @@
+/*
+ * Geodesic routines from GeographicLib translated to JavaScript.  See
+ * http://geographiclib.sf.net/html/other.html#javascript
+ *
+ * The algorithms are derived in
+ *
+ *    Charles F. F. Karney,
+ *    Algorithms for geodesics, J. Geodesy 87, 43-55 (2013),
+ *    https://dx.doi.org/10.1007/s00190-012-0578-z
+ *    Addenda: http://geographiclib.sf.net/geod-addenda.html
+ *
+ * This file is the concatenation and compression of the JavaScript files in
+ * doc/scripts/GeographicLib in the source tree for GeographicLib.
+ *
+ * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
+ * under the MIT/X11 License.  For more information, see
+ * http://geographiclib.sf.net/
+ *
+ * Version: @JS_VERSION@
+ * File inventory:
+ *  @FILE_INVENTORY@
+ */
diff --git a/js/Makefile.am b/js/Makefile.am
new file mode 100644
index 0000000..188a34a
--- /dev/null
+++ b/js/Makefile.am
@@ -0,0 +1,44 @@
+EXTRAFILES = $(srcdir)/HEADER.js
+
+SAMPLES = \
+	geod-calc.html \
+	geod-google.html \
+	geod-google-instructions.html
+
+# The order here is significant
+JSSCRIPTS = \
+	$(srcdir)/src/Math.js \
+	$(srcdir)/src/Geodesic.js \
+	$(srcdir)/src/GeodesicLine.js \
+	$(srcdir)/src/PolygonArea.js \
+	$(srcdir)/src/DMS.js
+TESTSCRIPTS = $(srcdir)/test/geodesictest.js
+
+all: geographiclib.js geographiclib.min.js $(SAMPLES)
+
+geod-calc.html: samples/geod-calc.html
+	cp $^ $@
+
+geod-google.html: samples/geod-google.html
+	cp $^ $@
+
+geod-google-instructions.html: samples/geod-google-instructions.html
+	cp $^ $@
+
+geographiclib.js: HEADER.js $(JSSCRIPTS)
+	$(srcdir)/js-cat.sh $^ > $@
+
+geographiclib.min.js: HEADER.js $(JSSCRIPTS)
+	$(srcdir)/js-compress.sh $^ > $@
+
+jsdir=$(DESTDIR)$(libdir)/node_modules/geographiclib
+
+install: all
+	$(INSTALL) -d $(jsdir)
+	$(INSTALL) -m 644 geographiclib.js geographiclib.min.js $(jsdir)
+	$(INSTALL) -m 644 $(top_srcdir)/LICENSE.txt $(srcdir)/README.md \
+		$(srcdir)/package.json $(jsdir)
+	$(INSTALL) -d $(jsdir)/src
+	$(INSTALL) -m 644 $(JSSCRIPTS) $(jsdir)/src
+	$(INSTALL) -d $(jsdir)/test
+	$(INSTALL) -m 644 $(TESTSCRIPTS) $(jsdir)/test
diff --git a/python/Makefile.in b/js/Makefile.in
similarity index 89%
copy from python/Makefile.in
copy to js/Makefile.in
index 77c8569..62163e9 100644
--- a/python/Makefile.in
+++ b/js/Makefile.in
@@ -13,11 +13,6 @@
 # PARTICULAR PURPOSE.
 
 @SET_MAKE@
-
-#
-# Makefile.am
-#
-# Copyright (C) 2011, Charles Karney <charles at karney.com>
 VPATH = @srcdir@
 am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
@@ -83,7 +78,7 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 target_triplet = @target@
-subdir = python
+subdir = js
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
@@ -178,7 +173,7 @@ OBJDUMP = @OBJDUMP@
 OBJEXT = @OBJEXT@
 OTOOL = @OTOOL@
 OTOOL64 = @OTOOL64@
-PACKAGE = geographiclib
+PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
 PACKAGE_NAME = @PACKAGE_NAME@
 PACKAGE_STRING = @PACKAGE_STRING@
@@ -251,20 +246,23 @@ target_vendor = @target_vendor@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-PYTHON_FILES = \
-	$(srcdir)/$(PACKAGE)/__init__.py \
-	$(srcdir)/$(PACKAGE)/geomath.py \
-	$(srcdir)/$(PACKAGE)/constants.py \
-	$(srcdir)/$(PACKAGE)/accumulator.py \
-	$(srcdir)/$(PACKAGE)/geodesiccapability.py \
-	$(srcdir)/$(PACKAGE)/geodesic.py \
-	$(srcdir)/$(PACKAGE)/geodesicline.py \
-	$(srcdir)/$(PACKAGE)/polygonarea.py
-
-pythondir = $(libdir)/python/site-packages/$(PACKAGE)
-EXTRA_DIST = Makefile.mk $(PACKAGE)/CMakeLists.txt $(PYTHON_FILES) setup.py \
-	MANIFEST.in README.txt
-
+EXTRAFILES = $(srcdir)/HEADER.js
+SAMPLES = \
+	geod-calc.html \
+	geod-google.html \
+	geod-google-instructions.html
+
+
+# The order here is significant
+JSSCRIPTS = \
+	$(srcdir)/src/Math.js \
+	$(srcdir)/src/Geodesic.js \
+	$(srcdir)/src/GeodesicLine.js \
+	$(srcdir)/src/PolygonArea.js \
+	$(srcdir)/src/DMS.js
+
+TESTSCRIPTS = $(srcdir)/test/geodesictest.js
+jsdir = $(DESTDIR)$(libdir)/node_modules/geographiclib
 all: all-am
 
 .SUFFIXES:
@@ -277,9 +275,9 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	      exit 1;; \
 	  esac; \
 	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python/Makefile'; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu js/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu python/Makefile
+	  $(AUTOMAKE) --gnu js/Makefile
 .PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
@@ -376,7 +374,7 @@ maintainer-clean-generic:
 	@echo "it deletes files that may require special tools to rebuild."
 clean: clean-am
 
-clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+clean-am: clean-generic clean-libtool mostlyclean-am
 
 distclean: distclean-am
 	-rm -f Makefile
@@ -443,7 +441,7 @@ uninstall-am:
 .MAKE: install-am install-strip
 
 .PHONY: all all-am check check-am clean clean-generic clean-libtool \
-	clean-local cscopelist-am ctags-am distclean distclean-generic \
+	cscopelist-am ctags-am distclean distclean-generic \
 	distclean-libtool distdir dvi dvi-am html html-am info info-am \
 	install install-am install-data install-data-am install-dvi \
 	install-dvi-am install-exec install-exec-am install-html \
@@ -455,16 +453,32 @@ uninstall-am:
 	tags-am uninstall uninstall-am
 
 
-install:
-	$(INSTALL) -d $(DESTDIR)$(pythondir)
-	$(INSTALL) -m 644 $(PYTHON_FILES) $(DESTDIR)$(pythondir)
+all: geographiclib.js geographiclib.min.js $(SAMPLES)
+
+geod-calc.html: samples/geod-calc.html
+	cp $^ $@
+
+geod-google.html: samples/geod-google.html
+	cp $^ $@
+
+geod-google-instructions.html: samples/geod-google-instructions.html
+	cp $^ $@
+
+geographiclib.js: HEADER.js $(JSSCRIPTS)
+	$(srcdir)/js-cat.sh $^ > $@
 
-# Don't install setup.py because it ends up in e.g.,
-# /usr/local/lib/python/site-packages/setup.py
-#	$(INSTALL) -m 644 setup.py $(DESTDIR)$(pythondir)/../
+geographiclib.min.js: HEADER.js $(JSSCRIPTS)
+	$(srcdir)/js-compress.sh $^ > $@
 
-clean-local:
-	rm -rf *.pyc $(PACKAGE)/*.pyc
+install: all
+	$(INSTALL) -d $(jsdir)
+	$(INSTALL) -m 644 geographiclib.js geographiclib.min.js $(jsdir)
+	$(INSTALL) -m 644 $(top_srcdir)/LICENSE.txt $(srcdir)/README.md \
+		$(srcdir)/package.json $(jsdir)
+	$(INSTALL) -d $(jsdir)/src
+	$(INSTALL) -m 644 $(JSSCRIPTS) $(jsdir)/src
+	$(INSTALL) -d $(jsdir)/test
+	$(INSTALL) -m 644 $(TESTSCRIPTS) $(jsdir)/test
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/js/Makefile.mk b/js/Makefile.mk
new file mode 100644
index 0000000..36da9ca
--- /dev/null
+++ b/js/Makefile.mk
@@ -0,0 +1,36 @@
+# The order here is significant
+JS_MODULES=Math Geodesic GeodesicLine PolygonArea DMS
+JSSCRIPTS = $(patsubst %,src/%.js,$(JS_MODULES))
+TESTSCRIPTS = $(wildcard test/*.js)
+
+SAMPLESIN = $(wildcard samples/geod-*.html)
+SAMPLES = $(patsubst samples/%,%,$(SAMPLESIN))
+
+all: geographiclib.js geographiclib.min.js $(SAMPLES)
+
+%.html: samples/%.html
+	cp $^ $@
+
+geographiclib.js: HEADER.js $(JSSCRIPTS)
+	./js-cat.sh $^ > $@
+
+geographiclib.min.js: HEADER.js $(JSSCRIPTS)
+	./js-compress.sh $^ > $@
+
+clean:
+	rm -f geographiclib.js geographiclib.min.js *.html
+
+PREFIX = /usr/local
+DEST = $(PREFIX)/lib/node_modules/geographiclib
+INSTALL = install -b
+
+install: all
+	test -d $(DEST) || mkdir -p $(DEST)
+	$(INSTALL) -m 644 geographiclib.js geographiclib.min.js $(DEST)/
+	$(INSTALL) -m 644 ../LICENSE.txt README.md package.json $(DEST)/
+	test -d $(DEST)/src || mkdir -p $(DEST)/src
+	$(INSTALL) -m 644 $(JSSCRIPTS) $(DEST)/src/
+	test -d $(DEST)/test || mkdir -p $(DEST)/test
+	$(INSTALL) -m 644 $(TESTSCRIPTS) $(DEST)/test/
+
+.PHONY: install clean
diff --git a/js/README.md b/js/README.md
new file mode 100644
index 0000000..4cf5524
--- /dev/null
+++ b/js/README.md
@@ -0,0 +1,47 @@
+# Geodesic routines from GeographicLib
+
+This library is a JavaScript implementation of the geodesic routines
+from [GeographicLib](http://geographiclib.sf.net).  This solves the
+direct and inverse geodesic problems for an ellipsoid of revolution.
+
+## Installation
+
+```bash
+$ npm install geographiclib
+```
+
+## Usage
+
+In [node](https://nodejs.org), do
+```javascript
+var GeographicLib = require("geographiclib");
+```
+
+## Documentation
+
+Full documentation is provided at
+[http://geographiclib.sourceforge.net/1.45/js/](http://geographiclib.sourceforge.net/1.45/js/).
+
+## Examples
+
+```javascript
+var GeographicLib = require("geographiclib"),
+    geod = GeographicLib.Geodesic.WGS84, r;
+
+// Find the distance from Wellington, NZ (41.32S, 174.81E) to
+// Salamanca, Spain (40.96N, 5.50W)...
+r = geod.Inverse(-41.32, 174.81, 40.96, -5.50);
+console.log("The distance is " + r.s12.toFixed(3) + " m.");
+// This prints "The distance is 19959679.267 m."
+
+// Find the point 20000 km SW of Perth, Australia (32.06S, 115.74E)...
+r = geod.Direct(-32.06, 115.74, 225, 20000e3);
+console.log("The position is (" +
+            r.lat2.toFixed(8) + ", " + r.lon2.toFixed(8) + ").");
+// This prints "The position is (32.11195529, -63.95925278)."
+```
+
+## Authors
+
+* algorithms + js code: Charles Karney (charles at karney.com)
+* node.js port: Yurij Mikhalevich (0 at 39.yt)
diff --git a/js/conf.json b/js/conf.json
new file mode 100644
index 0000000..d1d32d9
--- /dev/null
+++ b/js/conf.json
@@ -0,0 +1,17 @@
+{
+  "tags": {
+    "allowUnknownTags": true
+  },
+  "source": {
+    "includePattern": ".+\\.js(doc)?$",
+    "excludePattern": "(^|\\/|\\\\)_"
+  },
+  "plugins": [ "plugins/markdown" ],
+  "templates": {
+    "cleverLinks": false,
+    "monospaceLinks": false,
+    "default": {
+      "outputSourceFiles": true
+    }
+  }
+}
diff --git a/js/doc/1-geodesics.md b/js/doc/1-geodesics.md
new file mode 100644
index 0000000..a8cd9e3
--- /dev/null
+++ b/js/doc/1-geodesics.md
@@ -0,0 +1,166 @@
+Jump to
+* [Introduction](#intro)
+* [Additional properties](#additional)
+* [Multiple shortest geodesics](#multiple)
+* [Background](#background)
+* [References](#references)
+
+### <a name="intro"></a>Introduction
+
+Consider a ellipsoid of revolution with equatorial radius *a*, polar
+semi-axis *b*, and flattening *f* = (*a* − *b*)/*a* .  Points on
+the surface of the ellipsoid are characterized by their latitude φ
+and longitude λ.  (Note that latitude here means the
+*geographical latitude*, the angle between the normal to the ellipsoid
+and the equatorial plane).
+
+The shortest path between two points on the ellipsoid at
+(φ<sub>1</sub>, λ<sub>1</sub>) and (φ<sub>2</sub>,
+λ<sub>2</sub>) is called the geodesic.  Its length is
+*s*<sub>12</sub> and the geodesic from point 1 to point 2 has forward
+azimuths α<sub>1</sub> and α<sub>2</sub> at the two end
+points.  In this figure, we have λ<sub>12</sub> =
+λ<sub>2</sub> − λ<sub>1</sub>.
+<center>
+<img src="http://upload.wikimedia.org/wikipedia/commons/c/cb/Geodesic_problem_on_an_ellipsoid.svg" width="250">
+</center>
+A geodesic can be extended indefinitely by requiring that any
+sufficiently small segment is a shortest path; geodesics are also the
+straightest curves on the surface.
+
+Traditionally two geodesic problems are considered:
+* the direct problem — given φ<sub>1</sub>,
+  λ<sub>1</sub>, α<sub>1</sub>, *s*<sub>12</sub>,
+  determine φ<sub>2</sub>, λ<sub>2</sub>, and
+  α<sub>2</sub>; this is solved by
+  {@link module:GeographicLib/Geodesic.Geodesic#Direct Geodesic.Direct}.
+
+* the inverse problem — given φ<sub>1</sub>,
+  λ<sub>1</sub>, φ<sub>2</sub>, λ<sub>2</sub>,
+  determine *s*<sub>12</sub>, α<sub>1</sub>, and
+  α<sub>2</sub>; this is solved by
+  {@link module:GeographicLib/Geodesic.Geodesic#Inverse Geodesic.Inverse}.
+
+### <a name="additional"></a>Additional properties
+
+The routines also calculate several other quantities of interest
+* *S*<sub>12</sub> is the area between the geodesic from point 1 to
+  point 2 and the equator; i.e., it is the area, measured
+  counter-clockwise, of the quadrilateral with corners
+  (φ<sub>1</sub>,λ<sub>1</sub>), (0,λ<sub>1</sub>),
+  (0,λ<sub>2</sub>), and
+  (φ<sub>2</sub>,λ<sub>2</sub>).  It is given in
+  meters<sup>2</sup>.
+* *m*<sub>12</sub>, the reduced length of the geodesic is defined such
+  that if the initial azimuth is perturbed by *d*α<sub>1</sub>
+  (radians) then the second point is displaced by *m*<sub>12</sub>
+  *d*α<sub>1</sub> in the direction perpendicular to the
+  geodesic.  *m*<sub>12</sub> is given in meters.  On a curved surface
+  the reduced length obeys a symmetry relation, *m*<sub>12</sub> +
+  *m*<sub>21</sub> = 0.  On a flat surface, we have *m*<sub>12</sub> =
+  *s*<sub>12</sub>.
+* *M*<sub>12</sub> and *M*<sub>21</sub> are geodesic scales.  If two
+  geodesics are parallel at point 1 and separated by a small distance
+  *dt*, then they are separated by a distance *M*<sub>12</sub> *dt* at
+  point 2.  *M*<sub>21</sub> is defined similarly (with the geodesics
+  being parallel to one another at point 2).  *M*<sub>12</sub> and
+  *M*<sub>21</sub> are dimensionless quantities.  On a flat surface,
+  we have *M*<sub>12</sub> = *M*<sub>21</sub> = 1.
+* σ<sub>12</sub> is the arc length on the auxiliary sphere.
+  This is a construct for converting the problem to one in spherical
+  trigonometry.  The spherical arc length from one equator crossing to
+  the next is always 180°.
+
+If points 1, 2, and 3 lie on a single geodesic, then the following
+addition rules hold:
+* *s*<sub>13</sub> = *s*<sub>12</sub> + *s*<sub>23</sub>
+* σ<sub>13</sub> = σ<sub>12</sub> + σ<sub>23</sub>
+* *S*<sub>13</sub> = *S*<sub>12</sub> + *S*<sub>23</sub>
+* *m*<sub>13</sub> = *m*<sub>12</sub>*M*<sub>23</sub> +
+  *m*<sub>23</sub>*M*<sub>21</sub>
+* *M*<sub>13</sub> = *M*<sub>12</sub>*M*<sub>23</sub> −
+  (1 − *M*<sub>12</sub>*M*<sub>21</sub>)
+  *m*<sub>23</sub>/*m*<sub>12</sub>
+* *M*<sub>31</sub> = *M*<sub>32</sub>*M*<sub>21</sub> −
+  (1 − *M*<sub>23</sub>*M*<sub>32</sub>)
+  *m*<sub>12</sub>/*m*<sub>23</sub>
+
+### <a name="multiple"></a>Multiple shortest geodesics
+
+The shortest distance found by solving the inverse problem is
+(obviously) uniquely defined.  However, in a few special cases there are
+multiple azimuths which yield the same shortest distance.  Here is a
+catalog of those cases:
+* φ<sub>1</sub> = −φ<sub>2</sub> (with neither point at
+  a pole).  If α<sub>1</sub> = α<sub>2</sub>, the geodesic
+  is unique.  Otherwise there are two geodesics and the second one is
+  obtained by setting [α<sub>1</sub>,α<sub>2</sub>] ←
+  [α<sub>2</sub>,α<sub>1</sub>],
+  [*M*<sub>12</sub>,*M*<sub>21</sub>] ←
+  [*M*<sub>21</sub>,*M*<sub>12</sub>], *S*<sub>12</sub> ←
+  −*S*<sub>12</sub>.  (This occurs when the longitude difference
+  is near ±180° for oblate ellipsoids.)
+* λ<sub>2</sub> = λ<sub>1</sub> ± 180° (with
+  neither point at a pole).  If α<sub>1</sub> = 0° or
+  ±180°, the geodesic is unique.  Otherwise there are two
+  geodesics and the second one is obtained by setting
+  [α<sub>1</sub>,α<sub>2</sub>] ←
+  [−α<sub>1</sub>,−α<sub>2</sub>],
+  *S*<sub>12</sub> ← −*S*<sub>12</sub>.  (This occurs when
+  φ<sub>2</sub> is near −φ<sub>1</sub> for prolate
+  ellipsoids.)
+* Points 1 and 2 at opposite poles.  There are infinitely many
+  geodesics which can be generated by setting
+  [α<sub>1</sub>,α<sub>2</sub>] ←
+  [α<sub>1</sub>,α<sub>2</sub>] +
+  [δ,−δ], for arbitrary δ.  (For spheres, this
+  prescription applies when points 1 and 2 are antipodal.)
+* *s*<sub>12</sub> = 0 (coincident points).  There are infinitely many
+  geodesics which can be generated by setting
+  [α<sub>1</sub>,α<sub>2</sub>] ←
+  [α<sub>1</sub>,α<sub>2</sub>] + [δ,δ], for
+  arbitrary δ.
+
+### <a name="background"></a>Background
+
+The algorithms implemented by this package are given in Karney (2013)
+and are based on Bessel (1825) and Helmert (1880); the algorithm for
+areas is based on Danielsen (1989).  These improve on the work of
+Vincenty (1975) in the following respects:
+* The results are accurate to round-off for terrestrial ellipsoids (the
+  error in the distance is less then 15 nanometers, compared to 0.1 mm
+  for Vincenty).
+* The solution of the inverse problem is always found.  (Vincenty's
+  method fails to converge for nearly antipodal points.)
+* The routines calculate differential and integral properties of a
+  geodesic.  This allows, for example, the area of a geodesic polygon to
+  be computed.
+
+### <a name="references"></a>References
+
+* F. W. Bessel,
+  {@link http://arxiv.org/abs/0908.1824 The calculation of longitude and
+  latitude from geodesic measurements (1825)},
+  Astron. Nachr. **331**(8), 852–861 (2010),
+  translated by C. F. F. Karney and R. E. Deakin.
+* F. R. Helmert,
+  {@link http://geographiclib.sf.net/geodesic-papers/helmert80-en.html
+  Mathematical and Physical Theories of Higher Geodesy, Vol 1},
+  (Teubner, Leipzig, 1880), Chaps. 5–7.
+* T. Vincenty,
+  {@link http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
+  Direct and inverse solutions of geodesics on the ellipsoid with
+  application of nested equations},
+  Survey Review **23**(176), 88–93 (1975).
+- J. Danielsen,
+  {@link https://dx.doi.org/10.1179/003962689791474267 The area under
+  the geodesic}, Survey Review **30**(232), 61–66 (1989).
+* C. F. F. Karney,
+  {@link https://dx.doi.org/10.1007/s00190-012-0578-z
+  Algorithms for geodesics}, J. Geodesy **87**(1) 43–55 (2013);
+  {@link http://geographiclib.sf.net/geod-addenda.html addenda}.
+- {@link http://geographiclib.sf.net/geodesic-papers/biblio.html
+  An online geodesic bibliography}.
+* The wikipedia page,
+  {@link https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid
+  Geodesics on an ellipsoid}.
diff --git a/js/doc/2-interface.md b/js/doc/2-interface.md
new file mode 100644
index 0000000..93c1cc2
--- /dev/null
+++ b/js/doc/2-interface.md
@@ -0,0 +1,111 @@
+Jump to
+* [The units](#units)
+* [The results](#results)
+* [The *outmask* and *caps* parameters](#outmask)
+* [Restrictions on the parameters](#restrict)
+
+### <a name="units"></a>The units
+
+All angles (latitude, longitude, azimuth, arc length) are measured in
+degrees with latitudes increasing northwards, longitudes increasing
+eastwards, and azimuths measured clockwise from north.  For a point at a
+pole, the azimuth is defined by keeping the longitude fixed, writing
+φ = ±(90° − ε), and taking the limit
+ε → 0+.
+
+### <a name="results"></a>The results
+
+The results returned by
+{@link module:GeographicLib/Geodesic.Geodesic#Inverse Geodesic.Direct},
+{@link module:GeographicLib/Geodesic.Geodesic#Inverse Geodesic.Inverse},
+{@link module:GeographicLib/GeodesicLine.GeodesicLine#Position
+GeodesicLine.Position}, etc., return an object with
+(some) of the following 12 fields set:
+* *lat1* = φ<sub>1</sub>, latitude of point 1 (degrees)
+* *lon1* = λ<sub>1</sub>, longitude of point 1 (degrees)
+* *azi1* = α<sub>1</sub>, azimuth of line at point 1 (degrees)
+* *lat2* = φ<sub>2</sub>, latitude of point 2 (degrees)
+* *lon2* = λ<sub>2</sub>, longitude of point 2 (degrees)
+* *azi2* = α<sub>2</sub>, (forward) azimuth of line at point 2 (degrees)
+* *s12* = *s*<sub>12</sub>, distance from 1 to 2 (meters)
+* *a12* = σ<sub>12</sub>, arc length on auxiliary sphere from 1 to 2
+  (degrees)
+* *m12* = *m*<sub>12</sub>, reduced length of geodesic (meters)
+* *M12* = *M*<sub>12</sub>, geodesic scale at 2 relative to 1 (dimensionless)
+* *M21* = *M*<sub>21</sub>, geodesic scale at 1 relative to 2 (dimensionless)
+* *S12* = *S*<sub>12</sub>, area between geodesic and equator
+  (meters<sup>2</sup>)
+
+The input parameters together with *a12* are always included in the
+object.  Azimuths are reduced to the range [−180°, 180°).
+See {@tutorial 1-geodesics} for the definitions of these quantities.
+
+### <a name="outmask"></a>The *outmask* and *caps* parameters
+
+By default, the geodesic routines return the 7 basic quantities: *lat1*,
+*lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*, together with the arc
+length *a12*.  The optional output mask parameter, *outmask*, can be
+used to tailor which quantities to calculate.  In addition, when a
+{@link module:GeographicLib/GeodesicLine.GeodesicLine GeodesicLine} is
+constructed it can be provided with the optional capabilities parameter,
+*caps*.
+
+Both *outmask* and *caps* are obtained by or'ing together the following
+values
+* Geodesic.NONE, no capabilities, no output;
+* Geodesic.LATITUDE, compute latitude, *lat2*;
+* Geodesic.LONGITUDE, compute longitude, *lon2*;
+* Geodesic.AZIMUTH, compute azimuths, *azi1* and *azi2*;
+* Geodesic.DISTANCE, compute distance, *s12*;
+* Geodesic.STANDARD, all of the above;
+* Geodesic.DISTANCE_IN, allow *s12* to be used as input in the direct problem;
+* Geodesic.REDUCEDLENGTH, compute reduced length, *m12*;
+* Geodesic.GEODESICSCALE, compute geodesic scales, *M12* and *M21*;
+* Geodesic.AREA, compute area, *S12*;
+* Geodesic.ALL, all of the above;
+* Geodesic.LONG_UNROLL, unroll longitudes.
+
+Geodesic.DISTANCE_IN is a capability provided to the
+{@link module:GeographicLib/GeodesicLine.GeodesicLine GeodesicLine}
+constructor.  It allows the position on the line to specified in terms
+of distance.  (Without this, the position can only be specified in terms
+of the arc length.)
+
+Geodesic.LONG_UNROLL controls the treatment of longitude.  If it is not
+set then the *lon1* and *lon2* fields are both reduced to the range
+[−180°, 180°).  If it is set, then *lon1* is as given in
+the function call and (*lon2* − *lon1*) determines how many times
+and in what sense the geodesic has encircled the ellipsoid.
+
+### <a name="restrict"></a>Restrictions on the parameters
+
+* Latitudes must lie in [−90°, 90°].  Latitudes outside of
+  this range are replaced by NaNs.
+* The distance *s12* is unrestricted.  This allows geodesics to wrap
+  around the ellipsoid.  Such geodesics are no longer shortest paths.
+  However they retain the property that they are the straightest curves
+  on the surface.
+* Similarly, the spherical arc length *a12* is unrestricted.
+* Azimuths are unrestricted; internally these are exactly reduced to
+  the range [−180°, 180°).
+* The equatorial radius *a* and the polar semi-axis *b* must both be
+  positive and finite (this implies that −∞ < *f* < 1).
+* The flattening *f* should satisfy *f* ∈ [−1/50,1/50] in
+  order to retain full accuracy.  This condition holds for most
+  applications in geodesy.
+
+Reasonably accurate results can be obtained for −0.2 ≤ *f* ≤
+0.2.  Here is a table of the approximate maximum error (expressed as a
+distance) for an ellipsoid with the same major radius as the WGS84
+ellipsoid and different values of the flattening.
+
+  | abs(f) | error
+  |:-------|------:
+  | 0.003  |  15 nm
+  | 0.01   |  25 nm
+  | 0.02   |  30 nm
+  | 0.05   |  10 μm
+  | 0.1    | 1.5 mm
+  | 0.2    | 300 mm
+
+Here 1 nm (nanometer) = 10<sup>−9</sup> m (*not* nautical mile!)
diff --git a/js/doc/3-examples.md b/js/doc/3-examples.md
new file mode 100644
index 0000000..42bbf5b
--- /dev/null
+++ b/js/doc/3-examples.md
@@ -0,0 +1,278 @@
+Jump to
+* [GeographicLib namespace](#namespace)
+* [Specifying the ellipsoid](#ellipsoid)
+* [Basic geodesic calculations](#basic)
+* [Computing waypoints](#waypoints)
+* [Measuring areas](#area)
+* [Degrees, minutes, seconds conversion](#dms)
+
+### <a name="namespace"></a>GeographicLib namespace
+
+This capabilities of this package are all exposed through the
+{@link GeographicLib} namespace.  This is can brought into scope in
+various ways.
+
+#### Using node after installing the package with npm
+
+If [npm](https://www.npmjs.com) has been used to install geographiclib
+via one of
+```bash
+$ npm install geographiclib
+$ npm install --global geographiclib
+```
+then in [node](https://nodejs.org), you can do
+```javascript
+var GeographicLib = require("geographiclib");
+```
+
+#### Using node with a free-standing geographiclib.js
+
+If you have geographiclib.js (version 1.45 or later) in your current
+directory, then [node](https://nodejs.org) can access it with
+```javascript
+var GeographicLib = require("./geographiclib");
+```
+A similar prescription works if geographiclib.js is installed elsewhere
+in your filesystem, replacing "./" above with the correct directory.
+Note that the directory must begin with "./", "../", or "/".
+
+#### HTML with your own version of geographiclib.min.js
+
+Load geographiclib.min.js with
+```html
+<script type="text/javascript" src="geographiclib.min.js">
+</script>
+```
+This ".min.js" version has been "minified" by removing comments and
+redundant white space; this is appropriate for web applications.
+
+#### HTML downloading geographiclib.min.js from SourceForge
+
+Load geographiclib.min.js with
+```html
+<script type="text/javascript"
+        src="http://geographiclib.sf.net/scripts/geographiclib.min.js">
+</script>
+```
+This uses the latest version.  If you want use a specific version, load
+with, for example,
+```html
+<script type="text/javascript"
+        src="http://geographiclib.sf.net/scripts/geographiclib-1.45.min.js">
+</script>
+```
+Browse
+[http://geographiclib.sf.net/scripts](http://geographiclib.sf.net/scripts)
+to see what versions are available.
+
+#### Loading geographiclib.min.js with AMD
+
+This uses [require.js](http://requirejs.org/) (which you can download
+[here](http://requirejs.org/docs/download.html)) to load geographiclib
+(version 1.45 or later) asynchronously.  Your web page includes
+```html
+<script type="text/javascript" src="require.js"></script>
+<script type="text/javascript" src="main.js"></script>
+```
+where main.js contains, for example,
+```javascript
+require.config({
+  paths: {
+    geographiclib: "./geographiclib.min"
+    }
+});
+
+define(["geographiclib"], function(GeographicLib) {
+  // do something with GeographicLib here.
+});
+```
+
+### <a name="ellipsoid"></a>Specifying the ellipsoid
+
+Once {@link GeographicLib} has been brought into scope, the ellipsoid is
+defined via the {@link module:GeographicLib/Geodesic.Geodesic Geodesic}
+constructor using the equatorial radius *a* in meters and the flattening
+*f*, for example
+```javascipt
+var geod = new GeographicLib.Geodesic.Geodesic(6378137, 1/298.257223563);
+```
+These are the parameters for the WGS84 ellipsoid and this comes predefined
+by the package as
+```javascipt
+var geod = GeographicLib.Geodesic.WGS84;
+```
+Note that you can set *f* = 0 to give a sphere (on which geodesics are
+great circles) and *f* < 0 to give a prolate ellipsoid.
+
+The rest of the examples on this page assume the following assignments
+```javascript
+var GeographicLib = require("geographiclib");
+var Geodesic = GeographicLib.Geodesic,
+    DMS = GeographicLib.DMS,
+    geod = Geodesic.WGS84;
+```
+with the understanding that the first line should be replaced with the
+appropriate construction needed to bring the
+[GeographicLib namespace](#namespace) into scope.
+
+### <a name="basic"></a>Basic geodesic calculations
+
+The distance from Wellington, NZ (41.32S, 174.81E) to Salamanca, Spain
+(40.96N, 5.50W) using
+{@link module:GeographicLib/Geodesic.Geodesic#Inverse
+Geodesic.Inverse}:
+```javascript
+var r = geod.Inverse(-41.32, 174.81, 40.96, -5.50);
+console.log("The distance is " + r.s12.toFixed(3) + " m.");
+```
+→`The distance is 19959679.267 m.`
+
+The point the point 20000 km SW of Perth, Australia (32.06S, 115.74E) using
+{@link module:GeographicLib/Geodesic.Geodesic#Direct
+Geodesic.Direct}:
+```javascript
+var r = geod.Direct(-32.06, 115.74, 225, 20000e3);
+console.log("The position is (" +
+            r.lat2.toFixed(8) + ", " + r.lon2.toFixed(8) + ").");
+```
+→`The position is (32.11195529, -63.95925278).`
+
+The area between the geodesic from JFK Airport (40.6N, 73.8W) to LHR
+Airport (51.6N, 0.5W) and the equator.  This is an example of setting
+the *outmask* parameter, see {@tutorial 2-interface}, "The *outmask* and
+*caps* parameters".
+```javascript
+var r = geod.Inverse(40.6, -73.8, 51.6, -0.5, Geodesic.AREA);
+console.log("The area is " + r.S12.toFixed(1) + " m^2");
+```
+→ `The area is 40041368848742.5 m^2`
+
+### <a name="waypoints"></a>Computing waypoints
+
+Consider the geodesic between Beijing Airport (40.1N, 116.6E) and San
+Fransisco Airport (37.6N, 122.4W).  Compute waypoints and azimuths at
+intervals of 1000 km using
+{@link module:GeographicLib/Geodesic.Geodesic#Line
+Geodesic.Line} and
+{@link module:GeographicLib/GeodesicLine.GeodesicLine#Position
+GeodesicLine.Position}:
+```javascript
+var r = geod.Inverse(40.1, 116.6, 37.6, -122.4),
+    l = geod.Line(r.lat1, r.lon1, r.azi1),
+    s12 = r.s12; ds = 1000e3, n = Math.ceil(s12 / ds),
+    i, s;
+console.log("distance latitude longitude azimuth");
+for (i = 0; i <= n; ++i) {
+  s = Math.min(ds * i, s12);
+  r = l.Position(s, Geodesic.STANDARD | Geodesic.LONG_UNROLL);
+  console.log(r.s12.toFixed(0) + " " + r.lat2.toFixed(5) + " " +
+              r.lon2.toFixed(5) + " " + r.azi2.toFixed(5));
+}
+```
+gives
+```text
+distance latitude longitude azimuth
+0 40.10000 116.60000 42.91642
+1000000 46.37321 125.44903 48.99365
+2000000 51.78786 136.40751 57.29433
+3000000 55.92437 149.93825 68.24573
+4000000 58.27452 165.90776 81.68242
+5000000 58.43499 183.03167 96.29014
+6000000 56.37430 199.26948 109.99924
+7000000 52.45769 213.17327 121.33210
+8000000 47.19436 224.47209 129.98619
+9000000 41.02145 233.58294 136.34359
+9513998 37.60000 237.60000 138.89027
+```
+The inclusion of Geodesic.LONG_UNROLL in the call to
+{@link module:GeographicLib/GeodesicLine.GeodesicLine#Position
+GeodesicLine.Position} ensures that the longitude does not jump on
+crossing the international dateline.
+
+If the purpose of computing the waypoints is to plot a smooth geodesic,
+then it's not important that they be exactly equally spaced.  In this
+case, it's faster to parameterize the line in terms of the spherical arc
+length with
+{@link module:GeographicLib/GeodesicLine.GeodesicLine#ArcPosition
+GeodesicLine.ArcPosition} instead of the distance.  Here the spacing is
+about 1° of arc which means that the distance between the waypoints
+will be about 60 NM.
+```javascript
+var r = geod.Inverse(40.1, 116.6, 37.6, -122.4, Geodesic.AZIMUTH),
+    l = geod.Line(r.lat1, r.lon1, r.azi1,
+                  Geodesic.LATITUDE | Geodesic.LONGITUDE),
+    a12 = r.a12; da = 1, n = Math.ceil(a12 / da),
+    i, a;
+da = a12 / n;
+console.log("latitude longitude");
+for (i = 0; i <= n; ++i) {
+  a = da * i;
+  r = l.ArcPosition(a, Geodesic.LATITUDE |
+                    Geodesic.LONGITUDE | Geodesic.LONG_UNROLL);
+  console.log(r.lat2.toFixed(5) + " " + r.lon2.toFixed(5));
+}
+```
+gives
+```text
+latitude longitude
+40.10000 116.60000
+40.82573 117.49243
+41.54435 118.40447
+42.25551 119.33686
+42.95886 120.29036
+43.65403 121.26575
+44.34062 122.26380
+...
+39.82385 235.05331
+39.08884 235.91990
+38.34746 236.76857
+37.60000 237.60000
+```
+The variation in the distance between these waypoints is on the order of
+1/*f*.
+
+### <a name="area"></a>Measuring areas
+
+Measure the area of Antarctica using
+{@link module:GeographicLib/Geodesic.Geodesic#Polygon
+Geodesic.Polygon} and the
+{@link module:GeographicLib/PolygonArea.PolygonArea
+PolygonArea} class:
+```javascript
+var p = geod.Polygon(false), i,
+    antarctica = [
+      [-63.1, -58], [-72.9, -74], [-71.9,-102], [-74.9,-102], [-74.3,-131],
+      [-77.5,-163], [-77.4, 163], [-71.7, 172], [-65.9, 140], [-65.7, 113],
+      [-66.6,  88], [-66.9,  59], [-69.8,  25], [-70.0,  -4], [-71.0, -14],
+      [-77.3, -33], [-77.9, -46], [-74.7, -61]
+    ];
+for (i = 0; i < antarctica.length; ++i)
+  p.AddPoint(antarctica[i][0], antarctica[i][1]);
+p = p.Compute(false, true);
+console.log("Perimeter/area of Antarctica are " +
+            p.perimeter.toFixed(3) + " m / " +
+            p.area.toFixed(1) + " m^2.");
+```
+→ `Perimeter/area of Antarctica are 16831067.893 m / 13662703680020.1 m^2.`
+
+If the points of the polygon are being selected interactively, then
+{@link module:GeographicLib/PolygonArea.PolygonArea#TestPoint
+PolygonArea.TestPoint} can be used to report the area and perimeter for
+a polygon with a tentative final vertex which tracks the mouse pointer.
+
+### <a name="dms"></a>Degrees, minutes, seconds conversion
+
+Compute the azimuth for geodesic from JFK (73.8W, 40.6N) to Paris CDG
+(49°01'N, 2°33'E) using the {@link module:GeographicLib/DMS DMS} module:
+```javascript
+var c = "73.8W 40.6N 49°01'N 2°33'E".split(" "),
+    p1 = DMS.DecodeLatLon(c[0], c[1]),
+    p2 = DMS.DecodeLatLon(c[2], c[3]),
+    r = geod.Inverse(p1.lat, p1.lon, p2.lat, p2.lon);
+console.log("Start = (" +
+            DMS.Encode(r.lat1, DMS.MINUTE, 0, DMS.LATITUDE) + ", " +
+            DMS.Encode(r.lon1, DMS.MINUTE, 0, DMS.LONGITUDE) +
+            "), azimuth = " +
+            DMS.Encode(r.azi1, DMS.MINUTE, 1, DMS.AZIMUTH));
+```
+→ `Start = (40°36'N, 073°48'W), azimuth = 053°28.2'`
diff --git a/js/doc/tutorials.json b/js/doc/tutorials.json
new file mode 100644
index 0000000..0b6257f
--- /dev/null
+++ b/js/doc/tutorials.json
@@ -0,0 +1,11 @@
+ {
+     "1-geodesics": {
+         "title": "General information on geodesics"
+     },
+     "2-interface": {
+         "title": "The library interface"
+     },
+     "3-examples": {
+         "title": "Examples of use"
+     }
+ }
diff --git a/js/js-cat.sh b/js/js-cat.sh
new file mode 100755
index 0000000..2721499
--- /dev/null
+++ b/js/js-cat.sh
@@ -0,0 +1,37 @@
+#! /bin/sh -e
+# Concatenate JavaScript files
+HEADER=$1
+shift
+JS_VERSION=`grep -h "version_string = " "$@" | cut -f2 -d'"'`
+FILE_INVENTORY=
+for f; do
+    FILE_INVENTORY="$FILE_INVENTORY `basename $f`"
+done
+sed -e "s/@JS_VERSION@/$JS_VERSION/" -e "s/@FILE_INVENTORY@/$FILE_INVENTORY/" \
+    $HEADER
+cat <<EOF
+
+(function(cb) {
+EOF
+for f; do
+    echo
+    echo "/**************** `basename $f` ****************/"
+    cat $f
+done
+cat <<EOF
+
+cb(GeographicLib);
+
+})(function(geo) {
+  if (typeof module === 'object' && module.exports) {
+    /******** support loading with node's require ********/
+    module.exports = geo;
+  } else if (typeof define === 'function' && define.amd) {
+    /******** support loading with AMD ********/
+    define('geographiclib', [], function() { return geo; });
+  } else {
+    /******** otherwise just pollute our global namespace ********/
+    window.GeographicLib = geo;
+  }
+});
+EOF
diff --git a/js/js-compress.sh b/js/js-compress.sh
new file mode 100755
index 0000000..3bc1052
--- /dev/null
+++ b/js/js-compress.sh
@@ -0,0 +1,38 @@
+#! /bin/sh -e
+# Concatenate and strip JavaScript files
+HEADER=$1
+shift
+JS_VERSION=`grep -h "version_string = " "$@" | cut -f2 -d'"'`
+FILE_INVENTORY=
+for f; do
+    FILE_INVENTORY="$FILE_INVENTORY `basename $f`"
+done
+sed -e "s/@JS_VERSION@/$JS_VERSION/" -e "s/@FILE_INVENTORY@/$FILE_INVENTORY/" \
+    $HEADER
+cat <<EOF
+(function(cb){
+EOF
+for f; do
+    echo "// `basename $f`"
+    # The first regex matches /*...*/ style comments where ... is any
+    # number of \*[^/] and [^*].  This has the defect that the it
+    # won't detect, e.g., **/ as the end of the comment.
+    tr '\n' '\r' < $f |
+	sed -e 's%/\*\(\*[^/]\|[^*]\)*\*/%%g' | tr '\r' '\n' |
+	sed -e 's%//.*%%' | tr -s '	 ' ' ' |
+	sed -e 's/^ //' -e 's/ $//' | grep -v '^$' |
+	sed -e 's/\([^"A-Za-z0-9_]\) /\1/g' -e 's/ \([^\["A-Za-z0-9_]\)/\1/g'
+done
+# support loading with node's require
+cat <<EOF
+cb(GeographicLib);
+})(function(geo){
+if(typeof module==='object'&&module.exports){
+module.exports=geo;
+}else if(typeof define==='function'&&define.amd){
+define('geographiclib',[],function(){return geo;});
+}else{
+window.GeographicLib=geo;
+}
+});
+EOF
diff --git a/js/package.json b/js/package.json
new file mode 100644
index 0000000..cd0a774
--- /dev/null
+++ b/js/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "geographiclib",
+  "version": "1.45.0",
+  "description":
+  "JavaScript implementation of geodesic routines in GeographicLib",
+  "main": "geographiclib.js",
+  "scripts": {
+    "test": "mocha"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://git.code.sf.net/p/geographiclib/code geographiclib-code"
+  },
+  "url": "https://geographiclib.sf.net",
+  "keywords": [
+    "geodesics",
+    "geometry",
+    "geographiclib",
+    "geography",
+    "geodesic problem",
+    "direct geodesic problem",
+    "inverse geodesic problem",
+    "geodesic calculator"
+  ],
+  "devDependencies": {
+    "mocha": "2.3.0"
+  },
+  "author": "Charles Karney <charles at karney.com>",
+  "contributors": [
+    "Yurij Mikhalevich <0 at 39.yt>"
+  ],
+  "license": "MIT",
+  "bugs": {
+    "url": "https://sourceforge.net/p/geographiclib/discussion/",
+    "email": "charles at karney.com"
+  }
+}
diff --git a/doc/scripts/geod-calc.html b/js/samples/geod-calc.html
similarity index 81%
rename from doc/scripts/geod-calc.html
rename to js/samples/geod-calc.html
index e9be50f..bb61d79 100644
--- a/doc/scripts/geod-calc.html
+++ b/js/samples/geod-calc.html
@@ -24,41 +24,93 @@
 		   GeographicLib" />
     <meta http-equiv="Content-Type"
 	  content="text/html; charset=ISO-8859-1">
-    <script type="text/javascript"
-	    src="http://geographiclib.sf.net/scripts/geographiclib.js">
+    <!--
+	 Full URL for latest version of geographiclib.min.js is
+	 http://geographiclib.sf.net/scripts/geographiclib.min.js
+	 URL for a specific version is, for example,
+	 http://geographiclib.sf.net/scripts/geographiclib-1.45.min.js
+      -->
+    <script type="text/javascript" src="geographiclib.min.js">
     </script>
     <script type="text/javascript">
-var geod = GeographicLib.Geodesic.WGS84;
-var dms = GeographicLib.DMS;
+"use strict";
+
+var geod = GeographicLib.Geodesic.WGS84,
+    dms = GeographicLib.DMS;
+
+(function(g) {
+  "use strict";
+
+  /*
+   * Compute the area of a polygon
+   */
+  g.Geodesic.prototype.Area = function(points, polyline) {
+    var poly = this.Polygon(polyline), i;
+    for (i = 0; i < points.length; ++i)
+      poly.AddPoint(points[i].lat, points[i].lon);
+    return poly.Compute(false, true);
+  };
+
+  /*
+   * split a geodesic line into k equal pieces which are no longer than about
+   * ds12 (but k cannot exceed maxk, default 20), and returns an array of
+   * length k + 1 of objects with fields lat, lon, azi.
+   */
+  g.Geodesic.prototype.InversePath =
+    function(lat1, lon1, lat2, lon2, ds12, maxk) {
+      var t = this.Inverse(lat1, lon1, lat2, lon2),
+          k, points, line, da12, vals, i;
+      if (!maxk) maxk = 20;
+      if (!(ds12 > 0))
+        throw new Error("ds12 must be a positive number");
+      k = Math.max(1, Math.min(maxk, Math.ceil(t.s12/ds12)));
+      points = new Array(k + 1);
+      points[0] = {lat: t.lat1, lon: t.lon1, azi: t.azi1};
+      points[k] = {lat: t.lat2, lon: t.lon2, azi: t.azi2};
+      if (k > 1) {
+        line = this.Line(t.lat1, t.lon1, t.azi1, g.STANDARD);
+        da12 = t.a12/k;
+        for (i = 1; i < k; ++i) {
+          vals = line.ArcPosition(i * da12);
+          points[i] = {lat: vals.lat2, lon: vals.lon2, azi: vals.azi2};
+        }
+      }
+      return points;
+    };
+
+})(GeographicLib.Geodesic);
+
 function formatpoint(lat, lon, azi, dmsformat, prec) {
+  var trail;
   prec += 5;
   if (dmsformat) {
-    var trail = prec < 2 ? dms.DEGREE :
+    trail = prec < 2 ? dms.DEGREE :
       (prec < 4 ? dms.MINUTE : dms.SECOND);
     prec = prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4);
     return (dms.Encode(lat, trail, prec, dms.LATITUDE) + " " +
-	    dms.Encode(lon, trail, prec, dms.LONGITUDE) + " " +
-	    dms.Encode(azi, trail, prec, dms.AZIMUTH));
+            dms.Encode(lon, trail, prec, dms.LONGITUDE) + " " +
+            dms.Encode(azi, trail, prec, dms.AZIMUTH));
   } else {
     return (lat.toFixed(prec) + " " +
-	    lon.toFixed(prec) + " " +
-	    azi.toFixed(prec));
+            lon.toFixed(prec) + " " +
+            azi.toFixed(prec));
   }
-}
+};
 
 function GeodesicInverse(input, dmsformat, prec) {
-  var result = {};
+  var result = {},
+      t, p1, p2;
   try {
     // Input is a blank-delimited line: lat1 lon1 lat2 lon2
-    var t = new String(input);
+    t = input;
     t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
     if (t.length != 4)
       throw new Error("Need 4 input items");
-    var p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
-    var p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
+    p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
+    p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
     t = geod.Inverse(p1.lat, p1.lon, p2.lat, p2.lon,
-		     GeographicLib.Geodesic.ALL |
-		     GeographicLib.Geodesic.LONG_UNROLL);
+                     GeographicLib.Geodesic.ALL |
+                     GeographicLib.Geodesic.LONG_UNROLL);
     result.status = "OK";
     result.p1 = formatpoint(t.lat1, t.lon1, t.azi1, dmsformat, prec);
     result.p2 = formatpoint(t.lat2, t.lon2, t.azi2, dmsformat, prec);
@@ -79,22 +131,23 @@ function GeodesicInverse(input, dmsformat, prec) {
     result.S12 = "";
   }
   return result;
-}
+};
 
 function GeodesicDirect(input, dmsformat, prec) {
-  var result = {};
+  var result = {},
+      t, p1, azi1, s12;
   try {
     // Input is a blank-delimited line: lat1 lon1 azi1 s12
-    var t = new String(input);
+    t = input;
     t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
     if (t.length != 4)
       throw new Error("Need 4 input items");
-    var p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
-    var azi1 = GeographicLib.DMS.DecodeAzimuth(t[2]);
-    var s12 = parseFloat(t[3]);
+    p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
+    azi1 = GeographicLib.DMS.DecodeAzimuth(t[2]);
+    s12 = parseFloat(t[3]);
     t = geod.Direct(p1.lat, p1.lon, azi1, s12,
-		    GeographicLib.Geodesic.ALL |
-		    GeographicLib.Geodesic.LONG_UNROLL);
+                    GeographicLib.Geodesic.ALL |
+                    GeographicLib.Geodesic.LONG_UNROLL);
     result.status = "OK";
     result.p1 = formatpoint(t.lat1, t.lon1, t.azi1, dmsformat, prec);
     result.p2 = formatpoint(t.lat2, t.lon2, t.azi2, dmsformat, prec);
@@ -115,27 +168,25 @@ function GeodesicDirect(input, dmsformat, prec) {
     result.S12 = "";
   }
   return result;
-}
+};
 
 function GeodesicInversePath(input, dmsformat, prec) {
-  var result = {};
+  var result = {},
+      t, p1, p2, ds12, maxnum, i;
   try {
     // Input is a blank-delimited line: lat1 lon1 lat2 lon2 ds12 maxnum
-    var t = new String(input);
+    t = input;
     t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,8);
     if (t.length != 6)
       throw new Error("Need 6 input items");
-    var p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
-    var p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
-    var ds12 = parseFloat(t[4]);
-    var maxnum = parseInt(t[5]);
-    var t = new String(input);
-    t = t.split(/[\s,]+/,8);
-    if (t[0] == "") t.shift();
+    p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
+    p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
+    ds12 = parseFloat(t[4]);
+    maxnum = parseInt(t[5]);
     t = geod.InversePath(p1.lat, p1.lon, p2.lat, p2.lon, ds12, maxnum);
     result.status = "OK";
     result.points = ""
-    for (var i = 0; i < t.length; ++i)
+    for (i = 0; i < t.length; ++i)
       result.points +=
       formatpoint(t[i].lat, t[i].lon, t[i].azi, dmsformat, prec) + "\n";
   }
@@ -144,21 +195,22 @@ function GeodesicInversePath(input, dmsformat, prec) {
     result.points = "";
   }
   return result;
-}
+};
 
 function GeodesicArea(input, polyline) {
-  var result = {};
+  var result = {},
+      t, i, pos;
   try {
     // Input is a newline-delimited points;
     // each point is blank-delimited: lat lon
-    var t = new String(input);
+    t = input;
     t = t.split(/[\r\n]/);
-    if (t[0] == "") t.shift();
-    if (t[t.length-1] == "") t.pop();
-    for (var i = 0; i < t.length; ++i) {
-      var pos = t[i].replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,4);
+    if (t.length > 0 && t[0] == "") t.shift();
+    if (t.length > 0 && t[t.length-1] == "") t.pop();
+    for (i = 0; i < t.length; ++i) {
+      pos = t[i].replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,4);
       if (pos.length != 2)
-	throw new Error("Need 2 items on each line");
+        throw new Error("Need 2 items on each line");
       t[i] = dms.DecodeLatLon(pos[0], pos[1]);
     }
     t = geod.Area(t, polyline);
@@ -173,7 +225,7 @@ function GeodesicArea(input, polyline) {
     result.area = "";
   }
   return result;
-}
+};
     </script>
   </head>
   <body>
@@ -182,13 +234,15 @@ function GeodesicArea(input, polyline) {
     </div>
     <p>
       This page illustrates the geodesic routines available in
-      <a href="http://geographiclib.sf.net">GeographicLib</a>.
-      The C++ code has been converted to JavaScript so the calculations
-      are carried out on the client.  The algorithms are considerably
-      more accurate than Vincenty's method, and offer more functionality
-      (an inverse method which never fails to converge, differential
-      properties of the geodesic, and the area under a geodesic).  The
-      algorithms are derived in
+      JavaScript package
+      <a href="https://www.npmjs.com/package/geographiclib">
+	geographiclib</a>, which is documented
+      <a href="http://geographiclib.sourceforge.net/html/js/">
+	here</a>.
+      The algorithms are considerably more accurate than Vincenty's
+      method, and offer more functionality (an inverse method which
+      never fails to converge, differential properties of the geodesic,
+      and the area under a geodesic).  The algorithms are derived in
       <blockquote>
 	Charles F. F. Karney,<br>
 	<a href="https://dx.doi.org/10.1007/s00190-012-0578-z">
diff --git a/doc/scripts/geod-google-instructions.html b/js/samples/geod-google-instructions.html
similarity index 93%
rename from doc/scripts/geod-google-instructions.html
rename to js/samples/geod-google-instructions.html
index 9046bb5..bbbf1ae 100644
--- a/doc/scripts/geod-google-instructions.html
+++ b/js/samples/geod-google-instructions.html
@@ -91,10 +91,12 @@
       these call a C++ backend.
     </p>
     <p>
-      The Javascipt code for computing the geodesic lines, circles, and
-      envelopes is part of
-      <a href="http://geographiclib.sf.net/">GeographicLib</a>.
-      The algorithms are derived in
+      The JavaScript code for computing the geodesic lines, circles, and
+      envelopes availabe in the JavaScript package
+      <a href="https://www.npmjs.com/package/geographiclib">
+	geographiclib</a>, which is documented
+      <a href="http://geographiclib.sourceforge.net/html/js/">
+	here</a>.  The algorithms are derived in
       <blockquote>
 	Charles F. F. Karney,<br>
 	<a href="https://dx.doi.org/10.1007/s00190-012-0578-z">
diff --git a/js/samples/geod-google.html b/js/samples/geod-google.html
new file mode 100644
index 0000000..3c0be63
--- /dev/null
+++ b/js/samples/geod-google.html
@@ -0,0 +1,340 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
+    <title>Geodesic lines, circles, envelopes in Google Maps</title>
+    <meta name="description"
+	  content="Geodesic lines, circles, envelopes in Google Maps" />
+    <meta name="author" content="Charles F. F. Karney">
+    <meta name="keywords"
+	  content="geodesics,
+		   geodesic distance,
+		   geographic distance,
+		   shortest path,
+		   direct geodesic problem,
+		   inverse geodesic problem,
+		   distance and azimuth,
+		   distance and heading,
+		   range and bearing,
+		   geographic circle,
+		   geodesic envelope,
+		   geodesic astroid,
+		   latitude and longitude,
+		   Google Maps,
+		   WGS84 ellipsoid,
+		   GeographicLib" />
+    <meta http-equiv="Content-Type"
+	  content="text/html; charset=ISO-8859-1">
+    <style>
+      html, body, #map-canvas {
+        height: 100%;
+        margin: 0px;
+        padding: 0px
+      }
+    </style>
+    <!--
+	 Full URL for latest version of geographiclib.min.js is
+	 http://geographiclib.sf.net/scripts/geographiclib.min.js
+	 URL for a specific version is, for example,
+	 http://geographiclib.sf.net/scripts/geographiclib-1.45.min.js
+      -->
+    <script type="text/javascript" src="geographiclib.min.js">
+    </script>
+    <script type="text/javascript"
+	    src="https://maps.googleapis.com/maps/api/js?sensor=false">
+    </script>
+    <script type="text/javascript">
+"use strict";
+
+var geod = GeographicLib.Geodesic.WGS84,
+    dms = GeographicLib.DMS,
+    geodesic, circle, envelope, map;
+
+(function(g) {
+  "use strict";
+
+  /*
+   * split a geodesic line into k equal pieces which are no longer than about
+   * ds12 (but k cannot exceed maxk, default 20), and returns an array of
+   * length k + 1 of objects with fields lat, lon, azi.
+   */
+  g.Geodesic.prototype.DirectPath =
+    function(lat1, lon1, azi1, s12, ds12, maxk) {
+      var t = this.Direct(lat1, lon1, azi1, s12),
+          k, points, line, da12, vals, i;
+      if (!maxk) maxk = 20;
+      if (!(ds12 > 0))
+        throw new Error("ds12 must be a positive number");
+      k = Math.max(1, Math.min(maxk, Math.ceil(Math.abs(t.s12)/ds12)));
+      points = new Array(k + 1);
+      points[0] = {lat: t.lat1, lon: t.lon1, azi: t.azi1};
+      points[k] = {lat: t.lat2, lon: t.lon2, azi: t.azi2};
+      if (k > 1) {
+        line = this.Line(t.lat1, t.lon1, t.azi1, g.STANDARD);
+        da12 = t.a12/k;
+        for (i = 1; i < k; ++i) {
+          vals = line.ArcPosition(i * da12);
+          points[i] = {lat: vals.lat2, lon: vals.lon2, azi: vals.azi2};
+        }
+      }
+      return points;
+    };
+
+  /*
+   * return the geodesic circle of radius s12 centered on the point lat1,
+   * lon1.  k equally spaced azimuths starting at azi1 are computed.  This
+   * returns an array of k + 1 points with fields lat and lon (with the
+   * first and last points matching).
+   */
+  g.Geodesic.prototype.Circle = function(lat1, lon1, azi1, s12, k) {
+    var points, vals, i, azi1a;
+    if (!(Math.abs(lat1) <= 90))
+      throw new Error("lat1 must be in [-90,90]");
+    if (!(isFinite(s12)))
+      throw new Error("s12 must be a finite number");
+    if (!k || k < 4) k = 24;
+    points = new Array(k + 1);
+    for (i = 0; i <= k; ++i) {
+      azi1a = azi1 + (k - i) * 360 / k; // Traverse circle counter-clockwise
+      vals = this.Direct(lat1, lon1, azi1a, s12, g.LATITUDE | g.LONGITUDE);
+      points[i] = {lat: vals.lat2, lon: vals.lon2};
+    }
+    return points;
+  };
+
+  /*
+   * returns the ord'th order envelope for geodesics emanating from lat1, lon1.
+   * This is located approximately circumference * ord/2 from the point.  Thus
+   * odd numbered envelopes are centered at the antipodal point and even number
+   * envelopes are centered on the point itself.  This returns an array of k +
+   * 1 points with fields lat and lon (with the first and last points
+   * matching).
+   */
+  g.Geodesic.prototype.Envelope = function(lat1, lon1, k, ord) {
+    var points, vals, line, s12, j, i, azi1;
+    if (!(Math.abs(lat1) <= 90))
+      throw new Error("lat1 must be in [-90,90]");
+    if (!k || k < 4) k = 24;
+    if (!ord) ord = 1;
+    points = new Array(k + 1);
+    for (i = 0; i <= k; ++i) {
+      azi1 = -180 + i * 360 / k;
+      line = this.Line(lat1, lon1, azi1,
+                       g.LATITUDE | g.LONGITUDE | g.DISTANCE_IN |
+                       g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
+      vals = line.ArcPosition(180 * ord,
+                              g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
+      j = 0;
+      while (true) {
+        // Solve m12(s12) = 0 by Newton's method using dm12/ds12 = M21
+        s12 = vals.s12 - vals.m12/vals.M21;
+        if (Math.abs(vals.m12) < line._a * g.tol2_ * 0.1 || ++j > 10)
+          break;
+        vals = line.Position(s12,
+                             g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE);
+      }
+      vals = line.Position(s12, g.LATITUDE | g.LONGITUDE);
+      points[i] = {lat: vals.lat2, lon: vals.lon2};
+    }
+    return points;
+  };
+
+})(GeographicLib.Geodesic);
+
+function initialize() {
+  var i,
+      mapOptions = {
+        zoom: 8,
+        center: new google.maps.LatLng(41.3, -5.5),
+        mapTypeId: google.maps.MapTypeId.ROADMAP
+      },
+      geodesicOptions = {
+        strokeColor: '#0000FF',
+        strokeOpacity: 1,
+        strokeWeight: 3,
+        geodesic: true
+      },
+      circleOptions = {
+        strokeColor: '#00FF00',
+        strokeOpacity: 1,
+        strokeWeight: 3,
+        geodesic: true
+      },
+      envelopeOptions = {
+        strokeColor: '#FF0000',
+        strokeOpacity: 1,
+        strokeWeight: 3,
+        geodesic: true
+      };
+  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
+
+  geodesic = new google.maps.Polyline(geodesicOptions);
+  geodesic.setMap(map);
+
+  circle = new google.maps.Polyline(circleOptions);
+  circle.setMap(map);
+
+  envelope = new Array(4);
+  for (i = 0; i < 4; ++i) {
+    envelope[i] = new google.maps.Polyline(envelopeOptions);
+    envelope[i].setMap(map);
+  }
+};
+
+function formatpoint(lat, lon, azi, dmsformat, prec) {
+  "use strict";
+  var trail;
+  prec += 5;
+  if (dmsformat) {
+    trail = prec < 2 ? dms.DEGREE :
+      (prec < 4 ? dms.MINUTE : dms.SECOND);
+    prec = prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4);
+    return (dms.Encode(lat, trail, prec, dms.LATITUDE) + " " +
+            dms.Encode(lon, trail, prec, dms.LONGITUDE) + " " +
+            dms.Encode(azi, trail, prec, dms.AZIMUTH));
+  } else {
+    return (lat.toFixed(prec) + " " +
+            lon.toFixed(prec) + " " +
+            azi .toFixed(prec));
+  }
+};
+
+function GeodesicInverse(input) {
+  "use strict";
+  var result = {},
+      t, p1, p2, v;
+  try {
+    // Input is a blank-delimited line: lat1 lon1 lat2 lon2
+    t = input;
+    t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
+    if (t.length != 4)
+      throw new Error("Need 4 input items");
+    p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
+    p2 = GeographicLib.DMS.DecodeLatLon(t[2], t[3]);
+    v = geod.Inverse(p1.lat, p1.lon, p2.lat, p2.lon);
+    result.status = "OK";
+    result.p1 = formatpoint(v.lat1, v.lon1, v.azi1, true, 0);
+    result.p2 = formatpoint(v.lat2, v.lon2, v.azi2, true, 0);
+    result.s12 = v.s12.toFixed(0);
+    draw(v);
+  }
+  catch (e) {
+    result.status = "ERROR: " + e.message;
+    result.p1 = "";
+    result.p2 = "";
+    result.s12 = "";
+  }
+  return result;
+};
+
+function GeodesicDirect(input) {
+  "use strict";
+  var result = {},
+      t, p1, azi1, s12, v;
+  try {
+    // Input is a blank-delimited line: lat1 lon1 azi1 s12
+    t = input;
+    t = t.replace(/^\s+/,"").replace(/\s+$/,"").split(/[\s,]+/,6);
+    if (t.length != 4)
+      throw new Error("Need 4 input items");
+    p1 = GeographicLib.DMS.DecodeLatLon(t[0], t[1]);
+    azi1 = GeographicLib.DMS.DecodeAzimuth(t[2]);
+    s12 = parseFloat(t[3]);
+    v =  geod.Direct(p1.lat, p1.lon, azi1, s12);
+    result.status = "OK";
+    result.p1 = formatpoint(v.lat1, v.lon1, v.azi1, true, 0);
+    result.p2 = formatpoint(v.lat2, v.lon2, v.azi2, true, 0);
+    result.s12 = v.s12.toFixed(0);
+    draw(v);
+  }
+  catch (e) {
+    result.status = "ERROR: " + e.message;
+    result.p1 = "";
+    result.p2 = "";
+    result.s12 = "";
+  }
+  return result;
+};
+
+function draw(v) {
+  "use strict";
+  var points = geod.DirectPath(v.lat1, v.lon1, v.azi1, v.s12,
+                               100000, 100),
+      i, k, path;
+  clearPaths();
+
+  path = geodesic.getPath();
+  for (k = 0; k < points.length; ++k)
+    path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
+  points = geod.Circle(v.lat1, v.lon1, v.azi1, v.s12, 72);
+  path = circle.getPath();
+  for (k = 0; k < points.length; ++k)
+    path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
+  for (i = 0; i < 4; ++i) {
+    points = geod.Envelope(v.lat1, v.lon1, 72, i+1);
+    path = envelope[i].getPath();
+    for (k = 0; k < points.length; ++k)
+      path.push(new google.maps.LatLng(points[k].lat, points[k].lon));
+  }
+  map.panTo(new google.maps.LatLng(v.lat2, v.lon2));
+};
+
+function clearPaths() {
+  "use strict";
+  var i,
+      cPath = geodesic.getPath();
+  while (cPath.getLength()) cPath.pop();
+  cPath = circle.getPath();
+  while (cPath.getLength()) cPath.pop();
+  for (i = 0; i < 4; ++i) {
+    cPath = envelope[i].getPath();
+    while (cPath.getLength()) cPath.pop();
+  }
+};
+    </script>
+  </head>
+  <body onload="initialize()">
+    <div id="map-canvas"
+	 style="position:relative; border: 1px solid black; width:99.5%;
+	 height:72%;">
+    </div>
+    <div>
+      <p>
+	 Direct:  
+	<input id="inputb" size=60 value="41°19'S 174°49'E 135° 60000e3" />
+	<input type="button" value="compute"
+	       onclick="var t = GeodesicDirect(document.getElementById('inputb').value);
+			document.getElementById('status').value = t.status;
+			document.getElementById('p1').value = t.p1;
+			document.getElementById('p2').value = t.p2;
+			document.getElementById('s12').value = t.s12;" />
+      </p>
+      <p>
+	 Inverse:
+	<input id="inputa" size=60 value="41°19'S 174°49'E 40°58'N 5°30'W" />
+	<input type="button" value="compute"
+	       onclick="var t = GeodesicInverse(document.getElementById('inputa').value);
+			document.getElementById('status').value = t.status;
+			document.getElementById('p1').value = t.p1;
+			document.getElementById('p2').value = t.p2;
+			document.getElementById('s12').value = t.s12;" />
+      </p>
+      <p>
+	 lat1 lon1 azi1: <input type="text" "readonly" id="p1" size=60>
+      </p>
+      <p>
+	 lat2 lon2 azi2: <input type="text" "readonly" id="p2" size=60>
+      </p>
+      <p>
+	 s12: <input type="text" "readonly" id="s12" size=20>
+	   status: <input "readonly" id="status" size=30>
+	  
+	<a href="geod-google-instructions.html"><b>INSTRUCTIONS</b></a>
+	(v<script type="text/javascript">
+	  document.write(GeographicLib.Constants.version_string);
+	</script>)
+      </p>
+  </div>
+  </body>
+</html>
diff --git a/doc/scripts/GeographicLib/DMS.js b/js/src/DMS.js
similarity index 50%
rename from doc/scripts/GeographicLib/DMS.js
rename to js/src/DMS.js
index c79b65b..a87382e 100644
--- a/doc/scripts/GeographicLib/DMS.js
+++ b/js/src/DMS.js
@@ -1,4 +1,4 @@
-/**
+/*
  * DMS.js
  * Transcription of DMS.[ch]pp into JavaScript.
  *
@@ -8,64 +8,92 @@
  * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
  * under the MIT/X11 License.  For more information, see
  * http://geographiclib.sourceforge.net/
- **********************************************************************/
+ */
 
 GeographicLib.DMS = {};
 
-(function() {
-  var d = GeographicLib.DMS;
-  var m = GeographicLib.Math;
-  d.lookup = function(s, c) {
+(function(
+  /**
+   * @exports GeographicLib/DMS
+   * @description Decode/Encode angles expressed as degrees, minutes, and
+   *   seconds.  This module defines several constants:
+   *   - hemisphere indicator (returned by
+   *       {@link module:GeographicLib/DMS.Decode Decode}) and a formatting
+   *       indicator (used by
+   *       {@link module:GeographicLib/DMS.Encode Encode})
+   *     - NONE = 0, no designator and format as plain angle;
+   *     - LATITUDE = 1, a N/S designator and format as latitude;
+   *     - LONGITUDE = 2, an E/W designator and format as longitude;
+   *     - AZIMUTH = 3, format as azimuth;
+   *   - the specification of the trailing component in
+   *       {@link module:GeographicLib/DMS.Encode Encode}
+   *     - DEGREE;
+   *     - MINUTE;
+   *     - SECOND.
+   */
+  d) {
+  "use strict";
+
+  var lookup, zerofill, InternalDecode, NumMatch,
+      hemispheres_ = "SNWE",
+      signs_ = "-+",
+      digits_ = "0123456789",
+      dmsindicators_ = "D'\":",
+      // dmsindicatorsu_ = "\u00b0\u2032\u2033"; // Unicode variants
+      dmsindicatorsu_ = "\u00b0'\"", // Use degree symbol
+      components_ = ["degrees", "minutes", "seconds"];
+  lookup = function(s, c) {
     return s.indexOf(c.toUpperCase());
   };
-  d.zerofill = function(s, n) {
+  zerofill = function(s, n) {
     return String("0000").substr(0, Math.max(0, Math.min(4, n-s.length))) +
       s;
   };
-  d.hemispheres_ = "SNWE";
-  d.signs_ = "-+";
-  d.digits_ = "0123456789";
-  d.dmsindicators_ = "D'\":";
-  // d.dmsindicatorsu_ = "\u00b0\u2032\u2033"; // Unicode variants
-  d.dmsindicatorsu_ = "\u00b0'\""; // Use degree symbol
-  d.components_ = ["degrees", "minutes", "seconds"];
   d.NONE = 0;
   d.LATITUDE = 1;
   d.LONGITUDE = 2;
   d.AZIMUTH = 3;
-  d.NUMBER = 4;
   d.DEGREE = 0;
   d.MINUTE = 1;
   d.SECOND = 2;
 
-  // return val, ind
+  /**
+   * @summary Decode a DMS string.
+   * @description The interpretation of the string is given in the
+   *   documentation of the corresponding function, Decode(string&, flag&)
+   *   in the {@link
+   *   http://geographiclib.sourceforge.net/html/classGeographicLib_1_1DMS.html
+   *   C++ DMS class}
+   * @param {string} dms the string.
+   * @returns {object} r where r.val is the decoded value (degrees) and r.ind
+   *   is a hemisphere designator, one of NONE, LATITUDE, LONGITUDE.
+   * @throws an error if the string is illegal.
+   */
   d.Decode = function(dms) {
-    var dmsa = dms;
-    dmsa = dmsa.replace(/\u00b0/g, 'd');
-    dmsa = dmsa.replace(/\u00ba/g, 'd');
-    dmsa = dmsa.replace(/\u2070/g, 'd');
-    dmsa = dmsa.replace(/\u02da/g, 'd');
-    dmsa = dmsa.replace(/\u2032/g, '\'');
-    dmsa = dmsa.replace(/\u00b4/g, '\'');
-    dmsa = dmsa.replace(/\u2019/g, '\'');
-    dmsa = dmsa.replace(/\u2033/g, '"');
-    dmsa = dmsa.replace(/\u201d/g, '"');
-    dmsa = dmsa.replace(/\u2212/g, '-');
-    dmsa = dmsa.replace(/''/g, '"');
-    dmsa = dmsa.trim();
-    var errormsg = new String("");
-    var end = dmsa.length;
-    var v = 0, i = 0, mi, pi, vals;
-    var ind1 = d.NONE, ind2;
-    var p, pa, pb;
+    var dmsa = dms, end,
+        v = 0, i = 0, mi, pi, vals,
+        ind1 = d.NONE, ind2, p, pa, pb;
+    dmsa = dmsa.replace(/\u00b0/g, 'd')
+          .replace(/\u00ba/g, 'd')
+          .replace(/\u2070/g, 'd')
+          .replace(/\u02da/g, 'd')
+          .replace(/\u2032/g, '\'')
+          .replace(/\u00b4/g, '\'')
+          .replace(/\u2019/g, '\'')
+          .replace(/\u2033/g, '"')
+          .replace(/\u201d/g, '"')
+          .replace(/\u2212/g, '-')
+          .replace(/''/g, '"')
+          .trim();
+    end = dmsa.length;
     // p is pointer to the next piece that needs decoding
     for (p = 0; p < end; p = pb, ++i) {
       pa = p;
       // Skip over initial hemisphere letter (for i == 0)
-      if (i == 0 && d.lookup(d.hemispheres_, dmsa.charAt(pa)) >= 0)
+      if (i === 0 && lookup(hemispheres_, dmsa.charAt(pa)) >= 0)
         ++pa;
       // Skip over initial sign (checking for it if i == 0)
-      if (i > 0 || (pa < end && d.lookup(d.signs_, dmsa.charAt(pa)) >= 0))
+      if (i > 0 || (pa < end && lookup(signs_, dmsa.charAt(pa)) >= 0))
         ++pa;
       // Find next sign
       mi = dmsa.substr(pa, end - pa).indexOf('-');
@@ -73,7 +101,7 @@ GeographicLib.DMS = {};
       if (mi < 0) mi = end; else mi += pa;
       if (pi < 0) pi = end; else pi += pa;
       pb = Math.min(mi, pi);
-      vals = d.InternalDecode(dmsa.substr(p, pb - p));
+      vals = InternalDecode(dmsa.substr(p, pb - p));
       v += vals.val; ind2 = vals.ind;
       if (ind1 == d.NONE)
         ind1 = ind2;
@@ -81,26 +109,31 @@ GeographicLib.DMS = {};
         throw new Error("Incompatible hemisphere specifies in " +
                         dmsa.substr(0, pb));
     }
-    if (i == 0)
+    if (i === 0)
       throw new Error("Empty or incomplete DMS string " + dmsa);
     return {val: v, ind: ind1};
   };
 
-  d.InternalDecode = function(dmsa) {
-    var vals = {};
-    var errormsg = new String("");
+  InternalDecode = function(dmsa) {
+    var vals = {}, errormsg = "",
+        sign, beg, end, ind1, k,
+        ipieces, fpieces, npiece,
+        icurrent, fcurrent, ncurrent, p,
+        pointseen,
+        digcount, intcount,
+        x;
     do {                       // Executed once (provides the ability to break)
-      var sign = 1;
-      var beg = 0, end = dmsa.length;
-      var ind1 = d.NONE;
-      var k = -1;
-      if (end > beg && (k = d.lookup(d.hemispheres_, dmsa.charAt(beg))) >= 0) {
+      sign = 1;
+      beg = 0; end = dmsa.length;
+      ind1 = d.NONE;
+      k = -1;
+      if (end > beg && (k = lookup(hemispheres_, dmsa.charAt(beg))) >= 0) {
         ind1 = (k & 2) ? d.LONGITUDE : d.LATITUDE;
         sign = (k & 1) ? 1 : -1;
         ++beg;
       }
       if (end > beg &&
-          (k = d.lookup(d.hemispheres_, dmsa.charAt(end-1))) >= 0) {
+          (k = lookup(hemispheres_, dmsa.charAt(end-1))) >= 0) {
         if (k >= 0) {
           if (ind1 !== d.NONE) {
             if (dmsa.charAt(beg - 1).toUpperCase() ===
@@ -119,7 +152,7 @@ GeographicLib.DMS = {};
           --end;
         }
       }
-      if (end > beg && (k = d.lookup(d.signs_, dmsa.charAt(beg))) >= 0) {
+      if (end > beg && (k = lookup(signs_, dmsa.charAt(beg))) >= 0) {
         if (k >= 0) {
           sign *= k ? 1 : -1;
           ++beg;
@@ -129,18 +162,19 @@ GeographicLib.DMS = {};
         errormsg = "Empty or incomplete DMS string " + dmsa;
         break;
       }
-      var ipieces = [0, 0, 0];
-      var fpieces = [0, 0, 0];
-      var npiece = 0;
-      var icurrent = 0;
-      var fcurrent = 0;
-      var ncurrent = 0, p = beg;
-      var pointseen = false;
-      var digcount = 0;
-      var intcount = 0;
+      ipieces = [0, 0, 0];
+      fpieces = [0, 0, 0];
+      npiece = 0;
+      icurrent = 0;
+      fcurrent = 0;
+      ncurrent = 0;
+      p = beg;
+      pointseen = false;
+      digcount = 0;
+      intcount = 0;
       while (p < end) {
-        var x = dmsa.charAt(p++);
-        if ((k = d.lookup(d.digits_, x)) >= 0) {
+        x = dmsa.charAt(p++);
+        if ((k = lookup(digits_, x)) >= 0) {
           ++ncurrent;
           if (digcount > 0) {
             ++digcount;         // Count of decimal digits
@@ -156,27 +190,27 @@ GeographicLib.DMS = {};
           }
           pointseen = true;
           digcount = 1;
-        } else if ((k = d.lookup(d.dmsindicators_, x)) >= 0) {
+        } else if ((k = lookup(dmsindicators_, x)) >= 0) {
           if (k >= 3) {
             if (p === end) {
-              errormsg = "Illegal for : to appear at the end of " +
+              errormsg = "Illegal for colon to appear at the end of " +
                 dmsa.substr(beg, end - beg);
               break;
             }
             k = npiece;
           }
           if (k === npiece - 1) {
-            errormsg = "Repeated " + d.components_[k] +
+            errormsg = "Repeated " + components_[k] +
               " component in " + dmsa.substr(beg, end - beg);
             break;
           } else if (k < npiece) {
-            errormsg = d.components_[k] + " component follows " +
-              d.components_[npiece - 1] + " component in " +
+            errormsg = components_[k] + " component follows " +
+              components_[npiece - 1] + " component in " +
               dmsa.substr(beg, end - beg);
             break;
           }
           if (ncurrent === 0) {
-            errormsg = "Missing numbers in " + d.components_[k] +
+            errormsg = "Missing numbers in " + components_[k] +
               " component of " + dmsa.substr(beg, end - beg);
             break;
           }
@@ -192,7 +226,7 @@ GeographicLib.DMS = {};
             icurrent = fcurrent = 0;
             ncurrent = digcount = intcount = 0;
           }
-        } else if (d.lookup(d.signs_, x) >= 0) {
+        } else if (lookup(signs_, x) >= 0) {
           errormsg = "Internal sign in DMS string " +
             dmsa.substr(beg, end - beg);
           break;
@@ -204,7 +238,7 @@ GeographicLib.DMS = {};
       }
       if (errormsg.length)
         break;
-      if (d.lookup(d.dmsindicators_, dmsa.charAt(p - 1)) < 0) {
+      if (lookup(dmsindicators_, dmsa.charAt(p - 1)) < 0) {
         if (npiece >= 3) {
           errormsg = "Extra text following seconds in DMS string " +
             dmsa.substr(beg, end - beg);
@@ -230,11 +264,11 @@ GeographicLib.DMS = {};
       }
       // Note that we accept 59.999999... even though it rounds to 60.
       if (ipieces[1] >= 60 || fpieces[1] > 60) {
-        errormsg = "Minutes " + fpieces[1] + " not in range [0, 60)";
+        errormsg = "Minutes " + fpieces[1] + " not in range [0,60)";
         break;
       }
       if (ipieces[2] >= 60 || fpieces[2] > 60) {
-        errormsg = "Seconds " + fpieces[2] + " not in range [0, 60)";
+        errormsg = "Seconds " + fpieces[2] + " not in range [0,60)";
         break;
       }
       vals.ind = ind1;
@@ -245,7 +279,7 @@ GeographicLib.DMS = {};
           ( fpieces[1] ? (60*fpieces[0] + fpieces[1]) / 60 : fpieces[0] ) );
       return vals;
     } while (false);
-    vals.val = d.NumMatch(dmsa);
+    vals.val = NumMatch(dmsa);
     if (vals.val === 0)
       throw new Error(errormsg);
     else
@@ -253,13 +287,14 @@ GeographicLib.DMS = {};
     return vals;
   };
 
-  d.NumMatch = function(s) {
+  NumMatch = function(s) {
+    var t, sign, p0, p1;
     if (s.length < 3)
       return 0;
-    var t = s.toUpperCase().replace(/0+$/,"");
-    var sign = t.charAt(0) === '-' ? -1 : 1;
-    var p0 = t.charAt(0) === '-' || t.charAt(0) === '+' ? 1 : 0;
-    var p1 = t.length - 1;
+    t = s.toUpperCase().replace(/0+$/,"");
+    sign = t.charAt(0) === '-' ? -1 : 1;
+    p0 = t.charAt(0) === '-' || t.charAt(0) === '+' ? 1 : 0;
+    p1 = t.length - 1;
     if (p1 + 1 < p0 + 3)
       return 0;
     // Strip off sign and trailing 0s
@@ -272,56 +307,101 @@ GeographicLib.DMS = {};
     return 0;
   };
 
-  // return lat, lon
-  d.DecodeLatLon = function(stra, strb, swaplatlong) {
-    var vals = {};
-    if (!swaplatlong) swaplatlong = false;
-    var valsa = d.Decode(stra);
-    var valsb = d.Decode(strb);
-    var a = valsa.val, ia = valsa.ind;
-    var b = valsb.val, ib = valsb.ind;
+  /**
+   * @summary Decode two DMS strings interpreting them as a latitude/longitude
+   *   pair.
+   * @param {string} stra the first string.
+   * @param {string} strb the first string.
+   * @param {bool} [longfirst = false] if true assume then longitude is given
+   *   first (in the absense of any hemisphere indicators).
+   * @returns {object} r where r.lat is the decoded latitude and r.lon is the
+   *   decoded longitude (both in degrees).
+   * @throws an error if the strings are illegal.
+   */
+  d.DecodeLatLon = function(stra, strb, longfirst) {
+    var vals = {},
+        valsa = d.Decode(stra),
+        valsb = d.Decode(strb),
+        a = valsa.val, ia = valsa.ind,
+        b = valsb.val, ib = valsb.ind,
+        lat, lon;
+    if (!longfirst) longfirst = false;
     if (ia === d.NONE && ib === d.NONE) {
-      // Default to lat, long unless swaplatlong
-      ia = swaplatlong ? d.LONGITUDE : d.LATITUDE;
-      ib = swaplatlong ? d.LATITUDE : d.LONGITUDE;
+      // Default to lat, long unless longfirst
+      ia = longfirst ? d.LONGITUDE : d.LATITUDE;
+      ib = longfirst ? d.LATITUDE : d.LONGITUDE;
     } else if (ia === d.NONE)
       ia = d.LATITUDE + d.LONGITUDE - ib;
     else if (ib === d.NONE)
       ib = d.LATITUDE + d.LONGITUDE - ia;
     if (ia === ib)
-      throw new Error("Both " + stra + " and " +
-                      strb + " interpreted as " +
+      throw new Error("Both " + stra + " and " + strb + " interpreted as " +
                       (ia === d.LATITUDE ? "latitudes" : "longitudes"));
-    var lat = ia === d.LATITUDE ? a : b, lon = ia === d.LATITUDE ? b : a;
+    lat = ia === d.LATITUDE ? a : b;
+    lon = ia === d.LATITUDE ? b : a;
     if (Math.abs(lat) > 90)
-      throw new Error("Latitude " + lat + "d not in [-90d, 90d]");
+      throw new Error("Latitude " + lat + " not in [-90,90]");
     vals.lat = lat;
     vals.lon = lon;
     return vals;
   };
 
+  /**
+   * @summary Decode a DMS string interpreting it as an arc length.
+   * @param {string} angstr the string (this must not include a hemisphere
+   *   indicator).
+   * @returns {number} the arc length (degrees).
+   * @throws an error if the string is illegal.
+   */
   d.DecodeAngle = function(angstr) {
-    var vals = d.Decode(angstr);
-    var ang = vals.val, ind = vals.ind;
+    var vals = d.Decode(angstr),
+        ang = vals.val, ind = vals.ind;
     if (ind !== d.NONE)
-      throw new Error("Arc angle " + angstr +
-                      " includes a hemisphere, N/E/W/S");
+      throw new Error("Arc angle " + angstr + " includes a hemisphere N/E/W/S");
     return ang;
   };
 
+  /**
+   * @summary Decode a DMS string interpreting it as an azimuth.
+   * @param {string} azistr the string (this may include an E/W hemisphere
+   *   indicator).
+   * @returns {number} the azimuth (degrees).
+   * @throws an error if the string is illegal.
+   */
   d.DecodeAzimuth = function(azistr) {
-    var vals = d.Decode(azistr);
-    var azi = vals.val, ind = vals.ind;
+    var vals = d.Decode(azistr),
+        azi = vals.val, ind = vals.ind;
     if (ind === d.LATITUDE)
-      throw new Error("Azimuth " + azistr +
-                      " has a latitude hemisphere, N/S");
-    azi = m.AngNormalize(azi);
+      throw new Error("Azimuth " + azistr + " has a latitude hemisphere N/S");
     return azi;
   };
 
+  /**
+   * @summary Convert angle (in degrees) into a DMS string (using °, ',
+   *  and ").
+   * @param {number} angle input angle (degrees).
+   * @param {number} trailing one of DEGREE, MINUTE, or SECOND to indicate
+   *   the trailing component of the string (this component is given as a
+   *   decimal number if necessary).
+   * @param {number} prec the number of digits after the decimal point for
+   *   the trailing component.
+   * @param {number} [ind = NONE] a formatting indicator, one of NONE,
+   *   LATITUDE, LONGITUDE, AZIMUTH.
+   * @returns {string} the resulting string formatted as follows:
+   *   * NONE, signed result no leading zeros on degrees except in the units
+   *     place, e.g., -8°03'.
+   *   * LATITUDE, trailing N or S hemisphere designator, no sign, pad
+   *     degrees to 2 digits, e.g., 08°03'S.
+   *   * LONGITUDE, trailing E or W hemisphere designator, no sign, pad
+   *     degrees to 3 digits, e.g., 008°03'W.
+   *   * AZIMUTH, convert to the range [0, 360°), no sign, pad degrees to
+   *     3 digits, e.g., 351°57'.
+   */
   d.Encode = function(angle, trailing, prec, ind) {
     // Assume check on range of input angle has been made by calling
     // routine (which might be able to offer a better diagnostic).
+    var scale = 1, i, sign,
+        idegree, fdegree, f, pieces, ip, fp, s;
     if (!ind) ind = d.NONE;
     if (!isFinite(angle))
       return angle < 0 ? String("-inf") :
@@ -330,21 +410,19 @@ GeographicLib.DMS = {};
     // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
     // This suffices to give full real precision for numbers in [-90,90]
     prec = Math.min(15 - 2 * trailing, prec);
-    var scale = 1, i;
     for (i = 0; i < trailing; ++i)
       scale *= 60;
     for (i = 0; i < prec; ++i)
       scale *= 10;
     if (ind === d.AZIMUTH)
       angle -= Math.floor(angle/360) * 360;
-    var sign = angle < 0 ? -1 : 1;
+    sign = angle < 0 ? -1 : 1;
     angle *= sign;
 
     // Break off integer part to preserve precision in manipulation of
     // fractional part.
-    var
-    idegree = Math.floor(angle),
-    fdegree = (angle - idegree) * scale + 0.5,
+    idegree = Math.floor(angle);
+    fdegree = (angle - idegree) * scale + 0.5;
     f = Math.floor(fdegree);
     // Implement the "round ties to even" rule
     fdegree = (f == fdegree && (f & 1)) ? f - 1 : f;
@@ -355,47 +433,45 @@ GeographicLib.DMS = {};
       idegree += 1;
       fdegree -= 1;
     }
-    var pieces = [fdegree, 0, 0];
+    pieces = [fdegree, 0, 0];
     for (i = 1; i <= trailing; ++i) {
-      var
-      ip = Math.floor(pieces[i - 1]),
+      ip = Math.floor(pieces[i - 1]);
       fp = pieces[i - 1] - ip;
       pieces[i] = fp * 60;
       pieces[i - 1] = ip;
     }
     pieces[0] += idegree;
-    var s = new String("");
+    s = "";
     if (ind === d.NONE && sign < 0)
       s += '-';
     switch (trailing) {
     case d.DEGREE:
-      s += d.zerofill(pieces[0].toFixed(prec),
-                      ind === d.NONE ? 0 :
-                      1 + Math.min(ind, 2) + prec + (prec ? 1 : 0)) +
-        d.dmsindicatorsu_.charAt(0);
+      s += zerofill(pieces[0].toFixed(prec),
+                    ind === d.NONE ? 0 :
+                    1 + Math.min(ind, 2) + prec + (prec ? 1 : 0)) +
+        dmsindicatorsu_.charAt(0);
       break;
     default:
-      s += d.zerofill(pieces[0].toFixed(0),
-                      ind === d.NONE ? 0 : 1 + Math.min(ind, 2)) +
-        d.dmsindicatorsu_.charAt(0);
+      s += zerofill(pieces[0].toFixed(0),
+                    ind === d.NONE ? 0 : 1 + Math.min(ind, 2)) +
+        dmsindicatorsu_.charAt(0);
       switch (trailing) {
       case d.MINUTE:
-        s += d.zerofill(pieces[1].toFixed(prec), 2 + prec + (prec ? 1 : 0)) +
-          d.dmsindicatorsu_.charAt(1);
+        s += zerofill(pieces[1].toFixed(prec), 2 + prec + (prec ? 1 : 0)) +
+          dmsindicatorsu_.charAt(1);
         break;
       case d.SECOND:
-        s += d.zerofill(pieces[1].toFixed(0), 2) + d.dmsindicatorsu_.charAt(1);
-        s += d.zerofill(pieces[2].toFixed(prec), 2 + prec + (prec ? 1 : 0)) +
-          d.dmsindicatorsu_.charAt(2);
+        s += zerofill(pieces[1].toFixed(0), 2) + dmsindicatorsu_.charAt(1);
+        s += zerofill(pieces[2].toFixed(prec), 2 + prec + (prec ? 1 : 0)) +
+          dmsindicatorsu_.charAt(2);
         break;
       default:
         break;
       }
     }
     if (ind !== d.NONE && ind !== d.AZIMUTH)
-      s += d.hemispheres_.charAt((ind === d.LATITUDE ? 0 : 2) +
-                                 (sign < 0 ? 0 : 1));
+      s += hemispheres_.charAt((ind === d.LATITUDE ? 0 : 2) +
+                               (sign < 0 ? 0 : 1));
     return s;
   };
-
-})();
+})(GeographicLib.DMS);
diff --git a/doc/scripts/GeographicLib/Geodesic.js b/js/src/Geodesic.js
similarity index 56%
rename from doc/scripts/GeographicLib/Geodesic.js
rename to js/src/Geodesic.js
index c99ee0c..927adc7 100644
--- a/doc/scripts/GeographicLib/Geodesic.js
+++ b/js/src/Geodesic.js
@@ -1,4 +1,4 @@
-/**
+/*
  * Geodesic.js
  * Transcription of Geodesic.[ch]pp into JavaScript.
  *
@@ -15,60 +15,72 @@
  * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
  * under the MIT/X11 License.  For more information, see
  * http://geographiclib.sourceforge.net/
- **********************************************************************/
+ */
 
 // Load AFTER Math.js
 
 GeographicLib.Geodesic = {};
 GeographicLib.GeodesicLine = {};
+GeographicLib.PolygonArea = {};
+
+(function(
+  /**
+   * @exports GeographicLib/Geodesic
+   * @description Solve geodesic problems via the
+   *   {@link module:GeographicLib/Geodesic.Geodesic Geodesic} class.
+   */
+  g, l, p, m, c) {
+  "use strict";
+
+  var GEOGRAPHICLIB_GEODESIC_ORDER = 6,
+      nA1_ = GEOGRAPHICLIB_GEODESIC_ORDER,
+      nA2_ = GEOGRAPHICLIB_GEODESIC_ORDER,
+      nA3_ = GEOGRAPHICLIB_GEODESIC_ORDER,
+      nA3x_ = nA3_,
+      nC3x_, nC4x_,
+      maxit1_ = 20,
+      maxit2_ = maxit1_ + m.digits + 10,
+      tol0_ = m.epsilon,
+      tol1_ = 200 * tol0_,
+      tol2_ = Math.sqrt(tol0_),
+      tolb_ = tol0_ * tol1_,
+      xthresh_ = 1000 * tol2_,
+      CAP_NONE = 0,
+      CAP_ALL  = 0x1F,
+      CAP_MASK = CAP_ALL,
+      OUT_ALL  = 0x7F80,
+      Astroid,
+      A1m1f_coeff, C1f_coeff, C1pf_coeff,
+      A2m1f_coeff, C2f_coeff,
+      A3_coeff, C3_coeff, C4_coeff;
 
-(function() {
-  var m = GeographicLib.Math;
-  var g = GeographicLib.Geodesic;
-  var l = GeographicLib.GeodesicLine;
-  g.GEOGRAPHICLIB_GEODESIC_ORDER = 6;
-  g.nA1_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nC1_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nC1p_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nA2_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nC2_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nA3_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nA3x_ = g.nA3_;
-  g.nC3_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nC3x_ = (g.nC3_ * (g.nC3_ - 1)) / 2;
-  g.nC4_ = g.GEOGRAPHICLIB_GEODESIC_ORDER;
-  g.nC4x_ = (g.nC4_ * (g.nC4_ + 1)) / 2;
-  g.maxit1_ = 20;
-  g.maxit2_ = g.maxit1_ + m.digits + 10;
   g.tiny_ = Math.sqrt(Number.MIN_VALUE);
-  g.tol0_ = m.epsilon;
-  g.tol1_ = 200 * g.tol0_;
-  g.tol2_ = Math.sqrt(g.tol0_);
-  g.tolb_ = g.tol0_ * g.tol1_;
-  g.xthresh_ = 1000 * g.tol2_;
-
-  g.CAP_NONE = 0;
+  g.nC1_ = GEOGRAPHICLIB_GEODESIC_ORDER;
+  g.nC1p_ = GEOGRAPHICLIB_GEODESIC_ORDER;
+  g.nC2_ = GEOGRAPHICLIB_GEODESIC_ORDER;
+  g.nC3_ = GEOGRAPHICLIB_GEODESIC_ORDER;
+  g.nC4_ = GEOGRAPHICLIB_GEODESIC_ORDER;
+  nC3x_ = (g.nC3_ * (g.nC3_ - 1)) / 2;
+  nC4x_ = (g.nC4_ * (g.nC4_ + 1)) / 2,
   g.CAP_C1   = 1<<0;
   g.CAP_C1p  = 1<<1;
   g.CAP_C2   = 1<<2;
   g.CAP_C3   = 1<<3;
   g.CAP_C4   = 1<<4;
-  g.CAP_ALL  = 0x1F;
-  g.CAP_MASK = g.CAP_ALL;
-  g.OUT_ALL  = 0x7F80;
-  g.OUT_MASK = 0xFF80;          // Includes LONG_UNROLL
+
   g.NONE          = 0;
-  g.LATITUDE      = 1<<7  | g.CAP_NONE;
+  g.LATITUDE      = 1<<7  | CAP_NONE;
   g.LONGITUDE     = 1<<8  | g.CAP_C3;
-  g.AZIMUTH       = 1<<9  | g.CAP_NONE;
+  g.AZIMUTH       = 1<<9  | CAP_NONE;
   g.DISTANCE      = 1<<10 | g.CAP_C1;
+  g.STANDARD      = g.LATITUDE | g.LONGITUDE | g.AZIMUTH | g.DISTANCE;
   g.DISTANCE_IN   = 1<<11 | g.CAP_C1 | g.CAP_C1p;
   g.REDUCEDLENGTH = 1<<12 | g.CAP_C1 | g.CAP_C2;
   g.GEODESICSCALE = 1<<13 | g.CAP_C1 | g.CAP_C2;
   g.AREA          = 1<<14 | g.CAP_C4;
+  g.ALL           = OUT_ALL| CAP_ALL;
   g.LONG_UNROLL   = 1<<15;
-  g.LONG_NOWRAP   = g.LONG_UNROLL;
-  g.ALL           = g.OUT_ALL| g.CAP_ALL;
+  g.OUT_MASK      = OUT_ALL| g.LONG_UNROLL;
 
   g.SinCosSeries = function(sinp, sinx, cosx, c) {
     // Evaluate
@@ -76,12 +88,10 @@ GeographicLib.GeodesicLine = {};
     //            sum(c[i] * cos((2*i+1) * x), i, 0, n-1)
     // using Clenshaw summation.  N.B. c[0] is unused for sin series
     // Approx operation count = (n + 5) mult and (2 * n + 2) add
-    var
-    k = c.length,               // Point to one beyond last element
-    n = k - (sinp ? 1 : 0);
-    var
-    ar = 2 * (cosx - sinx) * (cosx + sinx), // 2 * cos(2 * x)
-    y0 = n & 1 ? c[--k] : 0, y1 = 0;        // accumulators for sum
+    var k = c.length,           // Point to one beyond last element
+        n = k - (sinp ? 1 : 0),
+        ar = 2 * (cosx - sinx) * (cosx + sinx), // 2 * cos(2 * x)
+        y0 = n & 1 ? c[--k] : 0, y1 = 0;        // accumulators for sum
     // Now n is even
     n = Math.floor(n/2);
     while (n--) {
@@ -93,49 +103,46 @@ GeographicLib.GeodesicLine = {};
             cosx * (y0 - y1));            // cos(x) * (y0 - y1)
   };
 
-  g.Astroid = function(x, y) {
+  Astroid = function(x, y) {
     // Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive
     // root k.  This solution is adapted from Geocentric::Reverse.
-    var k;
-    var
-    p = m.sq(x),
-    q = m.sq(y),
-    r = (p + q - 1) / 6;
+    var k,
+        p = m.sq(x),
+        q = m.sq(y),
+        r = (p + q - 1) / 6,
+        S, r2, r3, disc, u, T3, T, ang, v, uv, w;
     if ( !(q === 0 && r <= 0) ) {
-      var
       // Avoid possible division by zero when r = 0 by multiplying
       // equations for s and t by r^3 and r, resp.
-      S = p * q / 4,            // S = r^3 * s
-      r2 = m.sq(r),
-      r3 = r * r2,
+      S = p * q / 4;            // S = r^3 * s
+      r2 = m.sq(r);
+      r3 = r * r2
       // The discriminant of the quadratic equation for T3.  This is
       // zero on the evolute curve p^(1/3)+q^(1/3) = 1
       disc = S * (S + 2 * r3);
-      var u = r;
+      u = r;
       if (disc >= 0) {
-        var T3 = S + r3;
+        T3 = S + r3;
         // Pick the sign on the sqrt to maximize abs(T3).  This
         // minimizes loss of precision due to cancellation.  The
         // result is unchanged because of the way the T is used
         // in definition of u.
-        T3 += T3 < 0 ? -Math.sqrt(disc) :
-          Math.sqrt(disc);    // T3 = (r * t)^3
+        T3 += T3 < 0 ? -Math.sqrt(disc) : Math.sqrt(disc);    // T3 = (r * t)^3
         // N.B. cbrt always returns the real root.  cbrt(-8) = -2.
-        var T = m.cbrt(T3);     // T = r * t
+        T = m.cbrt(T3);     // T = r * t
         // T can be zero; but then r2 / T -> 0.
         u += T + (T !== 0 ? r2 / T : 0);
       } else {
         // T is complex, but the way u is defined the result is real.
-        var ang = Math.atan2(Math.sqrt(-disc), -(S + r3));
+        ang = Math.atan2(Math.sqrt(-disc), -(S + r3));
         // There are three possible cube roots.  We choose the
         // root which avoids cancellation.  Note that disc < 0
         // implies that r < 0.
         u += 2 * r * Math.cos(ang / 3);
       }
-      var
-      v = Math.sqrt(m.sq(u) + q),       // guaranteed positive
+      v = Math.sqrt(m.sq(u) + q);       // guaranteed positive
       // Avoid loss of accuracy when u < 0.
-      uv = u < 0 ? q / (v - u) : u + v, // u+v, guaranteed positive
+      uv = u < 0 ? q / (v - u) : u + v; // u+v, guaranteed positive
       w = (uv - q) / (2 * v);           // positive?
       // Rearrange expression for k to avoid loss of accuracy due to
       // subtraction.  Division by 0 not possible because uv > 0, w >= 0.
@@ -148,122 +155,151 @@ GeographicLib.GeodesicLine = {};
     return k;
   };
 
+  A1m1f_coeff = [
+    // (1-eps)*A1-1, polynomial in eps2 of order 3
+      +1, 4, 64, 0, 256,
+  ];
+
   // The scale factor A1-1 = mean value of (d/dsigma)I1 - 1
   g.A1m1f = function(eps) {
-    var coeff = [
-      // (1-eps)*A1-1, polynomial in eps2 of order 3
-      1, 4, 64, 0, 256,
-    ];
-    var p = Math.floor(g.nA1_/2);
-    var t = m.polyval(p, coeff, 0, m.sq(eps)) / coeff[p + 1];
+    var p = Math.floor(nA1_/2),
+        t = m.polyval(p, A1m1f_coeff, 0, m.sq(eps)) / A1m1f_coeff[p + 1];
     return (t + eps) / (1 - eps);
   };
 
+  C1f_coeff = [
+    // C1[1]/eps^1, polynomial in eps2 of order 2
+      -1, 6, -16, 32,
+    // C1[2]/eps^2, polynomial in eps2 of order 2
+      -9, 64, -128, 2048,
+    // C1[3]/eps^3, polynomial in eps2 of order 1
+      +9, -16, 768,
+    // C1[4]/eps^4, polynomial in eps2 of order 1
+      +3, -5, 512,
+    // C1[5]/eps^5, polynomial in eps2 of order 0
+      -7, 1280,
+    // C1[6]/eps^6, polynomial in eps2 of order 0
+      -7, 2048,
+  ];
+
   // The coefficients C1[l] in the Fourier expansion of B1
   g.C1f = function(eps, c) {
-    var coeff = [
-      // C1[1]/eps^1, polynomial in eps2 of order 2
-        -1, 6, -16, 32,
-      // C1[2]/eps^2, polynomial in eps2 of order 2
-        -9, 64, -128, 2048,
-      // C1[3]/eps^3, polynomial in eps2 of order 1
-      9, -16, 768,
-      // C1[4]/eps^4, polynomial in eps2 of order 1
-      3, -5, 512,
-      // C1[5]/eps^5, polynomial in eps2 of order 0
-        -7, 1280,
-      // C1[6]/eps^6, polynomial in eps2 of order 0
-        -7, 2048,
-    ];
-    var
-    eps2 = m.sq(eps),
-    d = eps;
-    var o = 0;
-    for (var l = 1; l <= g.nC1_; ++l) {     // l is index of C1p[l]
-      var p = Math.floor((g.nC1_ - l) / 2); // order of polynomial in eps^2
-      c[l] = d * m.polyval(p, coeff, o, eps2) / coeff[o + p + 1];
+    var eps2 = m.sq(eps),
+        d = eps,
+        o = 0,
+        l, p;
+    for (l = 1; l <= g.nC1_; ++l) {     // l is index of C1p[l]
+      p = Math.floor((g.nC1_ - l) / 2); // order of polynomial in eps^2
+      c[l] = d * m.polyval(p, C1f_coeff, o, eps2) / C1f_coeff[o + p + 1];
       o += p + 2;
       d *= eps;
     }
   };
 
+  C1pf_coeff = [
+    // C1p[1]/eps^1, polynomial in eps2 of order 2
+      +205, -432, 768, 1536,
+    // C1p[2]/eps^2, polynomial in eps2 of order 2
+      +4005, -4736, 3840, 12288,
+    // C1p[3]/eps^3, polynomial in eps2 of order 1
+      -225, 116, 384,
+    // C1p[4]/eps^4, polynomial in eps2 of order 1
+      -7173, 2695, 7680,
+    // C1p[5]/eps^5, polynomial in eps2 of order 0
+      +3467, 7680,
+    // C1p[6]/eps^6, polynomial in eps2 of order 0
+      +38081, 61440,
+  ];
+
   // The coefficients C1p[l] in the Fourier expansion of B1p
   g.C1pf = function(eps, c) {
-    var coeff = [
-      // C1p[1]/eps^1, polynomial in eps2 of order 2
-      205, -432, 768, 1536,
-      // C1p[2]/eps^2, polynomial in eps2 of order 2
-      4005, -4736, 3840, 12288,
-      // C1p[3]/eps^3, polynomial in eps2 of order 1
-        -225, 116, 384,
-      // C1p[4]/eps^4, polynomial in eps2 of order 1
-        -7173, 2695, 7680,
-      // C1p[5]/eps^5, polynomial in eps2 of order 0
-      3467, 7680,
-      // C1p[6]/eps^6, polynomial in eps2 of order 0
-      38081, 61440,
-    ];
-    var
-    eps2 = m.sq(eps),
-    d = eps;
-    var o = 0;
-    for (var l = 1; l <= g.nC1p_; ++l) {     // l is index of C1p[l]
-      var p = Math.floor((g.nC1p_ - l) / 2); // order of polynomial in eps^2
-      c[l] = d * m.polyval(p, coeff, o, eps2) / coeff[o + p + 1];
+    var eps2 = m.sq(eps),
+        d = eps,
+        o = 0,
+        l, p;
+    for (l = 1; l <= g.nC1p_; ++l) {     // l is index of C1p[l]
+      p = Math.floor((g.nC1p_ - l) / 2); // order of polynomial in eps^2
+      c[l] = d * m.polyval(p, C1pf_coeff, o, eps2) / C1pf_coeff[o + p + 1];
       o += p + 2;
       d *= eps;
     }
   };
 
+  A2m1f_coeff = [
+    // (eps+1)*A2-1, polynomial in eps2 of order 3
+      -11, -28, -192, 0, 256,
+  ];
+
   // The scale factor A2-1 = mean value of (d/dsigma)I2 - 1
   g.A2m1f = function(eps) {
-    var coeff = [
-      // (eps+1)*A2-1, polynomial in eps2 of order 3
-      -11, -28, -192, 0, 256,
-    ];
-    var p = Math.floor(g.nA2_/2);
-    var t = m.polyval(p, coeff, 0, m.sq(eps)) / coeff[p + 1];
+    var p = Math.floor(nA2_/2),
+        t = m.polyval(p, A2m1f_coeff, 0, m.sq(eps)) / A2m1f_coeff[p + 1];
     return (t - eps) / (1 + eps);
   };
 
+  C2f_coeff = [
+    // C2[1]/eps^1, polynomial in eps2 of order 2
+      +1, 2, 16, 32,
+    // C2[2]/eps^2, polynomial in eps2 of order 2
+      +35, 64, 384, 2048,
+    // C2[3]/eps^3, polynomial in eps2 of order 1
+      +15, 80, 768,
+    // C2[4]/eps^4, polynomial in eps2 of order 1
+      +7, 35, 512,
+    // C2[5]/eps^5, polynomial in eps2 of order 0
+      +63, 1280,
+    // C2[6]/eps^6, polynomial in eps2 of order 0
+      +77, 2048,
+  ];
+
   // The coefficients C2[l] in the Fourier expansion of B2
   g.C2f = function(eps, c) {
-    var coeff = [
-      // C2[1]/eps^1, polynomial in eps2 of order 2
-      1, 2, 16, 32,
-      // C2[2]/eps^2, polynomial in eps2 of order 2
-      35, 64, 384, 2048,
-      // C2[3]/eps^3, polynomial in eps2 of order 1
-      15, 80, 768,
-      // C2[4]/eps^4, polynomial in eps2 of order 1
-      7, 35, 512,
-      // C2[5]/eps^5, polynomial in eps2 of order 0
-      63, 1280,
-      // C2[6]/eps^6, polynomial in eps2 of order 0
-      77, 2048,
-    ];
-    var
-    eps2 = m.sq(eps),
-    d = eps;
-    var o = 0;
-    for (var l = 1; l <= g.nC2_; ++l) {     // l is index of C2[l]
-      var p = Math.floor((g.nC2_ - l) / 2); // order of polynomial in eps^2
-      c[l] = d * m.polyval(p, coeff, o, eps2) / coeff[o + p + 1];
+    var eps2 = m.sq(eps),
+        d = eps,
+        o = 0,
+        l, p;
+    for (l = 1; l <= g.nC2_; ++l) {     // l is index of C2[l]
+      p = Math.floor((g.nC2_ - l) / 2); // order of polynomial in eps^2
+      c[l] = d * m.polyval(p, C2f_coeff, o, eps2) / C2f_coeff[o + p + 1];
       o += p + 2;
       d *= eps;
     }
   };
 
+  /**
+   * @class
+   * @property {number} a the equatorial radius (meters).
+   * @property {number} f the flattening.
+   * @summary Initialize a Geodesic object for a specific ellipsoid.
+   * @classdesc Performs geodesic calculations on an ellipsoid of revolution.
+   *   The routines for solving the direct and inverse problems return an
+   *   object with some of the following fields set: lat1, lon1, azi1, lat2,
+   *   lon2, azi2, s12, a12, m12, M12, M21, S12.  See {@tutorial 2-interface},
+   *   "The results".
+   * @example
+   * var GeographicLib = require("geographiclib"),
+   *     geod = GeographicLib.Geodesic.WGS84;
+   * var inv = geod.Inverse(1,2,3,4);
+   * console.log("lat1 = " + inv.lat1 + ", lon1 = " + inv.lon1 +
+   *             ", lat2 = " + inv.lat2 + ", lon2 = " + inv.lon2 +
+   *             ",\nazi1 = " + inv.azi1 + ", azi2 = " + inv.azi2 +
+   *             ", s12 = " + inv.s12);
+   * @param {number} a the equatorial radius of the ellipsoid (meters).
+   * @param {number} f the flattening of the ellipsoid.  Setting f = 0 gives
+   *   a sphere (on which geodesics are great circles).  Negative f gives a
+   *   prolate ellipsoid.
+   * @throws an error if the parameters are illegal.
+   */
   g.Geodesic = function(a, f) {
-    this._a = a;
-    this._f = f <= 1 ? f : 1/f;
-    this._f1 = 1 - this._f;
-    this._e2 = this._f * (2 - this._f);
+    this.a = a;
+    this.f = f;
+    this._f1 = 1 - this.f;
+    this._e2 = this.f * (2 - this.f);
     this._ep2 = this._e2 / m.sq(this._f1); // e2 / (1 - e2)
-    this._n = this._f / ( 2 - this._f);
-    this._b = this._a * this._f1;
+    this._n = this.f / ( 2 - this.f);
+    this._b = this.a * this._f1;
     // authalic radius squared
-    this._c2 = (m.sq(this._a) + m.sq(this._b) *
+    this._c2 = (m.sq(this.a) + m.sq(this._b) *
                 (this._e2 === 0 ? 1 :
                  (this._e2 > 0 ? m.atanh(Math.sqrt(this._e2)) :
                   Math.atan(Math.sqrt(-this._e2))) /
@@ -277,139 +313,148 @@ GeographicLib.GeodesicLine = {};
     // by a factor of 2.)  Setting this equal to epsilon gives sig12 = etol2.
     // Here 0.1 is a safety factor (error decreased by 100) and max(0.001,
     // abs(f)) stops etol2 getting too large in the nearly spherical case.
-    this._etol2 = 0.1 * g.tol2_ /
-      Math.sqrt( Math.max(0.001, Math.abs(this._f)) *
-                 Math.min(1.0, 1 - this._f/2) / 2 );
-    if (!(isFinite(this._a) && this._a > 0))
+    this._etol2 = 0.1 * tol2_ /
+      Math.sqrt( Math.max(0.001, Math.abs(this.f)) *
+                 Math.min(1.0, 1 - this.f/2) / 2 );
+    if (!(isFinite(this.a) && this.a > 0))
       throw new Error("Major radius is not positive");
     if (!(isFinite(this._b) && this._b > 0))
       throw new Error("Minor radius is not positive");
-    this._A3x = new Array(g.nA3x_);
-    this._C3x = new Array(g.nC3x_);
-    this._C4x = new Array(g.nC4x_);
+    this._A3x = new Array(nA3x_);
+    this._C3x = new Array(nC3x_);
+    this._C4x = new Array(nC4x_);
     this.A3coeff();
     this.C3coeff();
     this.C4coeff();
   };
 
+  A3_coeff = [
+    // A3, coeff of eps^5, polynomial in n of order 0
+      -3, 128,
+    // A3, coeff of eps^4, polynomial in n of order 1
+      -2, -3, 64,
+    // A3, coeff of eps^3, polynomial in n of order 2
+      -1, -3, -1, 16,
+    // A3, coeff of eps^2, polynomial in n of order 2
+      +3, -1, -2, 8,
+    // A3, coeff of eps^1, polynomial in n of order 1
+      +1, -1, 2,
+    // A3, coeff of eps^0, polynomial in n of order 0
+      +1, 1,
+  ];
+
   // The scale factor A3 = mean value of (d/dsigma)I3
   g.Geodesic.prototype.A3coeff = function() {
-    var coeff = [
-      // A3, coeff of eps^5, polynomial in n of order 0
-        -3, 128,
-      // A3, coeff of eps^4, polynomial in n of order 1
-        -2, -3, 64,
-      // A3, coeff of eps^3, polynomial in n of order 2
-        -1, -3, -1, 16,
-      // A3, coeff of eps^2, polynomial in n of order 2
-      3, -1, -2, 8,
-      // A3, coeff of eps^1, polynomial in n of order 1
-      1, -1, 2,
-      // A3, coeff of eps^0, polynomial in n of order 0
-      1, 1,
-    ];
-    var o = 0, k = 0;
-    for (var j = g.nA3_ - 1; j >= 0; --j) { // coeff of eps^j
-      var p = Math.min(g.nA3_ - j - 1, j);  // order of polynomial in n
-      this._A3x[k++] = m.polyval(p, coeff, o, this._n) / coeff[o + p + 1];
+    var o = 0, k = 0,
+        j, p;
+    for (j = nA3_ - 1; j >= 0; --j) { // coeff of eps^j
+      p = Math.min(nA3_ - j - 1, j);  // order of polynomial in n
+      this._A3x[k++] = m.polyval(p, A3_coeff, o, this._n)
+        / A3_coeff[o + p + 1];
       o += p + 2;
     }
   };
 
+  C3_coeff = [
+    // C3[1], coeff of eps^5, polynomial in n of order 0
+      +3, 128,
+    // C3[1], coeff of eps^4, polynomial in n of order 1
+      +2, 5, 128,
+    // C3[1], coeff of eps^3, polynomial in n of order 2
+      -1, 3, 3, 64,
+    // C3[1], coeff of eps^2, polynomial in n of order 2
+      -1, 0, 1, 8,
+    // C3[1], coeff of eps^1, polynomial in n of order 1
+      -1, 1, 4,
+    // C3[2], coeff of eps^5, polynomial in n of order 0
+      +5, 256,
+    // C3[2], coeff of eps^4, polynomial in n of order 1
+      +1, 3, 128,
+    // C3[2], coeff of eps^3, polynomial in n of order 2
+      -3, -2, 3, 64,
+    // C3[2], coeff of eps^2, polynomial in n of order 2
+      +1, -3, 2, 32,
+    // C3[3], coeff of eps^5, polynomial in n of order 0
+      +7, 512,
+    // C3[3], coeff of eps^4, polynomial in n of order 1
+      -10, 9, 384,
+    // C3[3], coeff of eps^3, polynomial in n of order 2
+      +5, -9, 5, 192,
+    // C3[4], coeff of eps^5, polynomial in n of order 0
+      +7, 512,
+    // C3[4], coeff of eps^4, polynomial in n of order 1
+      -14, 7, 512,
+    // C3[5], coeff of eps^5, polynomial in n of order 0
+      +21, 2560,
+  ];
+
   // The coefficients C3[l] in the Fourier expansion of B3
   g.Geodesic.prototype.C3coeff = function() {
-    var coeff = [
-      // C3[1], coeff of eps^5, polynomial in n of order 0
-      3, 128,
-      // C3[1], coeff of eps^4, polynomial in n of order 1
-      2, 5, 128,
-      // C3[1], coeff of eps^3, polynomial in n of order 2
-        -1, 3, 3, 64,
-      // C3[1], coeff of eps^2, polynomial in n of order 2
-        -1, 0, 1, 8,
-      // C3[1], coeff of eps^1, polynomial in n of order 1
-        -1, 1, 4,
-      // C3[2], coeff of eps^5, polynomial in n of order 0
-      5, 256,
-      // C3[2], coeff of eps^4, polynomial in n of order 1
-      1, 3, 128,
-      // C3[2], coeff of eps^3, polynomial in n of order 2
-        -3, -2, 3, 64,
-      // C3[2], coeff of eps^2, polynomial in n of order 2
-      1, -3, 2, 32,
-      // C3[3], coeff of eps^5, polynomial in n of order 0
-      7, 512,
-      // C3[3], coeff of eps^4, polynomial in n of order 1
-        -10, 9, 384,
-      // C3[3], coeff of eps^3, polynomial in n of order 2
-      5, -9, 5, 192,
-      // C3[4], coeff of eps^5, polynomial in n of order 0
-      7, 512,
-      // C3[4], coeff of eps^4, polynomial in n of order 1
-        -14, 7, 512,
-      // C3[5], coeff of eps^5, polynomial in n of order 0
-      21, 2560,
-    ];
-    var o = 0, k = 0;
-    for (var l = 1; l < g.nC3_; ++l) {        // l is index of C3[l]
-      for (var j = g.nC3_ - 1; j >= l; --j) { // coeff of eps^j
-        var p = Math.min(g.nC3_ - j - 1, j);  // order of polynomial in n
-        this._C3x[k++] = m.polyval(p, coeff, o, this._n) / coeff[o + p + 1];
+    var o = 0, k = 0,
+        l, j, p;
+    for (l = 1; l < g.nC3_; ++l) {        // l is index of C3[l]
+      for (j = g.nC3_ - 1; j >= l; --j) { // coeff of eps^j
+        p = Math.min(g.nC3_ - j - 1, j);  // order of polynomial in n
+        this._C3x[k++] = m.polyval(p, C3_coeff, o, this._n) /
+          C3_coeff[o + p + 1];
         o += p + 2;
       }
     }
   };
 
+  C4_coeff = [
+    // C4[0], coeff of eps^5, polynomial in n of order 0
+      +97, 15015,
+    // C4[0], coeff of eps^4, polynomial in n of order 1
+      +1088, 156, 45045,
+    // C4[0], coeff of eps^3, polynomial in n of order 2
+      -224, -4784, 1573, 45045,
+    // C4[0], coeff of eps^2, polynomial in n of order 3
+      -10656, 14144, -4576, -858, 45045,
+    // C4[0], coeff of eps^1, polynomial in n of order 4
+      +64, 624, -4576, 6864, -3003, 15015,
+    // C4[0], coeff of eps^0, polynomial in n of order 5
+      +100, 208, 572, 3432, -12012, 30030, 45045,
+    // C4[1], coeff of eps^5, polynomial in n of order 0
+      +1, 9009,
+    // C4[1], coeff of eps^4, polynomial in n of order 1
+      -2944, 468, 135135,
+    // C4[1], coeff of eps^3, polynomial in n of order 2
+      +5792, 1040, -1287, 135135,
+    // C4[1], coeff of eps^2, polynomial in n of order 3
+      +5952, -11648, 9152, -2574, 135135,
+    // C4[1], coeff of eps^1, polynomial in n of order 4
+      -64, -624, 4576, -6864, 3003, 135135,
+    // C4[2], coeff of eps^5, polynomial in n of order 0
+      +8, 10725,
+    // C4[2], coeff of eps^4, polynomial in n of order 1
+      +1856, -936, 225225,
+    // C4[2], coeff of eps^3, polynomial in n of order 2
+      -8448, 4992, -1144, 225225,
+    // C4[2], coeff of eps^2, polynomial in n of order 3
+      -1440, 4160, -4576, 1716, 225225,
+    // C4[3], coeff of eps^5, polynomial in n of order 0
+      -136, 63063,
+    // C4[3], coeff of eps^4, polynomial in n of order 1
+      +1024, -208, 105105,
+    // C4[3], coeff of eps^3, polynomial in n of order 2
+      +3584, -3328, 1144, 315315,
+    // C4[4], coeff of eps^5, polynomial in n of order 0
+      -128, 135135,
+    // C4[4], coeff of eps^4, polynomial in n of order 1
+      -2560, 832, 405405,
+    // C4[5], coeff of eps^5, polynomial in n of order 0
+      +128, 99099,
+  ];
+
   g.Geodesic.prototype.C4coeff = function() {
-    var coeff = [
-      // C4[0], coeff of eps^5, polynomial in n of order 0
-      97, 15015,
-      // C4[0], coeff of eps^4, polynomial in n of order 1
-      1088, 156, 45045,
-      // C4[0], coeff of eps^3, polynomial in n of order 2
-        -224, -4784, 1573, 45045,
-      // C4[0], coeff of eps^2, polynomial in n of order 3
-        -10656, 14144, -4576, -858, 45045,
-      // C4[0], coeff of eps^1, polynomial in n of order 4
-      64, 624, -4576, 6864, -3003, 15015,
-      // C4[0], coeff of eps^0, polynomial in n of order 5
-      100, 208, 572, 3432, -12012, 30030, 45045,
-      // C4[1], coeff of eps^5, polynomial in n of order 0
-      1, 9009,
-      // C4[1], coeff of eps^4, polynomial in n of order 1
-        -2944, 468, 135135,
-      // C4[1], coeff of eps^3, polynomial in n of order 2
-      5792, 1040, -1287, 135135,
-      // C4[1], coeff of eps^2, polynomial in n of order 3
-      5952, -11648, 9152, -2574, 135135,
-      // C4[1], coeff of eps^1, polynomial in n of order 4
-        -64, -624, 4576, -6864, 3003, 135135,
-      // C4[2], coeff of eps^5, polynomial in n of order 0
-      8, 10725,
-      // C4[2], coeff of eps^4, polynomial in n of order 1
-      1856, -936, 225225,
-      // C4[2], coeff of eps^3, polynomial in n of order 2
-        -8448, 4992, -1144, 225225,
-      // C4[2], coeff of eps^2, polynomial in n of order 3
-        -1440, 4160, -4576, 1716, 225225,
-      // C4[3], coeff of eps^5, polynomial in n of order 0
-        -136, 63063,
-      // C4[3], coeff of eps^4, polynomial in n of order 1
-      1024, -208, 105105,
-      // C4[3], coeff of eps^3, polynomial in n of order 2
-      3584, -3328, 1144, 315315,
-      // C4[4], coeff of eps^5, polynomial in n of order 0
-        -128, 135135,
-      // C4[4], coeff of eps^4, polynomial in n of order 1
-        -2560, 832, 405405,
-      // C4[5], coeff of eps^5, polynomial in n of order 0
-      128, 99099,
-    ];
-    var o = 0, k = 0;
-    for (var l = 0; l < g.nC4_; ++l) {        // l is index of C4[l]
-      for (var j = g.nC4_ - 1; j >= l; --j) { // coeff of eps^j
-        var p = g.nC4_ - j - 1;               // order of polynomial in n
-        this._C4x[k++] = m.polyval(p, coeff, o, this._n) / coeff[o + p + 1];
+    var o = 0, k = 0,
+        l, j, p;
+    for (l = 0; l < g.nC4_; ++l) {        // l is index of C4[l]
+      for (j = g.nC4_ - 1; j >= l; --j) { // coeff of eps^j
+        p = g.nC4_ - j - 1;               // order of polynomial in n
+        this._C4x[k++] = m.polyval(p, C4_coeff, o, this._n)
+          / C4_coeff[o + p + 1];
         o += p + 2;
       }
     }
@@ -417,16 +462,17 @@ GeographicLib.GeodesicLine = {};
 
   g.Geodesic.prototype.A3f = function(eps) {
     // Evaluate A3
-    return m.polyval(g.nA3x_ - 1, this._A3x, 0, eps);
+    return m.polyval(nA3x_ - 1, this._A3x, 0, eps);
   };
 
   g.Geodesic.prototype.C3f = function(eps, c) {
     // Evaluate C3 coeffs
     // Elements c[1] thru c[nC3_ - 1] are set
-    var mult = 1;
-    var o = 0;
-    for (var l = 1; l < g.nC3_; ++l) { // l is index of C3[l]
-      var p = g.nC3_ - l - 1;          // order of polynomial in eps
+    var mult = 1,
+        o = 0,
+        l, p;
+    for (l = 1; l < g.nC3_; ++l) { // l is index of C3[l]
+      p = g.nC3_ - l - 1;          // order of polynomial in eps
       mult *= eps;
       c[l] = mult * m.polyval(p, this._C3x, o, eps);
       o += p + 1;
@@ -435,11 +481,12 @@ GeographicLib.GeodesicLine = {};
 
   g.Geodesic.prototype.C4f = function(eps, c) {
     // Evaluate C4 coeffs
-    // Elements c[0] thru c[nC4_ - 1] are set
-    var mult = 1;
-    var o = 0;
-    for (var l = 0; l < g.nC4_; ++l) { // l is index of C4[l]
-      var p = g.nC4_ - l - 1;          // order of polynomial in eps
+    // Elements c[0] thru c[g.nC4_ - 1] are set
+    var mult = 1,
+        o = 0,
+        l, p;
+    for (l = 0; l < g.nC4_; ++l) { // l is index of C4[l]
+      p = g.nC4_ - l - 1;          // order of polynomial in eps
       c[l] = mult * m.polyval(p, this._C4x, o, eps);
       o += p + 1;
       mult *= eps;
@@ -455,9 +502,9 @@ GeographicLib.GeodesicLine = {};
     // distance/_b, and m0 = coefficient of secular term in
     // expression for reduced length.
     outmask &= g.OUT_MASK;
-    var vals = {};
-
-    var m0x = 0, J12 = 0, A1 = 0, A2 = 0;
+    var vals = {},
+        m0x = 0, J12 = 0, A1 = 0, A2 = 0,
+        B1, B2, l, csig12, t;
     if (outmask & (g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE)) {
       A1 = g.A1m1f(eps);
       g.C1f(eps, C1a);
@@ -470,18 +517,18 @@ GeographicLib.GeodesicLine = {};
       A1 = 1 + A1;
     }
     if (outmask & g.DISTANCE) {
-      var B1 = g.SinCosSeries(true, ssig2, csig2, C1a) -
+      B1 = g.SinCosSeries(true, ssig2, csig2, C1a) -
         g.SinCosSeries(true, ssig1, csig1, C1a);
       // Missing a factor of _b
       vals.s12b = A1 * (sig12 + B1);
       if (outmask & (g.REDUCEDLENGTH | g.GEODESICSCALE)) {
-        var B2 = g.SinCosSeries(true, ssig2, csig2, C2a) -
+        B2 = g.SinCosSeries(true, ssig2, csig2, C2a) -
           g.SinCosSeries(true, ssig1, csig1, C2a);
         J12 = m0x * sig12 + (A1 * B1 - A2 * B2);
       }
     } else if (outmask & (g.REDUCEDLENGTH | g.GEODESICSCALE)) {
       // Assume here that nC1_ >= nC2_
-      for (var l = 1; l <= g.nC2_; ++l)
+      for (l = 1; l <= g.nC2_; ++l)
         C2a[l] = A1 * C1a[l] - A2 * C2a[l];
       J12 = m0x * sig12 + (g.SinCosSeries(true, ssig2, csig2, C2a) -
                            g.SinCosSeries(true, ssig1, csig1, C2a));
@@ -495,8 +542,8 @@ GeographicLib.GeodesicLine = {};
         csig1 * csig2 * J12;
     }
     if (outmask & g.GEODESICSCALE) {
-      var csig12 = csig1 * csig2 + ssig1 * ssig2;
-      var t = this._ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
+      csig12 = csig1 * csig2 + ssig1 * ssig2;
+      t = this._ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
       vals.M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1;
       vals.M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2;
     }
@@ -511,39 +558,40 @@ GeographicLib.GeodesicLine = {};
     // (function value is -1).  If Newton's method doesn't need to be
     // used, return also salp2 and calp2 and function value is sig12.
     // salp2, calp2 only updated if return val >= 0.
-    var
-    vals = {},
-    // bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0]
-    sbet12 = sbet2 * cbet1 - cbet2 * sbet1,
-    cbet12 = cbet2 * cbet1 + sbet2 * sbet1;
+    var vals = {},
+        // bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0]
+        sbet12 = sbet2 * cbet1 - cbet2 * sbet1,
+        cbet12 = cbet2 * cbet1 + sbet2 * sbet1,
+        sbet12a, shortline, omg12, sbetm2, somg12, comg12, t, ssig12, csig12,
+        x, y, lamscale, betscale, k2, eps, cbet12a, bet12a, m12b, m0, nvals,
+        k, omg12a;
     vals.sig12 = -1;        // Return value
     // Volatile declaration needed to fix inverse cases
     // 88.202499451857 0 -88.202499451857 179.981022032992859592
     // 89.262080389218 0 -89.262080389218 179.992207982775375662
     // 89.333123580033 0 -89.333123580032997687 179.99295812360148422
     // which otherwise fail with g++ 4.4.4 x86 -O3
-    var sbet12a = sbet2 * cbet1;
+    sbet12a = sbet2 * cbet1;
     sbet12a += cbet2 * sbet1;
 
-    var shortline = cbet12 >= 0 && sbet12 < 0.5 && cbet2 * lam12 < 0.5;
-    var omg12 = lam12;
+    shortline = cbet12 >= 0 && sbet12 < 0.5 && cbet2 * lam12 < 0.5;
+    omg12 = lam12;
     if (shortline) {
-      var sbetm2 = m.sq(sbet1 + sbet2);
+      sbetm2 = m.sq(sbet1 + sbet2);
       // sin((bet1+bet2)/2)^2
       // =  (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2)
       sbetm2 /= sbetm2 + m.sq(cbet1 + cbet2);
       vals.dnm = Math.sqrt(1 + this._ep2 * sbetm2);
       omg12 /= this._f1 * vals.dnm;
     }
-    var somg12 = Math.sin(omg12), comg12 = Math.cos(omg12);
+    somg12 = Math.sin(omg12); comg12 = Math.cos(omg12);
 
     vals.salp1 = cbet2 * somg12;
     vals.calp1 = comg12 >= 0 ?
       sbet12 + cbet2 * sbet1 * m.sq(somg12) / (1 + comg12) :
       sbet12a - cbet2 * sbet1 * m.sq(somg12) / (1 - comg12);
 
-    var t,
-    ssig12 = m.hypot(vals.salp1, vals.calp1),
+    ssig12 = m.hypot(vals.salp1, vals.calp1);
     csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;
 
     if (shortline && ssig12 < this._etol2) {
@@ -558,59 +606,51 @@ GeographicLib.GeodesicLine = {};
     } else if (Math.abs(this._n) > 0.1 || // Skip astroid calc if too eccentric
                csig12 >= 0 ||
                ssig12 >= 6 * Math.abs(this._n) * Math.PI * m.sq(cbet1)) {
-      0; // Nothing to do, zeroth order spherical approximation is OK
+      // Nothing to do, zeroth order spherical approximation is OK
     } else {
       // Scale lam12 and bet2 to x, y coordinate system where antipodal
       // point is at origin and singular point is at y = 0, x = -1.
-      var y, lamscale, betscale;
-      // Volatile declaration needed to fix inverse case
-      // 56.320923501171 0 -56.320923501171 179.664747671772880215
-      // which otherwise fails with g++ 4.4.4 x86 -O3
-      var x;
-      if (this._f >= 0) {       // In fact f == 0 does not get here
+      if (this.f >= 0) {       // In fact f == 0 does not get here
         // x = dlong, y = dlat
-        var
-        k2 = m.sq(sbet1) * this._ep2,
+        k2 = m.sq(sbet1) * this._ep2;
         eps = k2 / (2 * (1 + Math.sqrt(1 + k2)) + k2);
-        lamscale = this._f * cbet1 * this.A3f(eps) * Math.PI;
+        lamscale = this.f * cbet1 * this.A3f(eps) * Math.PI;
         betscale = lamscale * cbet1;
 
         x = (lam12 - Math.PI) / lamscale;
         y = sbet12a / betscale;
-      } else {                  // _f < 0
+      } else {                  // f < 0
         // x = dlat, y = dlong
-        var
-        cbet12a = cbet2 * cbet1 - sbet2 * sbet1,
+        cbet12a = cbet2 * cbet1 - sbet2 * sbet1;
         bet12a = Math.atan2(sbet12a, cbet12a);
-        var m12b, m0;
         // In the case of lon12 = 180, this repeats a calculation made
         // in Inverse.
-        var nvals = this.Lengths(this._n, Math.PI + bet12a,
-                                 sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
-                                 cbet1, cbet2, g.REDUCEDLENGTH, C1a, C2a);
+        nvals = this.Lengths(this._n, Math.PI + bet12a,
+                             sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
+                             cbet1, cbet2, g.REDUCEDLENGTH, C1a, C2a);
         m12b = nvals.m12b; m0 = nvals.m0;
         x = -1 + m12b / (cbet1 * cbet2 * m0 * Math.PI);
         betscale = x < -0.01 ? sbet12a / x :
-          -this._f * m.sq(cbet1) * Math.PI;
+          -this.f * m.sq(cbet1) * Math.PI;
         lamscale = betscale / cbet1;
         y = (lam12 - Math.PI) / lamscale;
       }
 
-      if (y > -g.tol1_ && x > -1 - g.xthresh_) {
+      if (y > -tol1_ && x > -1 - xthresh_) {
         // strip near cut
-        if (this._f >= 0) {
+        if (this.f >= 0) {
           vals.salp1 = Math.min(1, -x);
           vals.calp1 = - Math.sqrt(1 - m.sq(vals.salp1));
         } else {
-          vals.calp1 = Math.max(x > -g.tol1_ ? 0 : -1, x);
+          vals.calp1 = Math.max(x > -tol1_ ? 0 : -1, x);
           vals.salp1 = Math.sqrt(1 - m.sq(vals.calp1));
         }
       } else {
         // Estimate alp1, by solving the astroid problem.
         //
         // Could estimate alpha1 = theta + pi/2, directly, i.e.,
-        //   calp1 = y/k; salp1 = -x/(1+k);  for _f >= 0
-        //   calp1 = x/(1+k); salp1 = -y/k;  for _f < 0 (need to check)
+        //   calp1 = y/k; salp1 = -x/(1+k);  for f >= 0
+        //   calp1 = x/(1+k); salp1 = -y/k;  for f < 0 (need to check)
         //
         // However, it's better to estimate omg12 from astroid and use
         // spherical formula to compute alp1.  This reduces the mean number of
@@ -640,9 +680,8 @@ GeographicLib.GeodesicLine = {};
         //    6    56      0
         //
         // Because omg12 is near pi, estimate work with omg12a = pi - omg12
-        var k = g.Astroid(x, y);
-        var
-        omg12a = lamscale * ( this._f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k );
+        k = Astroid(x, y);
+        omg12a = lamscale * ( this.f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k );
         somg12 = Math.sin(omg12a); comg12 = -Math.cos(omg12a);
         // Update spherical estimate of alp1 using omg12 instead of
         // lam12
@@ -666,18 +705,18 @@ GeographicLib.GeodesicLine = {};
   g.Geodesic.prototype.Lambda12 = function(sbet1, cbet1, dn1, sbet2, cbet2, dn2,
                                            salp1, calp1, diffp,
                                            C1a, C2a, C3a) {
-    var vals = {};
+    var vals = {},
+        t, salp0, calp0,
+        somg1, comg1, somg2, comg2, omg12, B312, h0, k2, nvals;
     if (sbet1 === 0 && calp1 === 0)
       // Break degeneracy of equatorial line.  This case has already been
       // handled.
       calp1 = -g.tiny_;
 
-    var t,
     // sin(alp1) * cos(bet1) = sin(alp0)
-    salp0 = salp1 * cbet1,
+    salp0 = salp1 * cbet1;
     calp0 = m.hypot(calp1, salp1 * sbet1); // calp0 > 0
 
-    var somg1, comg1, somg2, comg2, omg12;
     // tan(bet1) = tan(sig1) * cos(alp1)
     // tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1)
     vals.ssig1 = sbet1; somg1 = salp0 * sbet1;
@@ -709,20 +748,19 @@ GeographicLib.GeodesicLine = {};
     // norm(somg2, comg2); -- don't need to normalize!
 
     // sig12 = sig2 - sig1, limit to [0, pi]
-    vals.sig12 = Math.atan2(Math.max(vals.csig1 * vals.ssig2 -
-                                     vals.ssig1 * vals.csig2, 0),
+    vals.sig12 = Math.atan2(Math.max(0, vals.csig1 * vals.ssig2 -
+                                     vals.ssig1 * vals.csig2),
                             vals.csig1 * vals.csig2 + vals.ssig1 * vals.ssig2);
 
     // omg12 = omg2 - omg1, limit to [0, pi]
-    omg12 = Math.atan2(Math.max(comg1 * somg2 - somg1 * comg2, 0),
+    omg12 = Math.atan2(Math.max(0, comg1 * somg2 - somg1 * comg2),
                        comg1 * comg2 + somg1 * somg2);
-    var B312, h0;
-    var k2 = m.sq(calp0) * this._ep2;
+    k2 = m.sq(calp0) * this._ep2;
     vals.eps = k2 / (2 * (1 + Math.sqrt(1 + k2)) + k2);
     this.C3f(vals.eps, C3a);
     B312 = (g.SinCosSeries(true, vals.ssig2, vals.csig2, C3a) -
             g.SinCosSeries(true, vals.ssig1, vals.csig1, C3a));
-    h0 = -this._f * this.A3f(vals.eps);
+    h0 = -this.f * this.A3f(vals.eps);
     vals.domg12 = salp0 * h0 * (vals.sig12 + B312);
     vals.lam12 = omg12 + vals.domg12;
 
@@ -730,10 +768,10 @@ GeographicLib.GeodesicLine = {};
       if (vals.calp2 === 0)
         vals.dlam12 = - 2 * this._f1 * dn1 / sbet1;
       else {
-        var nvals = this.Lengths(vals.eps, vals.sig12,
-                                 vals.ssig1, vals.csig1, dn1,
-                                 vals.ssig2, vals.csig2, dn2,
-                                 cbet1, cbet2, g.REDUCEDLENGTH, C1a, C2a);
+        nvals = this.Lengths(vals.eps, vals.sig12,
+                             vals.ssig1, vals.csig1, dn1,
+                             vals.ssig2, vals.csig2, dn2,
+                             cbet1, cbet2, g.REDUCEDLENGTH, C1a, C2a);
         vals.dlam12 = nvals.m12b;
         vals.dlam12 *= this._f1 / (vals.calp2 * cbet2);
       }
@@ -741,15 +779,37 @@ GeographicLib.GeodesicLine = {};
     return vals;
   };
 
-  // return a12, s12, azi1, azi2, m12, M12, M21, S12
-  g.Geodesic.prototype.GenInverse = function(lat1, lon1, lat2, lon2, outmask) {
-    var vals = {};
+  /**
+   * @summary Solve the inverse geodesic problem.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} lat2 the latitude of the second point in degrees.
+   * @param {number} lon2 the longitude of the second point in degrees.
+   * @param {bitmask} [outmask = STANDARD] which results to include.
+   * @returns {object} the requested results
+   * @description The lat1, lon1, lat2, lon2, and a12 fields of the result are
+   *   always set.  For details on the outmask parameter, see {@tutorial
+   *   2-interface}, "The outmask and caps parameters".
+   */
+  g.Geodesic.prototype.Inverse = function(lat1, lon1, lat2, lon2, outmask) {
+    var vals = {},
+        lon12, lonsign, t, swapp, latsign,
+        sbet1, cbet1, sbet2, cbet2, s12x, m12x,
+        dn1, dn2, lam12, slam12, clam12,
+        sig12, calp1, salp1, calp2, salp2, C1a, C2a, C3a, meridian, nvals,
+        ssig1, csig1, ssig2, csig2, eps, omg12, dnm,
+        numit, salp1a, calp1a, salp1b, calp1b,
+        tripn, tripb, v, dv, dalp1, sdalp1, cdalp1, nsalp1,
+        lengthmask, salp0, calp0, alp12, k2, A4, C4a, B41, B42,
+        somg12, domg12, dbet1, dbet2, salp12, calp12;
+    if (!outmask) outmask = g.STANDARD;
+    if (outmask == g.LONG_UNROLL) outmask |= g.STANDARD;
     outmask &= g.OUT_MASK;
     // Compute longitude difference (AngDiff does this carefully).  Result is
     // in [-180, 180] but -180 is only for west-going geodesics.  180 is for
     // east-going and meridional geodesics.
     vals.lat1 = lat1 = m.LatFix(lat1); vals.lat2 = lat2 = m.LatFix(lat2);
-    var lon12 = m.AngDiff(lon1, lon2);
+    lon12 = m.AngDiff(lon1, lon2);
     if (outmask & g.LONG_UNROLL) {
       vals.lon1 = lon1; vals.lon2 = lon1 + lon12;
     } else {
@@ -758,13 +818,14 @@ GeographicLib.GeodesicLine = {};
     // If very close to being on the same half-meridian, then make it so.
     lon12 = m.AngRound(lon12);
     // Make longitude difference positive.
-    var lonsign = lon12 >= 0 ? 1 : -1;
+    lonsign = lon12 >= 0 ? 1 : -1;
     lon12 *= lonsign;
     // If really close to the equator, treat as on equator.
     lat1 = m.AngRound(lat1);
     lat2 = m.AngRound(lat2);
     // Swap points so that point with higher (abs) latitude is point 1
-    var t, swapp = Math.abs(lat1) >= Math.abs(lat2) ? 1 : -1;
+    // If one latitude is a nan, then it becomes lat1.
+    swapp = Math.abs(lat1) < Math.abs(lat2) ? -1 : 1;
     if (swapp < 0) {
       lonsign *= -1;
       t = lat1;
@@ -773,7 +834,7 @@ GeographicLib.GeodesicLine = {};
       // swap(lat1, lat2);
     }
     // Make lat1 <= 0
-    var latsign = lat1 < 0 ? 1 : -1;
+    latsign = lat1 < 0 ? 1 : -1;
     lat1 *= latsign;
     lat2 *= latsign;
     // Now we have
@@ -788,8 +849,6 @@ GeographicLib.GeodesicLine = {};
     // check, e.g., on verifying quadrants in atan2.  In addition, this
     // enforces some symmetries in the results returned.
 
-    var sbet1, cbet1, sbet2, cbet2, s12x, m12x;
-
     t = m.sincosd(lat1); sbet1 = this._f1 * t.s; cbet1 = t.c;
     // norm(sbet1, cbet1);
     t = m.hypot(sbet1, cbet1); sbet1 /= t; cbet1 /= t;
@@ -818,23 +877,18 @@ GeographicLib.GeodesicLine = {};
         cbet2 = cbet1;
     }
 
-    var
-    dn1 = Math.sqrt(1 + this._ep2 * m.sq(sbet1)),
+    dn1 = Math.sqrt(1 + this._ep2 * m.sq(sbet1));
     dn2 = Math.sqrt(1 + this._ep2 * m.sq(sbet2));
 
-    var lam12 = lon12 * m.degree, slam12, clam12;
-    t = m.sincosd(lam12); slam12 = t.s; clam12 = t.c;
+    lam12 = lon12 * m.degree;
+    t = m.sincosd(lon12); slam12 = t.s; clam12 = t.c;
 
-    var sig12, calp1, salp1, calp2, salp2;
     // index zero elements of these arrays are unused
-    var
-    C1a = new Array(g.nC1_ + 1),
-    C2a = new Array(g.nC2_ + 1),
+    C1a = new Array(g.nC1_ + 1);
+    C2a = new Array(g.nC2_ + 1)
     C3a = new Array(g.nC3_);
 
-    var meridian = lat1 === -90 || slam12 === 0, nvals;
-
-    var ssig1, csig1, ssig2, csig2, eps;
+    meridian = lat1 === -90 || slam12 === 0;
     if (meridian) {
 
       // Endpoints are on a single full meridian, so the geodesic might
@@ -848,7 +902,7 @@ GeographicLib.GeodesicLine = {};
       ssig2 = sbet2; csig2 = calp2 * cbet2;
 
       // sig12 = sig2 - sig1
-      sig12 = Math.atan2(Math.max(csig1 * ssig2 - ssig1 * csig2, 0),
+      sig12 = Math.atan2(Math.max(0, csig1 * ssig2 - ssig1 * csig2),
                          csig1 * csig2 + ssig1 * ssig2);
       nvals = this.Lengths(this._n, sig12,
                            ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2,
@@ -880,15 +934,14 @@ GeographicLib.GeodesicLine = {};
         meridian = false;
     }
 
-    var omg12;
     if (!meridian &&
         sbet1 === 0 &&           // and sbet2 == 0
         // Mimic the way Lambda12 works with calp1 = 0
-        (this._f <= 0 || lam12 <= Math.PI - this._f * Math.PI)) {
+        (this.f <= 0 || lam12 <= Math.PI - this.f * Math.PI)) {
 
       // Geodesic runs along equator
       calp1 = calp2 = 0; salp1 = salp2 = 1;
-      s12x = this._a * lam12;
+      s12x = this.a * lam12;
       sig12 = omg12 = lam12 / this._f1;
       m12x = this._b * Math.sin(sig12);
       if (outmask & g.GEODESICSCALE)
@@ -912,7 +965,7 @@ GeographicLib.GeodesicLine = {};
         calp2 = nvals.calp2;
         // Short lines (InverseStart sets salp2, calp2, dnm)
 
-        var dnm = nvals.dnm;
+        dnm = nvals.dnm;
         s12x = sig12 * this._b * dnm;
         m12x = m.sq(dnm) * this._b * Math.sin(sig12 / dnm);
         if (outmask & g.GEODESICSCALE)
@@ -932,17 +985,16 @@ GeographicLib.GeodesicLine = {};
         // value of alp1 is then further from the solution) or if the new
         // estimate of alp1 lies outside (0,pi); in this case, the new starting
         // guess is taken to be (alp1a + alp1b) / 2.
-        var numit = 0;
+        numit = 0;
         // Bracketing range
-        var salp1a = g.tiny_, calp1a = 1, salp1b = g.tiny_, calp1b = -1;
-        for (var tripn = false, tripb = false; numit < g.maxit2_; ++numit) {
+        salp1a = g.tiny_; calp1a = 1; salp1b = g.tiny_; calp1b = -1;
+        for (tripn = false, tripb = false; numit < maxit2_; ++numit) {
           // the WGS84 test set: mean = 1.47, sd = 1.25, max = 16
           // WGS84 and random input: mean = 2.85, sd = 0.60
-          var dv;
           nvals = this.Lambda12(sbet1, cbet1, dn1, sbet2, cbet2, dn2,
-                                salp1, calp1, numit < g.maxit1_,
+                                salp1, calp1, numit < maxit1_,
                                 C1a, C2a, C3a);
-          var v = nvals.lam12 - lam12;
+          v = nvals.lam12 - lam12;
           salp2 = nvals.salp2;
           calp2 = nvals.calp2;
           sig12 = nvals.sig12;
@@ -956,30 +1008,28 @@ GeographicLib.GeodesicLine = {};
 
           // 2 * tol0 is approximately 1 ulp for a number in [0, pi].
           // Reversed test to allow escape with NaNs
-          if (tripb || !(Math.abs(v) >= (tripn ? 8 : 2) * g.tol0_))
+          if (tripb || !(Math.abs(v) >= (tripn ? 8 : 2) * tol0_))
             break;
           // Update bracketing values
-          if (v > 0 && (numit < g.maxit1_ || calp1/salp1 > calp1b/salp1b)) {
+          if (v > 0 && (numit < maxit1_ || calp1/salp1 > calp1b/salp1b)) {
               salp1b = salp1; calp1b = calp1;
           } else if (v < 0 &&
-                     (numit < g.maxit1_ || calp1/salp1 < calp1a/salp1a)) {
+                     (numit < maxit1_ || calp1/salp1 < calp1a/salp1a)) {
             salp1a = salp1; calp1a = calp1;
           }
-          if (numit < g.maxit1_ && dv > 0) {
-            var
+          if (numit < maxit1_ && dv > 0) {
             dalp1 = -v/dv;
-            var
-            sdalp1 = Math.sin(dalp1), cdalp1 = Math.cos(dalp1),
+            sdalp1 = Math.sin(dalp1); cdalp1 = Math.cos(dalp1);
             nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
             if (nsalp1 > 0 && Math.abs(dalp1) < Math.PI) {
               calp1 = calp1 * cdalp1 - salp1 * sdalp1;
-              salp1 = Math.max(0, nsalp1);
+              salp1 = nsalp1;
               // norm(salp1, calp1);
               t = m.hypot(salp1, calp1); salp1 /= t; calp1 /= t;
               // In some regimes we don't get quadratic convergence because
               // slope -> 0.  So use convergence conditions based on epsilon
               // instead of sqrt(epsilon).
-              tripn = Math.abs(v) <= 16 * g.tol0_;
+              tripn = Math.abs(v) <= 16 * tol0_;
               continue;
             }
           }
@@ -996,10 +1046,10 @@ GeographicLib.GeodesicLine = {};
           // norm(salp1, calp1);
           t = m.hypot(salp1, calp1); salp1 /= t; calp1 /= t;
           tripn = false;
-          tripb = (Math.abs(salp1a - salp1) + (calp1a - calp1) < g.tolb_ ||
-                   Math.abs(salp1 - salp1b) + (calp1 - calp1b) < g.tolb_);
+          tripb = (Math.abs(salp1a - salp1) + (calp1a - calp1) < tolb_ ||
+                   Math.abs(salp1 - salp1b) + (calp1 - calp1b) < tolb_);
         }
-        var lengthmask = outmask |
+        lengthmask = outmask |
             (outmask & (g.REDUCEDLENGTH | g.GEODESICSCALE) ?
              g.DISTANCE : g.NONE);
         nvals = this.Lengths(eps, sig12,
@@ -1026,27 +1076,24 @@ GeographicLib.GeodesicLine = {};
       vals.m12 = 0 + m12x;      // Convert -0 to 0
 
     if (outmask & g.AREA) {
-      var
       // From Lambda12: sin(alp1) * cos(bet1) = sin(alp0)
-      salp0 = salp1 * cbet1,
+      salp0 = salp1 * cbet1;
       calp0 = m.hypot(calp1, salp1 * sbet1); // calp0 > 0
-      var alp12;
       if (calp0 !== 0 && salp0 !== 0) {
         // From Lambda12: tan(bet) = tan(sig) * cos(alp)
         ssig1 = sbet1; csig1 = calp1 * cbet1;
         ssig2 = sbet2; csig2 = calp2 * cbet2;
-        var k2 = m.sq(calp0) * this._ep2;
+        k2 = m.sq(calp0) * this._ep2;
         eps = k2 / (2 * (1 + Math.sqrt(1 + k2)) + k2);
         // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0).
-        A4 = m.sq(this._a) * calp0 * salp0 * this._e2;
+        A4 = m.sq(this.a) * calp0 * salp0 * this._e2;
         // norm(ssig1, csig1);
         t = m.hypot(ssig1, csig1); ssig1 /= t; csig1 /= t;
         // norm(ssig2, csig2);
         t = m.hypot(ssig2, csig2); ssig2 /= t; csig2 /= t;
-        var C4a = new Array(g.nC4_);
+        C4a = new Array(g.nC4_);
         this.C4f(eps, C4a);
-        var
-        B41 = g.SinCosSeries(false, ssig1, csig1, C4a),
+        B41 = g.SinCosSeries(false, ssig1, csig1, C4a);
         B42 = g.SinCosSeries(false, ssig2, csig2, C4a);
         vals.S12 = A4 * (B42 - B41);
       } else
@@ -1058,15 +1105,13 @@ GeographicLib.GeodesicLine = {};
           // Use tan(Gamma/2) = tan(omg12/2)
           // * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2))
           // with tan(x/2) = sin(x)/(1+cos(x))
-          var
-        somg12 = Math.sin(omg12), domg12 = 1 + Math.cos(omg12),
-        dbet1 = 1 + cbet1, dbet2 = 1 + cbet2;
+        somg12 = Math.sin(omg12); domg12 = 1 + Math.cos(omg12);
+        dbet1 = 1 + cbet1; dbet2 = 1 + cbet2;
         alp12 = 2 * Math.atan2( somg12 * (sbet1*dbet2 + sbet2*dbet1),
                                 domg12 * (sbet1*sbet2 + dbet1*dbet2) );
       } else {
         // alp12 = alp2 - alp1, used in atan2 so no need to normalize
-        var
-        salp12 = salp2 * calp1 - calp2 * salp1,
+        salp12 = salp2 * calp1 - calp2 * salp1;
         calp12 = calp2 * calp1 + salp2 * salp1;
         // The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz
         // salp12 = -0 and alp12 = -180.  However this depends on the sign
@@ -1114,15 +1159,105 @@ GeographicLib.GeodesicLine = {};
     return vals;
   };
 
-  // return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
+  /**
+   * @summary Solve the general direct geodesic problem.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} azi1 the azimuth at the first point in degrees.
+   * @param {bool} arcmode is the next parameter an arc length?
+   * @param {number} s12_a12 the (arcmode ? arc length : distance) from the
+   *   first point to the second in (arcmode ? degrees : meters).
+   * @param {bitmask} [outmask = STANDARD] which results to include.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, and a12 fields of the result are always
+   *   set; s12 is included if arcmode is false.  For details on the outmask
+   *   parameter, see {@tutorial 2-interface}, "The outmask and caps
+   *   parameters".
+   */
   g.Geodesic.prototype.GenDirect = function (lat1, lon1, azi1,
                                              arcmode, s12_a12, outmask) {
-    var line = new l.GeodesicLine(this, lat1, lon1, azi1,
-     // Automatically supply DISTANCE_IN if necessary
-     outmask | (arcmode ? g.NONE : g.DISTANCE_IN));
+    var line;
+    if (!outmask)
+      outmask = g.STANDARD;
+    else if (outmask == g.LONG_UNROLL)
+      outmask |= g.STANDARD;
+    line = new l.GeodesicLine(this, lat1, lon1, azi1,
+                              // Automatically supply DISTANCE_IN if necessary
+                              outmask | (arcmode ? g.NONE : g.DISTANCE_IN));
     return line.GenPosition(arcmode, s12_a12, outmask);
   };
 
-  g.WGS84 = new g.Geodesic(GeographicLib.Constants.WGS84.a,
-                           GeographicLib.Constants.WGS84.f);
-})();
+  /**
+   * @summary Solve the direct geodesic problem.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} azi1 the azimuth at the first point in degrees.
+   * @param {number} s12 the distance from the first point to the second in
+   *   meters.
+   * @param {bitmask} [outmask = STANDARD] which results to include.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, s12, and a12 fields of the result are
+   *   always set.  For details on the outmask parameter, see {@tutorial
+   *   2-interface}, "The outmask and caps parameters".
+   */
+  g.Geodesic.prototype.Direct = function (lat1, lon1, azi1, s12, outmask) {
+    return this.GenDirect(lat1, lon1, azi1, false, s12, outmask);
+  };
+
+  /**
+   * @summary Solve the direct geodesic problem with arc length.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} azi1 the azimuth at the first point in degrees.
+   * @param {number} a12 the arc length from the first point to the second in
+   *   degrees.
+   * @param {bitmask} [outmask = STANDARD] which results to include.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, and a12 fields of the result are
+   *   always set.  For details on the outmask parameter, see {@tutorial
+   *   2-interface}, "The outmask and caps parameters".
+   */
+  g.Geodesic.prototype.ArcDirect = function (lat1, lon1, azi1, a12, outmask) {
+    return this.GenDirect(lat1, lon1, azi1, true, a12, outmask);
+  };
+
+  /**
+   * @summary Create a {@link module:GeographicLib/GeodesicLine.GeodesicLine
+   *   GeodesicLine} object.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} azi1 the azimuth at the first point in degrees.
+   *   degrees.
+   * @param {bitmask} [caps = STANDARD | DISTANCE_IN] which capabilities to
+   *   include.
+   * @returns {object} the
+   *   {@link module:GeographicLib/GeodesicLine.GeodesicLine
+   *   GeodesicLine} object
+   * @description For details on the caps parameter, see {@tutorial
+   *   2-interface}, "The outmask and caps parameters".
+   */
+  g.Geodesic.prototype.Line = function (lat1, lon1, azi1, caps) {
+    return new l.GeodesicLine(this, lat1, lon1, azi1, caps);
+  };
+
+  /**
+   * @summary Create a {@link module:GeographicLib/PolygonArea.PolygonArea
+   *   PolygonArea} object.
+   * @param {bool} [polyline = false] if true the new PolygonArea object
+   *   describes a polyline instead of a polygon.
+   * @returns {object} the
+   *   {@link module:GeographicLib/PolygonArea.PolygonArea
+   *   PolygonArea} object
+   */
+  g.Geodesic.prototype.Polygon = function (polyline) {
+    return new p.PolygonArea(this, polyline);
+  };
+
+  /**
+   * @summary a {@link module:GeographicLib/Geodesic.Geodesic Geodesic} object
+   *   initialized for the WGS84 ellipsoid.
+   * @constant {object}
+   */
+  g.WGS84 = new g.Geodesic(c.WGS84.a, c.WGS84.f);
+})(GeographicLib.Geodesic, GeographicLib.GeodesicLine,
+   GeographicLib.PolygonArea, GeographicLib.Math, GeographicLib.Constants);
diff --git a/doc/scripts/GeographicLib/GeodesicLine.js b/js/src/GeodesicLine.js
similarity index 65%
rename from doc/scripts/GeographicLib/GeodesicLine.js
rename to js/src/GeodesicLine.js
index fdabdd9..3dd11dc 100644
--- a/doc/scripts/GeographicLib/GeodesicLine.js
+++ b/js/src/GeodesicLine.js
@@ -1,4 +1,4 @@
-/**
+/*
  * GeodesicLine.js
  * Transcription of GeodesicLine.[ch]pp into JavaScript.
  *
@@ -15,31 +15,60 @@
  * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
  * under the MIT/X11 License.  For more information, see
  * http://geographiclib.sourceforge.net/
- **********************************************************************/
+ */
 
-// Load AFTER GeographicLib/Math.js and GeographicLib/Geodesic.js
+// Load AFTER GeographicLib/Math.js, GeographicLib/Geodesic.js
 
-(function() {
-  var g = GeographicLib.Geodesic;
-  var l = GeographicLib.GeodesicLine;
-  var m = GeographicLib.Math;
+(function(
+  g,
+  /**
+   * @exports GeographicLib/GeodesicLine
+   * @description Solve geodesic problems on a single geodesic line via the
+   *   {@link module:GeographicLib/GeodesicLine.GeodesicLine GeodesicLine}
+   *   class.
+   */
+  l, m) {
+  "use strict";
 
+  /**
+   * @class
+   * @property {number} a the equatorial radius (meters).
+   * @property {number} f the flattening.
+   * @property {number} lat1 the initial latitude (degrees).
+   * @property {number} lon1 the initial longitude (degrees).
+   * @property {number} azi1 the initial azimuth (degrees).
+   * @property {bitmask} caps the capabilities of the object.
+   * @summary Initialize a GeodesicLine object.  For details on the caps
+   *   parameter, see {@tutorial 2-interface}, "The outmask and caps
+   *   parameters".
+   * @classdesc Performs geodesic calculations along a given geodesic line.
+   *   This object is usually instantiated by
+   *   {@link module:GeographicLib/Geodesic.Geodesic#Line Geodesic.Line}.
+   * @param {object} geod a {@link module:GeographicLib/Geodesic.Geodesic
+   *   Geodesic} object.
+   * @param {number} lat1 the latitude of the first point in degrees.
+   * @param {number} lon1 the longitude of the first point in degrees.
+   * @param {number} azi1 the azimuth at the first point in degrees.
+   * @param {bitmask} [caps = STANDARD | DISTANCE_IN] which capabilities to
+   *   include; LATITUDE | AZIMUTH are always included.
+   */
   l.GeodesicLine = function(geod, lat1, lon1, azi1, caps) {
-    this._a = geod._a;
-    this._f = geod._f;
+    var t, cbet1, sbet1, eps, s, c;
+    if (!caps) caps = g.STANDARD | g.DISTANCE_IN;
+
+    this.a = geod.a;
+    this.f = geod.f;
     this._b = geod._b;
     this._c2 = geod._c2;
     this._f1 = geod._f1;
     this._caps = (!caps ? g.ALL : (caps | g.LATITUDE | g.AZIMUTH)) |
       g.LONG_UNROLL;
 
-    this._lat1 = m.LatFix(lat1);
-    this._lon1 = lon1;
-    this._azi1 = m.AngNormalize(azi1);
-    var t;
-    t = m.sincosd(m.AngRound(this._azi1)); this._salp1 = t.s; this._calp1 = t.c;
-    var cbet1, sbet1;
-    t = m.sincosd(m.AngRound(this._lat1)); sbet1 = this._f1 * t.s; cbet1 = t.c;
+    this.lat1 = m.LatFix(lat1);
+    this.lon1 = lon1;
+    this.azi1 = m.AngNormalize(azi1);
+    t = m.sincosd(m.AngRound(this.azi1)); this._salp1 = t.s; this._calp1 = t.c;
+    t = m.sincosd(m.AngRound(this.lat1)); sbet1 = this._f1 * t.s; cbet1 = t.c;
     // norm(sbet1, cbet1);
     t = m.hypot(sbet1, cbet1); sbet1 /= t; cbet1 /= t;
     // Ensure cbet1 = +epsilon at poles
@@ -69,14 +98,14 @@
     // norm(this._somg1, this._comg1); -- don't need to normalize!
 
     this._k2 = m.sq(this._calp0) * geod._ep2;
-    var eps = this._k2 / (2 * (1 + Math.sqrt(1 + this._k2)) + this._k2);
+    eps = this._k2 / (2 * (1 + Math.sqrt(1 + this._k2)) + this._k2);
 
     if (this._caps & g.CAP_C1) {
       this._A1m1 = g.A1m1f(eps);
       this._C1a = new Array(g.nC1_ + 1);
       g.C1f(eps, this._C1a);
       this._B11 = g.SinCosSeries(true, this._ssig1, this._csig1, this._C1a);
-      var s = Math.sin(this._B11), c = Math.cos(this._B11);
+      s = Math.sin(this._B11); c = Math.cos(this._B11);
       // tau1 = sig1 + B11
       this._stau1 = this._ssig1 * c + this._csig1 * s;
       this._ctau1 = this._csig1 * c - this._ssig1 * s;
@@ -99,7 +128,7 @@
     if (this._caps & g.CAP_C3) {
       this._C3a = new Array(g.nC3_);
       geod.C3f(eps, this._C3a);
-      this._A3c = -this._f * this._salp0 * geod.A3f(eps);
+      this._A3c = -this.f * this._salp0 * geod.A3f(eps);
       this._B31 = g.SinCosSeries(true, this._ssig1, this._csig1, this._C3a);
     }
 
@@ -107,19 +136,38 @@
       this._C4a = new Array(g.nC4_); // all the elements of _C4a are used
       geod.C4f(eps, this._C4a);
       // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
-      this._A4 = m.sq(this._a) * this._calp0 * this._salp0 * geod._e2;
+      this._A4 = m.sq(this.a) * this._calp0 * this._salp0 * geod._e2;
       this._B41 = g.SinCosSeries(false, this._ssig1, this._csig1, this._C4a);
     }
   };
 
-  // return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
+  /**
+   * @summary Find the position on the line (general case).
+   * @param {bool} arcmode is the next parameter an arc length?
+   * @param {number} s12_a12 the (arcmode ? arc length : distance) from the
+   *   first point to the second in (arcmode ? degrees : meters).
+   * @param {bitmask} [outmask = STANDARD] which results to include; this is
+   *   subject to the capabilities of the object.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, and a12 fields of the result are
+   *   always set; s12 is included if arcmode is false.  For details on the
+   *   outmask parameter, see {@tutorial 2-interface}, "The outmask and caps
+   *   parameters".
+   */
   l.GeodesicLine.prototype.GenPosition = function(arcmode, s12_a12,
                                                   outmask) {
-    var vals = {};
+    var vals = {},
+        sig12, ssig12, csig12, B12, AB1, ssig2, csig2, tau12, s, c, serr,
+        omg12, lam12, lon12, E, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2,
+        B22, AB2, J12, t, B42, salp12, calp12;
+    if (!outmask)
+      outmask = g.STANDARD;
+    else if (outmask == g.LONG_UNROLL)
+      outmask |= g.STANDARD;
     outmask &= this._caps & g.OUT_MASK;
-    vals.lat1 = this._lat1; vals.azi1 = this._azi1;
+    vals.lat1 = this.lat1; vals.azi1 = this.azi1;
     vals.lon1 = outmask & g.LONG_UNROLL ?
-      this._lon1 : m.AngNormalize(this._lon1);
+      this.lon1 : m.AngNormalize(this.lon1);
     if (arcmode)
       vals.a12 = s12_a12;
     else
@@ -131,16 +179,15 @@
     }
 
     // Avoid warning about uninitialized B12.
-    var sig12, ssig12, csig12, B12 = 0, AB1 = 0, ssig2, csig2;
+    B12 = 0; AB1 = 0;
     if (arcmode) {
       // Interpret s12_a12 as spherical arc length
       sig12 = s12_a12 * m.degree;
       t = m.sincosd(s12_a12); ssig12 = t.s; csig12 = t.c;
     } else {
       // Interpret s12_a12 as distance
-      var
-      tau12 = s12_a12 / (this._b * (1 + this._A1m1)),
-      s = Math.sin(tau12),
+      tau12 = s12_a12 / (this._b * (1 + this._A1m1));
+      s = Math.sin(tau12);
       c = Math.cos(tau12);
       // tau2 = tau1 + tau12
       B12 = - g.SinCosSeries(true,
@@ -149,7 +196,7 @@
                              this._C1pa);
       sig12 = tau12 - (B12 - this._B11);
       ssig12 = Math.sin(sig12); csig12 = Math.cos(sig12);
-      if (Math.abs(this._f) > 0.01) {
+      if (Math.abs(this.f) > 0.01) {
         // Reverted distance series is inaccurate for |f| > 1/100, so correct
         // sig12 with 1 Newton iteration.  The following table shows the
         // approximate maximum error for a = WGS_a() and various f relative to
@@ -174,7 +221,7 @@
         ssig2 = this._ssig1 * csig12 + this._csig1 * ssig12;
         csig2 = this._csig1 * csig12 - this._ssig1 * ssig12;
         B12 = g.SinCosSeries(true, ssig2, csig2, this._C1a);
-        var serr = (1 + this._A1m1) * (sig12 + (B12 - this._B11)) -
+        serr = (1 + this._A1m1) * (sig12 + (B12 - this._B11)) -
           s12_a12 / this._b;
         sig12 = sig12 - serr / Math.sqrt(1 + this._k2 * m.sq(ssig2));
         ssig12 = Math.sin(sig12); csig12 = Math.cos(sig12);
@@ -182,14 +229,12 @@
       }
     }
 
-    var omg12, lam12, lon12, E;
-    var sbet2, cbet2, somg2, comg2, salp2, calp2;
     // sig2 = sig1 + sig12
     ssig2 = this._ssig1 * csig12 + this._csig1 * ssig12;
     csig2 = this._csig1 * csig12 - this._ssig1 * ssig12;
-    var dn2 = Math.sqrt(1 + this._k2 * m.sq(ssig2));
+    dn2 = Math.sqrt(1 + this._k2 * m.sq(ssig2));
     if (outmask & (g.DISTANCE | g.REDUCEDLENGTH | g.GEODESICSCALE)) {
-      if (arcmode || Math.abs(this._f) > 0.01)
+      if (arcmode || Math.abs(this.f) > 0.01)
         B12 = g.SinCosSeries(true, ssig2, csig2, this._C1a);
       AB1 = (1 + this._A1m1) * (B12 - this._B11);
     }
@@ -223,8 +268,8 @@
         ( sig12 + (g.SinCosSeries(true, ssig2, csig2, this._C3a) -
                    this._B31));
       lon12 = lam12 / m.degree;
-      vals.lon2 = outmask & g.LONG_UNROLL ? this._lon1 + lon12 :
-        m.AngNormalize(m.AngNormalize(this._lon1) + m.AngNormalize(lon12));
+      vals.lon2 = outmask & g.LONG_UNROLL ? this.lon1 + lon12 :
+        m.AngNormalize(m.AngNormalize(this.lon1) + m.AngNormalize(lon12));
     }
 
     if (outmask & g.LATITUDE)
@@ -234,9 +279,8 @@
       vals.azi2 = m.atan2d(salp2, calp2);
 
     if (outmask & (g.REDUCEDLENGTH | g.GEODESICSCALE)) {
-      var
-      B22 = g.SinCosSeries(true, ssig2, csig2, this._C2a),
-      AB2 = (1 + this._A2m1) * (B22 - this._B21),
+      B22 = g.SinCosSeries(true, ssig2, csig2, this._C2a);
+      AB2 = (1 + this._A2m1) * (B22 - this._B21);
       J12 = (this._A1m1 - this._A2m1) * sig12 + (AB1 - AB2);
       if (outmask & g.REDUCEDLENGTH)
         // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
@@ -245,7 +289,7 @@
                                this._dn1 * (this._ssig1 * csig2)) -
                               this._csig1 * csig2 * J12);
       if (outmask & g.GEODESICSCALE) {
-        var t = this._k2 * (ssig2 - this._ssig1) * (ssig2 + this._ssig1) /
+        t = this._k2 * (ssig2 - this._ssig1) * (ssig2 + this._ssig1) /
           (this._dn1 + dn2);
         vals.M12 = csig12 + (t * ssig2 - csig2 * J12) * this._ssig1 / this._dn1;
         vals.M21 = csig12 - (t * this._ssig1 - this._csig1 * J12) * ssig2 / dn2;
@@ -253,9 +297,7 @@
     }
 
     if (outmask & g.AREA) {
-      var
       B42 = g.SinCosSeries(false, ssig2, csig2, this._C4a);
-      var salp12, calp12;
       if (this._calp0 === 0 || this._salp0 === 0) {
         // alp12 = alp2 - alp1, used in atan2 so no need to normalize
         salp12 = salp2 * this._calp1 - calp2 * this._salp1;
@@ -290,4 +332,35 @@
     return vals;
   };
 
-})();
+  /**
+   * @summary Find the position on the line given s12.
+   * @param {number} s12 the distance from the first point to the second in
+   *   meters.
+   * @param {bitmask} [outmask = STANDARD] which results to include; this is
+   *   subject to the capabilities of the object.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, s12, and a12 fields of the result are
+   *   always set; s12 is included if arcmode is false.  For details on the
+   *   outmask parameter, see {@tutorial 2-interface}, "The outmask and caps
+   *   parameters".
+   */
+  l.GeodesicLine.prototype.Position = function(s12, outmask) {
+    return this.GenPosition(false, s12, outmask);
+  };
+
+  /**
+   * @summary Find the position on the line given a12.
+   * @param {number} a12 the arc length from the first point to the second in
+   *   degrees.
+   * @param {bitmask} [outmask = STANDARD] which results to include; this is
+   *   subject to the capabilities of the object.
+   * @returns {object} the requested results.
+   * @description The lat1, lon1, azi1, and a12 fields of the result are
+   *   always set.  For details on the outmask parameter, see {@tutorial
+   *   2-interface}, "The outmask and caps parameters".
+   */
+  l.GeodesicLine.prototype.ArcPosition = function(a12, outmask) {
+    return this.GenPosition(true, a12, outmask);
+  };
+
+})(GeographicLib.Geodesic, GeographicLib.GeodesicLine, GeographicLib.Math);
diff --git a/js/src/Math.js b/js/src/Math.js
new file mode 100644
index 0000000..0786b86
--- /dev/null
+++ b/js/src/Math.js
@@ -0,0 +1,414 @@
+/*
+ * Math.js
+ * Transcription of Math.hpp, Constants.hpp, and Accumulator.hpp into
+ * JavaScript.
+ *
+ * Copyright (c) Charles Karney (2011-2015) <charles at karney.com> and licensed
+ * under the MIT/X11 License.  For more information, see
+ * http://geographiclib.sourceforge.net/
+ */
+
+/**
+ * @namespace GeographicLib
+ * @description The parent namespace for the following modules:
+ * - {@link module:GeographicLib/Geodesic GeographicLib/Geodesic} The main
+ *   engine for solving geodesic problems via the
+ *   {@link module:GeographicLib/Geodesic.Geodesic Geodesic} class.
+ * - {@link module:GeographicLib/GeodesicLine GeographicLib/GeodesicLine}
+ *   computes points along a single geodesic line via the
+ *   {@link module:GeographicLib/GeodesicLine.GeodesicLine GeodesicLine}
+ *   class.
+ * - {@link module:GeographicLib/PolygonArea GeographicLib/PolygonArea}
+ *   computes the area of a geodesic polygon via the
+ *   {@link module:GeographicLib/PolygonArea.PolygonArea PolygonArea}
+ *   class.
+ * - {@link module:GeographicLib/DMS GeographicLib/DMS} handles the decoding
+ *   and encoding of angles in degree, minutes, and seconds, via static
+ *   functions in this module.
+ * - {@link module:GeographicLib/Constants GeographicLib/Constants} defines
+ *   constants specifying the version numbers and the parameters for the WGS84
+ *   ellipsoid.
+ *
+ * The following modules are used internally by the package:
+ * - {@link module:GeographicLib/Math GeographicLib/Math} defines various
+ *   mathematical functions.
+ * - {@link module:GeographicLib/Accumulator GeographicLib/Accumulator}
+ *   interally used by
+ *   {@link module:GeographicLib/PolygonArea.PolygonArea PolygonArea} (via the
+ *   {@link module:GeographicLib/Accumulator.Accumulator Accumulator} class)
+ *   for summing the contributions to the area of a polygon.
+ */
+var GeographicLib = {};
+GeographicLib.Constants = {};
+GeographicLib.Math = {};
+GeographicLib.Accumulator = {};
+
+(function(
+  /**
+   * @exports GeographicLib/Constants
+   * @description Define constants defining the version and WGS84 parameters.
+   */
+  c) {
+  "use strict";
+
+  /**
+   * @constant
+   * @summary WGS84 parameters.
+   * @property {number} a the equatorial radius (meters).
+   * @property {number} f the flattening.
+   */
+  c.WGS84 = { a: 6378137, f: 1/298.257223563 };
+  /**
+   * @constant
+   * @summary an array of version numbers.
+   * @property {number} major the major version number.
+   * @property {number} minor the minor version number.
+   * @property {number} patch the patch number.
+   */
+  c.version = { major: 1, minor: 45, patch: 0 };
+  /**
+   * @constant
+   * @summary version string
+   */
+  c.version_string = "1.45";
+})(GeographicLib.Constants);
+
+(function(
+  /**
+   * @exports GeographicLib/Math
+   * @description Some useful mathematical constants and functions (mainly for
+   *   internal use).
+   */
+  m) {
+  "use strict";
+
+  /**
+   * @summary The number of digits of precision in floating-point numbers.
+   * @constant {number}
+   */
+  m.digits = 53;
+  /**
+   * @summary The machine epsilon.
+   * @constant {number}
+   */
+  m.epsilon = Math.pow(0.5, m.digits - 1);
+  /**
+   * @summary The factor to convert degrees to radians.
+   * @constant {number}
+   */
+  m.degree = Math.PI/180;
+
+  /**
+   * @summary Square a number.
+   * @param {number} x the number.
+   * @returns {number} the square.
+   */
+  m.sq = function(x) { return x * x; };
+
+  /**
+   * @summary The hypotenuse function.
+   * @param {number} x the first side.
+   * @param {number} y the second side.
+   * @returns {number} the hypotenuse.
+   */
+  m.hypot = function(x, y) {
+    var a, b;
+    x = Math.abs(x);
+    y = Math.abs(y);
+    a = Math.max(x, y); b = Math.min(x, y) / (a ? a : 1);
+    return a * Math.sqrt(1 + b * b);
+  };
+
+  /**
+   * @summary Cube root function.
+   * @param {number} x the argument.
+   * @returns {number} the real cube root.
+   */
+  m.cbrt = function(x) {
+    var y = Math.pow(Math.abs(x), 1/3);
+    return x < 0 ? -y : y;
+  };
+
+  /**
+   * @summary The log1p function.
+   * @param {number} x the argument.
+   * @returns {number} log(1 + x).
+   */
+  m.log1p = function(x) {
+    var y = 1 + x,
+        z = y - 1;
+    // Here's the explanation for this magic: y = 1 + z, exactly, and z
+    // approx x, thus log(y)/z (which is nearly constant near z = 0) returns
+    // a good approximation to the true log(1 + x)/x.  The multiplication x *
+    // (log(y)/z) introduces little additional error.
+    return z === 0 ? x : x * Math.log(y) / z;
+  };
+
+  /**
+   * @summary Inverse hyperbolic tangent.
+   * @param {number} x the argument.
+   * @returns {number} tanh<sup>−1</sup> x.
+   */
+  m.atanh = function(x) {
+    var y = Math.abs(x);          // Enforce odd parity
+    y = m.log1p(2 * y/(1 - y))/2;
+    return x < 0 ? -y : y;
+  };
+
+  /**
+   * @summary An error-free sum.
+   * @param {number} u
+   * @param {number} v
+   * @returns {object} sum with sum.s = round(u + v) and sum.t is u + v −
+   *   round(u + v)
+   */
+  m.sum = function(u, v) {
+    var s = u + v,
+        up = s - v,
+        vpp = s - up,
+        t;
+    up -= u;
+    vpp -= v;
+    t = -(up + vpp);
+    // u + v =       s      + t
+    //       = round(u + v) + t
+    return {s: s, t: t};
+  };
+
+  /**
+   * @summary Evaluate a polynomial.
+   * @param {integer} N the order of the polynomial.
+   * @param {array} p the coefficient array (of size N + 1) (leading
+   *   order coefficient first)
+   * @param {number} x the variable.
+   * @returns {number} the value of the polynomial.
+   */
+  m.polyval = function(N, p, s, x) {
+    var y = N < 0 ? 0 : p[s++];
+    while (--N >= 0) y = y * x + p[s++];
+    return y;
+  };
+
+  /**
+   * @summary Coarsen a value close to zero.
+   * @param {number} x
+   * @returns {number} the coarsened value.
+   */
+  m.AngRound = function(x) {
+    // The makes the smallest gap in x = 1/16 - nextafter(1/16, 0) = 1/2^57 for
+    // reals = 0.7 pm on the earth if x is an angle in degrees.  (This is about
+    // 1000 times more resolution than we get with angles around 90 degrees.)
+    // We use this to avoid having to deal with near singular cases when x is
+    // non-zero but tiny (e.g., 1.0e-200).  This also converts -0 to +0.
+    var z = 1/16,
+        y = Math.abs(x);
+    // The compiler mustn't "simplify" z - (z - y) to y
+    y = y < z ? z - (z - y) : y;
+    return x < 0 ? 0 - y : y;
+  };
+
+  /**
+   * @summary Normalize an angle.
+   * @param {number} x the angle in degrees.
+   * @returns {number} the angle reduced to the range [−180°,
+   *   180°).
+   */
+  m.AngNormalize = function(x) {
+    // Place angle in [-180, 180).
+    x = x % 360;
+    return x < -180 ? x + 360 : (x < 180 ? x : x - 360);
+  };
+
+  /**
+   * @summary Normalize a latitude.
+   * @param {number} x the angle in degrees.
+   * @returns {number} x if it is in the range [−90°, 90°],
+   *   otherwise return NaN.
+   */
+  m.LatFix = function(x) {
+    // Replace angle with NaN if outside [-90, 90].
+    return Math.abs(x) > 90 ? Number.NaN : x;
+  };
+
+  /**
+   * @summary Difference of two angles reduced to [−180°,
+   *   180°]
+   * @param {number} x the first angle in degrees.
+   * @param {number} y the second angle in degrees.
+   * @return {number} y − x, reduced to the range [−180°,
+   *   180°].
+   */
+  m.AngDiff = function(x, y) {
+    // Compute y - x and reduce to [-180,180] accurately.
+    var r = m.sum(m.AngNormalize(x), m.AngNormalize(-y)),
+        d = - m.AngNormalize(r.s),
+        t = r.t;
+    return (d == 180 && t < 0 ? -180 : d) - t;
+  };
+
+  /**
+   * @summary Evaluate the sine and cosine function with the argument in
+   *   degrees
+   * @param {number} x in degrees.
+   * @returns {object} r with r.s = sin(x) and r.c = cos(x).
+   */
+  m.sincosd = function(x) {
+    // In order to minimize round-off errors, this function exactly reduces
+    // the argument to the range [-45, 45] before converting it to radians.
+    var r, q, s, c, sinx, cosx;
+    r = x % 360;
+    q = Math.floor(r / 90 + 0.5);
+    r -= 90 * q;
+    // now abs(r) <= 45
+    r *= this.degree;
+    // Possibly could call the gnu extension sincos
+    s = Math.sin(r); c = Math.cos(r);
+    switch (q & 3) {
+    case  0: sinx =     s; cosx =     c; break;
+    case  1: sinx =     c; cosx = 0 - s; break;
+    case  2: sinx = 0 - s; cosx = 0 - c; break;
+    default: sinx = 0 - c; cosx =     s; break; // case 3
+    }
+    return {s: sinx, c: cosx};
+  };
+
+  /**
+   * @summary Evaluate the atan2 function with the result in degrees
+   * @param {number} y
+   * @param {number} x
+   * @returns atan2(y, x) in degrees, in the range [−180°
+   *   180°).
+   */
+  m.atan2d = function(y, x) {
+    // In order to minimize round-off errors, this function rearranges the
+    // arguments so that result of atan2 is in the range [-pi/4, pi/4] before
+    // converting it to degrees and mapping the result to the correct
+    // quadrant.
+    var q = 0, t, ang;
+    if (Math.abs(y) > Math.abs(x)) { t = x; x = y; y = t; q = 2; }
+    if (x < 0) { x = -x; ++q; }
+    // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
+    ang = Math.atan2(y, x) / this.degree;
+    switch (q) {
+      // Note that atan2d(-0.0, 1.0) will return -0.  However, we expect that
+      // atan2d will not be called with y = -0.  If need be, include
+      //
+      //   case 0: ang = 0 + ang; break;
+      //
+      // and handle mpfr as in AngRound.
+    case 1: ang = (y > 0 ? 180 : -180) - ang; break;
+    case 2: ang =  90 - ang; break;
+    case 3: ang = -90 + ang; break;
+    }
+    return ang;
+  };
+})(GeographicLib.Math);
+
+(function(
+  /**
+   * @exports GeographicLib/Accumulator
+   * @description Accurate summation via the
+   *   {@link module:GeographicLib/Accumulator.Accumulator Accumulator} class
+   *   (mainly for internal use).
+   */
+  a, m) {
+  "use strict";
+
+  /**
+   * @class
+   * @summary Accurate summation of many numbers.
+   * @classdesc This allows many numbers to be added together with twice the
+   *   normal precision.  In the documentation of the member functions, sum
+   *   stands for the value currently held in the accumulator.
+   * @param {number | Accumulator} [y = 0]  set sum = y.
+   */
+  a.Accumulator = function(y) {
+    this.Set(y);
+  };
+
+  /**
+   * @summary Set the accumulator to a number.
+   * @param {number | Accumulator} [y = 0] set sum = y.
+   */
+  a.Accumulator.prototype.Set = function(y) {
+    if (!y) y = 0;
+    if (y.constructor === a.Accumulator) {
+      this._s = y._s;
+      this._t = y._t;
+    } else {
+      this._s = y;
+      this._t = 0;
+    }
+  };
+
+  /**
+   * @summary Add a number to the accumulator.
+   * @param {number} [y = 0] set sum += y.
+   */
+  a.Accumulator.prototype.Add = function(y) {
+    // Here's Shewchuk's solution...
+    // Accumulate starting at least significant end
+    var u = m.sum(y, this._t),
+        v = m.sum(u.s, this._s);
+    u = u.t;
+    this._s = v.s;
+    this._t = v.t;
+    // Start is _s, _t decreasing and non-adjacent.  Sum is now (s + t + u)
+    // exactly with s, t, u non-adjacent and in decreasing order (except
+    // for possible zeros).  The following code tries to normalize the
+    // result.  Ideally, we want _s = round(s+t+u) and _u = round(s+t+u -
+    // _s).  The follow does an approximate job (and maintains the
+    // decreasing non-adjacent property).  Here are two "failures" using
+    // 3-bit floats:
+    //
+    // Case 1: _s is not equal to round(s+t+u) -- off by 1 ulp
+    // [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3
+    //
+    // Case 2: _s+_t is not as close to s+t+u as it shold be
+    // [64, 5] + 4 -> [64, 8, 1] -> [64,  8] = 72 (off by 1)
+    //                    should be [80, -7] = 73 (exact)
+    //
+    // "Fixing" these problems is probably not worth the expense.  The
+    // representation inevitably leads to small errors in the accumulated
+    // values.  The additional errors illustrated here amount to 1 ulp of
+    // the less significant word during each addition to the Accumulator
+    // and an additional possible error of 1 ulp in the reported sum.
+    //
+    // Incidentally, the "ideal" representation described above is not
+    // canonical, because _s = round(_s + _t) may not be true.  For
+    // example, with 3-bit floats:
+    //
+    // [128, 16] + 1 -> [160, -16] -- 160 = round(145).
+    // But [160, 0] - 16 -> [128, 16] -- 128 = round(144).
+    //
+    if (this._s === 0)          // This implies t == 0,
+      this._s = u;              // so result is u
+    else
+      this._t += u;             // otherwise just accumulate u to t.
+  };
+
+  /**
+   * @summary Return the result of adding a number to sum (but
+   *   don't change sum).
+   * @param {number} [y = 0] the number to be added to the sum.
+   * @return sum + y.
+   */
+  a.Accumulator.prototype.Sum = function(y) {
+    var b;
+    if (!y)
+      return this._s;
+    else {
+      b = new a.Accumulator(this);
+      b.Add(y);
+      return b._s;
+    }
+  };
+
+  /**
+   * @summary Set sum = −sum.
+   */
+  a.Accumulator.prototype.Negate = function() {
+    this._s *= -1;
+    this._t *= -1;
+  };
+})(GeographicLib.Accumulator, GeographicLib.Math);
diff --git a/js/src/PolygonArea.js b/js/src/PolygonArea.js
new file mode 100644
index 0000000..4cf53f0
--- /dev/null
+++ b/js/src/PolygonArea.js
@@ -0,0 +1,318 @@
+/*
+ * PolygonArea.js
+ * Transcription of PolygonArea.[ch]pp into JavaScript.
+ *
+ * See the documentation for the C++ class.  The conversion is a literal
+ * conversion from C++.
+ *
+ * The algorithms are derived in
+ *
+ *    Charles F. F. Karney,
+ *    Algorithms for geodesics, J. Geodesy 87, 43-55 (2013);
+ *    https://dx.doi.org/10.1007/s00190-012-0578-z
+ *    Addenda: http://geographiclib.sf.net/geod-addenda.html
+ *
+ * Copyright (c) Charles Karney (2011-2014) <charles at karney.com> and licensed
+ * under the MIT/X11 License.  For more information, see
+ * http://geographiclib.sourceforge.net/
+ */
+
+// Load AFTER GeographicLib/Math.js and GeographicLib/Geodesic.js
+
+(function(
+  /**
+   * @exports GeographicLib/PolygonArea
+   * @description Compute the area of geodesic polygons via the
+   *   {@link module:GeographicLib/PolygonArea.PolygonArea PolygonArea}
+   *   class.
+   */
+  p, g, m, a) {
+  "use strict";
+
+  var transit, transitdirect;
+  transit = function(lon1, lon2) {
+    // Return 1 or -1 if crossing prime meridian in east or west direction.
+    // Otherwise return zero.
+    var lon12, cross;
+    // Compute lon12 the same way as Geodesic::Inverse.
+    lon1 = m.AngNormalize(lon1);
+    lon2 = m.AngNormalize(lon2);
+    lon12 = m.AngDiff(lon1, lon2);
+    cross = lon1 < 0 && lon2 >= 0 && lon12 > 0 ? 1 :
+      (lon2 < 0 && lon1 >= 0 && lon12 < 0 ? -1 : 0);
+    return cross;
+  };
+
+  // an alternate version of transit to deal with longitudes in the direct
+  // problem.
+  transitdirect = function(lon1, lon2) {
+    // We want to compute exactly
+    //   int(floor(lon2 / 360)) - int(floor(lon1 / 360))
+    // Since we only need the parity of the result we can use std::remquo but
+    // this is buggy with g++ 4.8.3 and requires C++11.  So instead we do
+    lon1 = lon1 % 720.0; lon2 = lon2 % 720.0;
+    return ( ((lon2 >= 0 && lon2 < 360) || lon2 < -360 ? 0 : 1) -
+             ((lon1 >= 0 && lon1 < 360) || lon1 < -360 ? 0 : 1) );
+  };
+
+  /**
+   * @class
+   * @property {number} a the equatorial radius (meters).
+   * @property {number} f the flattening.
+   * @property {bool} polyline whether the PolygonArea object describes a
+   *   polyline or a polygon.
+   * @property {number} num the number of vertices so far.
+   * @property {number} lat the current latitude (degrees).
+   * @property {number} lon the current longitude (degrees).
+   * @summary Initialize a PolygonArea object.
+   * @classdesc Computes the area and perimeter of a geodesic polygon.
+   *   This object is usually instantiated by
+   *   {@link module:GeographicLib/Geodesic.Geodesic#Polygon Geodesic.Polygon}.
+   * @param {object} geod a {@link module:GeographicLib/Geodesic.Geodesic
+   *   Geodesic} object.
+   * @param {bool} [polyline = false] if true the new PolygonArea object
+   *   describes a polyline instead of a polygon.
+   */
+  p.PolygonArea = function(geod, polyline) {
+    this._geod = geod;
+    this.a = this._geod.a;
+    this.f = this._geod.f;
+    this._area0 = 4 * Math.PI * geod._c2;
+    this.polyline = !polyline ? false : polyline;
+    this._mask = g.LATITUDE | g.LONGITUDE | g.DISTANCE |
+          (this.polyline ? g.NONE : g.AREA | g.LONG_UNROLL);
+    if (!this.polyline)
+      this._areasum = new a.Accumulator(0);
+    this._perimetersum = new a.Accumulator(0);
+    this.Clear();
+  };
+
+  /**
+   * @summary Clear the PolygonArea object, setting the number of vertices to
+   *   0.
+   */
+  p.PolygonArea.prototype.Clear = function() {
+    this.num = 0;
+    this._crossings = 0;
+    if (!this.polyline)
+      this._areasum.Set(0);
+    this._perimetersum.Set(0);
+    this._lat0 = this._lon0 = this.lat = this.lon = Number.NaN;
+  };
+
+  /**
+   * @summary Add the next vertex to the polygon.
+   * @param {number} lat the latitude of the point (degrees).
+   * @param {number} lon the longitude of the point (degrees).
+   * @description This adds an edge from the current vertex to the new vertex.
+   */
+  p.PolygonArea.prototype.AddPoint = function(lat, lon) {
+    var t;
+    if (this.num === 0) {
+      this._lat0 = this.lat = lat;
+      this._lon0 = this.lon = lon;
+    } else {
+      t = this._geod.Inverse(this.lat, this.lon, lat, lon, this._mask);
+      this._perimetersum.Add(t.s12);
+      if (!this.polyline) {
+        this._areasum.Add(t.S12);
+        this._crossings += transit(this.lon, lon);
+      }
+      this.lat = lat;
+      this.lon = lon;
+    }
+    ++this.num;
+  };
+
+  /**
+   * @summary Add the next edge to the polygon.
+   * @param {number} azi the azimuth at the current the point (degrees).
+   * @param {number} s the length of the edge (meters).
+   * @description This specifies the new vertex in terms of the edge from the
+   *   current vertex.
+   */
+  p.PolygonArea.prototype.AddEdge = function(azi, s) {
+    var t;
+    if (this.num) {
+      t = this._geod.Direct(this.lat, this.lon, azi, s, this._mask);
+      this._perimetersum.Add(s);
+      if (!this.polyline) {
+        this._areasum.Add(t.S12);
+        this._crossings += transitdirect(this.lon, t.lon2);
+      }
+      this.lat = t.lat2;
+      this.lon = t.lon2;
+    }
+    ++this.num;
+  };
+
+  /**
+   * @summary Compute the perimeter and area of the polygon.
+   * @param {bool} reverse if true then clockwise (instead of
+   *   counter-clockwise) traversal counts as a positive area.
+   * @param {bool} sign if true then return a signed result for the area if the
+   *   polygon is traversed in the "wrong" direction instead of returning the
+   * @returns {object} r where r.number is the number of vertices, r.perimeter
+   *   is the perimeter (meters), and r.area (only returned if polyline is
+   *   false) is the area (meters<sup>2</sup>).
+   * @description More points can be added to the polygon after this call.
+   */
+  p.PolygonArea.prototype.Compute = function(reverse, sign) {
+    var vals = {number: this.num}, t, tempsum, crossings;
+    if (this.num < 2) {
+      vals.perimeter = 0;
+      if (!this.polyline)
+        vals.area = 0;
+      return vals;
+    }
+    if (this.polyline) {
+      vals.perimeter = this._perimetersum.Sum();
+      return vals;
+    }
+    t = this._geod.Inverse(this.lat, this.lon, this._lat0, this._lon0,
+                           this._mask);
+    vals.perimeter = this._perimetersum.Sum(t.s12);
+    tempsum = new a.Accumulator(this._areasum);
+    tempsum.Add(t.S12);
+    crossings = this._crossings + transit(this.lon, this._lon0);
+    if (crossings & 1)
+      tempsum.Add( (tempsum.Sum() < 0 ? 1 : -1) * this._area0/2 );
+    // area is with the clockwise sense.  If !reverse convert to
+    // counter-clockwise convention.
+    if (!reverse)
+      tempsum.Negate();
+    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
+    if (sign) {
+      if (tempsum.Sum() > this._area0/2)
+        tempsum.Add( -this._area0 );
+      else if (tempsum.Sum() <= -this._area0/2)
+        tempsum.Add( +this._area0 );
+    } else {
+      if (tempsum.Sum() >= this._area0)
+        tempsum.Add( -this._area0 );
+      else if (tempsum < 0)
+        tempsum.Add( -this._area0 );
+    }
+    vals.area = tempsum.Sum();
+    return vals;
+  };
+
+  /**
+   * @summary Compute the perimeter and area of the polygon with a tentative
+   *   new vertex.
+   * @param {number} lat the latitude of the point (degrees).
+   * @param {number} lon the longitude of the point (degrees).
+   * @param {bool} reverse if true then clockwise (instead of
+   *   counter-clockwise) traversal counts as a positive area.
+   * @param {bool} sign if true then return a signed result for the area if the
+   *   polygon is traversed in the "wrong" direction instead of returning the
+   * @returns {object} r where r.number is the number of vertices, r.perimeter
+   *   is the perimeter (meters), and r.area (only returned if polyline is
+   *   false) is the area (meters<sup>2</sup>).
+   * @description A new vertex is *not* added to the polygon.
+   */
+  p.PolygonArea.prototype.TestPoint = function(lat, lon, reverse, sign) {
+    var vals = {number: this.num + 1}, t, tempsum, crossings, i;
+    if (this.num === 0) {
+      vals.perimeter = 0;
+      if (!this.polyline)
+        vals.area = 0;
+      return vals;
+    }
+    vals.perimeter = this._perimetersum.Sum();
+    tempsum = this.polyline ? 0 : this._areasum.Sum();
+    crossings = this._crossings;
+    for (i = 0; i < (this.polyline ? 1 : 2); ++i) {
+      t = this._geod.Inverse(
+       i === 0 ? this.lat : lat, i === 0 ? this.lon : lon,
+       i !== 0 ? this._lat0 : lat, i !== 0 ? this._lon0 : lon,
+       this._mask);
+      vals.perimeter += t.s12;
+      if (!this.polyline) {
+        tempsum += t.S12;
+        crossings += transit(i === 0 ? this.lon : lon,
+                               i !== 0 ? this._lon0 : lon);
+      }
+    }
+
+    if (this.polyline)
+      return vals;
+
+    if (crossings & 1)
+      tempsum += (tempsum < 0 ? 1 : -1) * this._area0/2;
+    // area is with the clockwise sense.  If !reverse convert to
+    // counter-clockwise convention.
+    if (!reverse)
+      tempsum *= -1;
+    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
+    if (sign) {
+      if (tempsum > this._area0/2)
+        tempsum -= this._area0;
+      else if (tempsum <= -this._area0/2)
+        tempsum += this._area0;
+    } else {
+      if (tempsum >= this._area0)
+        tempsum -= this._area0;
+      else if (tempsum < 0)
+        tempsum += this._area0;
+    }
+    vals.area = tempsum;
+    return vals;
+  };
+
+  /**
+   * @summary Compute the perimeter and area of the polygon with a tentative
+   *   new edge.
+   * @param {number} azi the azimuth of the edge (degrees).
+   * @param {number} s the length of the edge (meters).
+   * @param {bool} reverse if true then clockwise (instead of
+   *   counter-clockwise) traversal counts as a positive area.
+   * @param {bool} sign if true then return a signed result for the area if the
+   *   polygon is traversed in the "wrong" direction instead of returning the
+   * @returns {object} r where r.number is the number of vertices, r.perimeter
+   *   is the perimeter (meters), and r.area (only returned if polyline is
+   *   false) is the area (meters<sup>2</sup>).
+   * @description A new vertex is *not* added to the polygon.
+   */
+  p.PolygonArea.prototype.TestEdge = function(azi, s, reverse, sign) {
+    var vals = {number: this.num ? this.num + 1 : 0}, t, tempsump, crossings;
+    if (this.num === 0)
+      return vals;
+    vals.perimeter = this._perimetersum.Sum() + s;
+    if (this.polyline)
+      return vals;
+
+    tempsum = this._areasum.Sum();
+    crossings = this._crossings;
+    t = this._geod.Direct(this.lat, this.lon, azi, s, this._mask);
+    tempsum += t.S12;
+    crossings += transitdirect(this.lon, t.lon2);
+    t = this._geod(t.lat2, t.lon2, this._lat0, this._lon0, this._mask);
+    perimeter += t.s12;
+    tempsum += t.S12;
+    crossings += transit(t.lon2, this._lon0);
+
+    if (crossings & 1)
+      tempsum += (tempsum < 0 ? 1 : -1) * this._area0/2;
+    // area is with the clockwise sense.  If !reverse convert to
+    // counter-clockwise convention.
+    if (!reverse)
+      tempsum *= -1;
+    // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
+    if (sign) {
+      if (tempsum > this._area0/2)
+        tempsum -= this._area0;
+      else if (tempsum <= -this._area0/2)
+        tempsum += this._area0;
+    } else {
+      if (tempsum >= this._area0)
+        tempsum -= this._area0;
+      else if (tempsum < 0)
+        tempsum += this._area0;
+    }
+    vals.area = tempsum;
+    return vals;
+  };
+
+})(GeographicLib.PolygonArea, GeographicLib.Geodesic,
+   GeographicLib.Math, GeographicLib.Accumulator);
diff --git a/js/test/geodesictest.js b/js/test/geodesictest.js
new file mode 100644
index 0000000..b1595d4
--- /dev/null
+++ b/js/test/geodesictest.js
@@ -0,0 +1,514 @@
+"use strict";
+
+var assert = require("assert"),
+    G = require("../geographiclib"),
+    g = G.Geodesic,
+    d = G.DMS,
+    testcases = [
+      [35.60777, -139.44815, 111.098748429560326,
+       -11.17491, -69.95921, 129.289270889708762,
+       8935244.5604818305, 80.50729714281974, 6273170.2055303837,
+       0.16606318447386067, 0.16479116945612937, 12841384694976.432],
+      [55.52454, 106.05087, 22.020059880982801,
+       77.03196, 197.18234, 109.112041110671519,
+       4105086.1713924406, 36.892740690445894, 3828869.3344387607,
+       0.80076349608092607, 0.80101006984201008, 61674961290615.615],
+      [-21.97856, 142.59065, -32.44456876433189,
+       41.84138, 98.56635, -41.84359951440466,
+       8394328.894657671, 75.62930491011522, 6161154.5773110616,
+       0.24816339233950381, 0.24930251203627892, -6637997720646.717],
+      [-66.99028, 112.2363, 173.73491240878403,
+       -12.70631, 285.90344, 2.512956620913668,
+       11150344.2312080241, 100.278634181155759, 6289939.5670446687,
+       -0.17199490274700385, -0.17722569526345708, -121287239862139.744],
+      [-17.42761, 173.34268, -159.033557661192928,
+       -15.84784, 5.93557, -20.787484651536988,
+       16076603.1631180673, 144.640108810286253, 3732902.1583877189,
+       -0.81273638700070476, -0.81299800519154474, 97825992354058.708],
+      [32.84994, 48.28919, 150.492927788121982,
+       -56.28556, 202.29132, 48.113449399816759,
+       16727068.9438164461, 150.565799985466607, 3147838.1910180939,
+       -0.87334918086923126, -0.86505036767110637, -72445258525585.010],
+      [6.96833, 52.74123, 92.581585386317712,
+       -7.39675, 206.17291, 90.721692165923907,
+       17102477.2496958388, 154.147366239113561, 2772035.6169917581,
+       -0.89991282520302447, -0.89986892177110739, -1311796973197.995],
+      [-50.56724, -16.30485, -105.439679907590164,
+       -33.56571, -94.97412, -47.348547835650331,
+       6455670.5118668696, 58.083719495371259, 5409150.7979815838,
+       0.53053508035997263, 0.52988722644436602, 41071447902810.047],
+      [-58.93002, -8.90775, 140.965397902500679,
+       -8.91104, 133.13503, 19.255429433416599,
+       11756066.0219864627, 105.755691241406877, 6151101.2270708536,
+       -0.26548622269867183, -0.27068483874510741, -86143460552774.735],
+      [-68.82867, -74.28391, 93.774347763114881,
+       -50.63005, -8.36685, 34.65564085411343,
+       3956936.926063544, 35.572254987389284, 3708890.9544062657,
+       0.81443963736383502, 0.81420859815358342, -41845309450093.787],
+      [-10.62672, -32.0898, -86.426713286747751,
+       5.883, -134.31681, -80.473780971034875,
+       11470869.3864563009, 103.387395634504061, 6184411.6622659713,
+       -0.23138683500430237, -0.23155097622286792, 4198803992123.548],
+      [-21.76221, 166.90563, 29.319421206936428,
+       48.72884, 213.97627, 43.508671946410168,
+       9098627.3986554915, 81.963476716121964, 6299240.9166992283,
+       0.13965943368590333, 0.14152969707656796, 10024709850277.476],
+      [-19.79938, -174.47484, 71.167275780171533,
+       -11.99349, -154.35109, 65.589099775199228,
+       2319004.8601169389, 20.896611684802389, 2267960.8703918325,
+       0.93427001867125849, 0.93424887135032789, -3935477535005.785],
+      [-11.95887, -116.94513, 92.712619830452549,
+       4.57352, 7.16501, 78.64960934409585,
+       13834722.5801401374, 124.688684161089762, 5228093.177931598,
+       -0.56879356755666463, -0.56918731952397221, -9919582785894.853],
+      [-87.85331, 85.66836, -65.120313040242748,
+       66.48646, 16.09921, -4.888658719272296,
+       17286615.3147144645, 155.58592449699137, 2635887.4729110181,
+       -0.90697975771398578, -0.91095608883042767, 42667211366919.534],
+      [1.74708, 128.32011, -101.584843631173858,
+       -11.16617, 11.87109, -86.325793296437476,
+       12942901.1241347408, 116.650512484301857, 5682744.8413270572,
+       -0.44857868222697644, -0.44824490340007729, 10763055294345.653],
+      [-25.72959, -144.90758, -153.647468693117198,
+       -57.70581, -269.17879, -48.343983158876487,
+       9413446.7452453107, 84.664533838404295, 6356176.6898881281,
+       0.09492245755254703, 0.09737058264766572, 74515122850712.444],
+      [-41.22777, 122.32875, 14.285113402275739,
+       -7.57291, 130.37946, 10.805303085187369,
+       3812686.035106021, 34.34330804743883, 3588703.8812128856,
+       0.82605222593217889, 0.82572158200920196, -2456961531057.857],
+      [11.01307, 138.25278, 79.43682622782374,
+       6.62726, 247.05981, 103.708090215522657,
+       11911190.819018408, 107.341669954114577, 6070904.722786735,
+       -0.29767608923657404, -0.29785143390252321, 17121631423099.696],
+      [-29.47124, 95.14681, -163.779130441688382,
+       -27.46601, -69.15955, -15.909335945554969,
+       13487015.8381145492, 121.294026715742277, 5481428.9945736388,
+       -0.51527225545373252, -0.51556587964721788, 104679964020340.318]];
+
+assert.approx = function(x, y, d) {
+  assert(Math.abs(x-y) <= d, x + " = " + y + " +/- " + d);
+};
+
+describe("GeographicLib", function() {
+  describe("GeodesicTest", function () {
+    var geod = g.WGS84, i,
+        check_geod_inverse, check_geod_direct, check_geod_arcdirect;
+
+    check_geod_inverse = function(l) {
+      var lat1 = l[0], lon1 = l[1], azi1 = l[2],
+          lat2 = l[3], lon2 = l[4], azi2 = l[5],
+          s12 = l[6], a12 = l[7], m12 = l[8],
+          M12 = l[9], M21 = l[10], S12 = l[11],
+          inv = geod.Inverse(lat1, lon1, lat2, G.Math.AngNormalize(lon2),
+                             g.ALL | g.LONG_UNROLL);
+      assert.approx(lon2, inv.lon2, 1e-13);
+      assert.approx(azi1, inv.azi1, 1e-13);
+      assert.approx(azi2, inv.azi2, 1e-13);
+      assert.approx(s12, inv.s12, 1e-8);
+      assert.approx(a12, inv.a12, 1e-13);
+      assert.approx(m12, inv.m12, 1e-8);
+      assert.approx(M12, inv.M12, 1e-15);
+      assert.approx(M21, inv.M21, 1e-15);
+      assert.approx(S12, inv.S12, 0.1);
+    };
+
+    check_geod_direct = function(l) {
+      var lat1 = l[0], lon1 = l[1], azi1 = l[2],
+          lat2 = l[3], lon2 = l[4], azi2 = l[5],
+          s12 = l[6], a12 = l[7], m12 = l[8],
+          M12 = l[9], M21 = l[10], S12 = l[11],
+          dir = geod.Direct(lat1, lon1, azi1, s12, g.ALL | g.LONG_UNROLL);
+      assert.approx(lat2, dir.lat2, 1e-13);
+      assert.approx(lon2, dir.lon2, 1e-13);
+      assert.approx(azi2, dir.azi2, 1e-13);
+      assert.approx(a12, dir.a12, 1e-13);
+      assert.approx(m12, dir.m12, 1e-8);
+      assert.approx(M12, dir.M12, 1e-15);
+      assert.approx(M21, dir.M21, 1e-15);
+      assert.approx(S12, dir.S12, 0.1);
+    };
+
+    check_geod_arcdirect = function(l) {
+      var lat1 = l[0], lon1 = l[1], azi1 = l[2],
+          lat2 = l[3], lon2 = l[4], azi2 = l[5],
+          s12 = l[6], a12 = l[7], m12 = l[8],
+          M12 = l[9], M21 = l[10], S12 = l[11],
+          dir = geod.ArcDirect(lat1, lon1, azi1, a12, g.ALL | g.LONG_UNROLL);
+      assert.approx(lat2, dir.lat2, 1e-13);
+      assert.approx(lon2, dir.lon2, 1e-13);
+      assert.approx(azi2, dir.azi2, 1e-13);
+      assert.approx(s12, dir.s12, 1e-8);
+      assert.approx(m12, dir.m12, 1e-8);
+      assert.approx(M12, dir.M12, 1e-15);
+      assert.approx(M21, dir.M21, 1e-15);
+      assert.approx(S12, dir.S12, 0.1);
+    };
+
+    it("check inverse", function () {
+      for (i = 0; i < testcases.length; ++i) {
+        check_geod_inverse(testcases[i]);
+      }
+    });
+
+    it("check direct", function () {
+      for (i = 0; i < testcases.length; ++i) {
+        check_geod_direct(testcases[i]);
+      }
+    });
+
+    it("check arcdirect", function () {
+      for (i = 0; i < testcases.length; ++i) {
+        check_geod_arcdirect(testcases[i]);
+      }
+    });
+
+  });
+
+  describe("DMSTest", function () {
+    it("check decode", function () {
+      assert.deepEqual(d.Decode("E7:33:36"), d.Decode("-7.56W"));
+    });
+  });
+
+  describe("GeodesicSolve", function () {
+    it("GeodSolve0", function () {
+      var geod = g.WGS84,
+          inv = geod.Inverse(40.6, -73.8, 49.01666667, 2.55);
+      assert.approx(inv.azi1, 53.47022, 0.5e-5);
+      assert.approx(inv.azi2, 111.59367, 0.5e-5);
+      assert.approx(inv.s12, 5853226, 0.5);
+    });
+
+    it("GeodSolve1", function() {
+      var geod = g.WGS84,
+          dir = geod.Direct(40.63972222, -73.77888889, 53.5, 5850e3);
+      assert.approx(dir.lat2, 49.01467, 0.5e-5);
+      assert.approx(dir.lon2, 2.56106, 0.5e-5);
+      assert.approx(dir.azi2, 111.62947, 0.5e-5);
+    });
+
+    it("GeodSolve2", function() {
+      // Check fix for antipodal prolate bug found 2010-09-04
+      var geod = new g.Geodesic(6.4e6, -1/150.0),
+          inv = geod.Inverse(0.07476, 0, -0.07476, 180);
+      assert.approx(inv.azi1, 90.00078, 0.5e-5);
+      assert.approx(inv.azi2, 90.00078, 0.5e-5);
+      assert.approx(inv.s12, 20106193, 0.5);
+      inv = geod.Inverse(0.1, 0, -0.1, 180);
+      assert.approx(inv.azi1, 90.00105, 0.5e-5);
+      assert.approx(inv.azi2, 90.00105, 0.5e-5);
+      assert.approx(inv.s12, 20106193, 0.5);
+    });
+
+    it("GeodSolve4", function() {
+      // Check fix for short line bug found 2010-05-21
+      var geod = g.WGS84,
+          inv = geod.Inverse(36.493349428792, 0, 36.49334942879201, .0000008);
+      assert.approx(inv.s12, 0.072, 0.5e-3);
+    });
+
+    it("GeodSolve5", function() {
+      // Check fix for point2=pole bug found 2010-05-03
+      var geod = g.WGS84,
+          dir = geod.Direct(0.01777745589997, 30, 0, 10e6);
+      assert.approx(dir.lat2, 90, 0.5e-5);
+      if (dir.lon2 < 0) {
+        assert.approx(dir.lon2, -150, 0.5e-5);
+        assert.approx(dir.azi2, -180, 0.5e-5);
+      } else {
+        assert.approx(dir.lon2, 30, 0.5e-5);
+        assert.approx(dir.azi2, 0, 0.5e-5);
+      }
+    });
+
+    it("GeodSolve6", function() {
+      // Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
+      // x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1).
+      var geod = g.WGS84,
+          inv = geod.Inverse(88.202499451857, 0,
+                             -88.202499451857, 179.981022032992859592);
+      assert.approx(inv.s12, 20003898.214, 0.5e-3);
+      inv = geod.Inverse(89.262080389218, 0,
+                         -89.262080389218, 179.992207982775375662);
+      assert.approx(inv.s12, 20003925.854, 0.5e-3);
+      inv = geod.Inverse(89.333123580033, 0,
+                         -89.333123580032997687, 179.99295812360148422);
+      assert.approx(inv.s12, 20003926.881, 0.5e-3);
+    });
+
+    it("GeodSolve9", function() {
+      // Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3)
+      var geod = g.WGS84,
+          inv = geod.Inverse(56.320923501171, 0,
+                             -56.320923501171, 179.664747671772880215);
+      assert.approx(inv.s12, 19993558.287, 0.5e-3);
+    });
+
+    it("GeodSolve10", function() {
+      // Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
+      // 10 rel + debug)
+      var geod = g.WGS84,
+          inv = geod.Inverse(52.784459512564, 0,
+                             -52.784459512563990912, 179.634407464943777557);
+      assert.approx(inv.s12, 19991596.095, 0.5e-3);
+    });
+
+    it("GeodSolve11", function() {
+      // Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
+      // 10 rel + debug)
+      var geod = g.WGS84,
+          inv = geod.Inverse(48.522876735459, 0,
+                             -48.52287673545898293, 179.599720456223079643);
+      assert.approx(inv.s12, 19989144.774, 0.5e-3);
+    });
+
+    it("GeodSolve12", function() {
+      // Check fix for inverse geodesics on extreme prolate/oblate
+      // ellipsoids Reported 2012-08-29 Stefan Guenther
+      // <stefan.gunther at embl.de>; fixed 2012-10-07
+      var geod = new g.Geodesic(89.8, -1.83),
+          inv = geod.Inverse(0, 0, -10, 160);
+      assert.approx(inv.azi1, 120.27, 1e-2);
+      assert.approx(inv.azi2, 105.15, 1e-2);
+      assert.approx(inv.s12, 266.7, 1e-1);
+    });
+
+    it("GeodSolve14", function() {
+      // Check fix for inverse ignoring lon12 = nan
+      var geod = g.WGS84,
+          inv = geod.Inverse(0, 0, 1, NaN);
+      assert(isNaN(inv.azi1));
+      assert(isNaN(inv.azi2));
+      assert(isNaN(inv.s12));
+    });
+
+    it("GeodSolve15", function() {
+      // Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+      // checks that this is fixed.
+      var geod = new g.Geodesic(6.4e6, -1/150.0),
+          dir = geod.Direct(1, 2, 3, 4, g.AREA);
+      assert.approx(dir.S12, 23700, 0.5);
+    });
+
+    it("GeodSolve17", function() {
+      // Check fix for LONG_UNROLL bug found on 2015-05-07
+      var geod = g.WGS84,
+          dir = geod.Direct(40, -75, -10, 2e7, g.STANDARD | g.LONG_UNROLL),
+          line;
+      assert.approx(dir.lat2, -39, 1);
+      assert.approx(dir.lon2, -254, 1);
+      assert.approx(dir.azi2, -170, 1);
+      line = geod.Line(40, -75, -10);
+      dir = line.Position(2e7, g.STANDARD | g.LONG_UNROLL);
+      assert.approx(dir.lat2, -39, 1);
+      assert.approx(dir.lon2, -254, 1);
+      assert.approx(dir.azi2, -170, 1);
+      dir = geod.Direct(40, -75, -10, 2e7);
+      assert.approx(dir.lat2, -39, 1);
+      assert.approx(dir.lon2, 105, 1);
+      assert.approx(dir.azi2, -170, 1);
+      dir = line.Position(2e7);
+      assert.approx(dir.lat2, -39, 1);
+      assert.approx(dir.lon2, 105, 1);
+      assert.approx(dir.azi2, -170, 1);
+    });
+
+    it("GeodSolve26", function() {
+      // Check 0/0 problem with area calculation on sphere 2015-09-08
+      var geod = new g.Geodesic(6.4e6, 0),
+          inv = geod.Inverse(1, 2, 3, 4, g.AREA);
+      assert.approx(inv.S12, 49911046115.0, 0.5);
+    });
+
+    it("GeodSolve28", function() {
+      // Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+      // Java implementation fixed on 2015-05-19).
+      var geod = new g.Geodesic(6.4e6, 0.1),
+          dir = geod.Direct(1, 2, 10, 5e6);
+      assert.approx(dir.a12, 48.55570690, 0.5e-8);
+    });
+
+    it("GeodSolve29", function() {
+      // Check longitude unrolling with inverse calculation 2015-09-16
+      var geod = g.WGS84,
+          dir = geod.Inverse(0, 539, 0, 181);
+      assert.approx(dir.lon1, 179, 1e-10);
+      assert.approx(dir.lon2, -179, 1e-10);
+      assert.approx(dir.s12, 222639, 0.5);
+      dir = geod.Inverse(0, 539, 0, 181, g.STANDARD | g.LONG_UNROLL);
+      assert.approx(dir.lon1, 539, 1e-10);
+      assert.approx(dir.lon2, 541, 1e-10);
+      assert.approx(dir.s12, 222639, 0.5);
+    });
+
+    it("GeodSolve33", function() {
+      // Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+      // sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+      // fmod(-0.0, 360.0) = +0.0.
+      var geod = g.WGS84,
+          inv = geod.Inverse(0, 0, 0, 179);
+      assert.approx(inv.azi1, 90.00000, 0.5e-5);
+      assert.approx(inv.azi2, 90.00000, 0.5e-5);
+      assert.approx(inv.s12, 19926189, 0.5);
+      inv = geod.Inverse(0, 0, 0, 179.5);
+      assert.approx(inv.azi1, 55.96650, 0.5e-5);
+      assert.approx(inv.azi2, 124.03350, 0.5e-5);
+      assert.approx(inv.s12, 19980862, 0.5);
+      inv = geod.Inverse(0, 0, 0, 180);
+      assert.approx(inv.azi1, 0.00000, 0.5e-5);
+      assert.approx(inv.azi2, -180.00000, 0.5e-5);
+      assert.approx(inv.s12, 20003931, 0.5);
+      inv = geod.Inverse(0, 0, 1, 180);
+      assert.approx(inv.azi1, 0.00000, 0.5e-5);
+      assert.approx(inv.azi2, -180.00000, 0.5e-5);
+      assert.approx(inv.s12, 19893357, 0.5);
+      geod = new g.Geodesic(6.4e6, 0);
+      inv = geod.Inverse(0, 0, 0, 179);
+      assert.approx(inv.azi1, 90.00000, 0.5e-5);
+      assert.approx(inv.azi2, 90.00000, 0.5e-5);
+      assert.approx(inv.s12, 19994492, 0.5);
+      inv = geod.Inverse(0, 0, 0, 180);
+      assert.approx(inv.azi1, 0.00000, 0.5e-5);
+      assert.approx(inv.azi2, -180.00000, 0.5e-5);
+      assert.approx(inv.s12, 20106193, 0.5);
+      inv = geod.Inverse(0, 0, 1, 180);
+      assert.approx(inv.azi1, 0.00000, 0.5e-5);
+      assert.approx(inv.azi2, -180.00000, 0.5e-5);
+      assert.approx(inv.s12, 19994492, 0.5);
+      geod = new g.Geodesic(6.4e6, -1/300.0);
+      inv = geod.Inverse(0, 0, 0, 179);
+      assert.approx(inv.azi1, 90.00000, 0.5e-5);
+      assert.approx(inv.azi2, 90.00000, 0.5e-5);
+      assert.approx(inv.s12, 19994492, 0.5);
+      inv = geod.Inverse(0, 0, 0, 180);
+      assert.approx(inv.azi1, 90.00000, 0.5e-5);
+      assert.approx(inv.azi2, 90.00000, 0.5e-5);
+      assert.approx(inv.s12, 20106193, 0.5);
+      inv = geod.Inverse(0, 0, 0.5, 180);
+      assert.approx(inv.azi1, 33.02493, 0.5e-5);
+      assert.approx(inv.azi2, 146.97364, 0.5e-5);
+      assert.approx(inv.s12, 20082617, 0.5);
+      inv = geod.Inverse(0, 0, 1, 180);
+      assert.approx(inv.azi1, 0.00000, 0.5e-5);
+      assert.approx(inv.azi2, -180.00000, 0.5e-5);
+      assert.approx(inv.s12, 20027270, 0.5);
+    });
+
+    it("GeodSolve55", function() {
+      // Check fix for nan + point on equator or pole not returning all nans in
+      // Geodesic::Inverse, found 2015-09-23.
+      var geod = g.WGS84,
+          inv = geod.Inverse(NaN, 0, 0, 90);
+      assert(isNaN(inv.azi1));
+      assert(isNaN(inv.azi2));
+      assert(isNaN(inv.s12));
+      inv = geod.Inverse(NaN, 0, 90, 9);
+      assert(isNaN(inv.azi1));
+      assert(isNaN(inv.azi2));
+      assert(isNaN(inv.s12));
+    });
+  });
+
+  describe("Planimeter", function () {
+
+    var geod = g.WGS84,
+        polygon = geod.Polygon(false),
+        polyline = geod.Polygon(true),
+        Planimeter, PolyLength;
+
+    Planimeter = function(points) {
+      var i;
+      polygon.Clear();
+      for (i = 0; i < points.length; ++i) {
+        polygon.AddPoint(points[i][0], points[i][1]);
+      }
+      return polygon.Compute(false, true);
+    }
+
+    PolyLength = function(points) {
+      var i;
+      polyline.Clear();
+      for (i = 0; i < points.length; ++i) {
+        polyline.AddPoint(points[i][0], points[i][1]);
+      }
+      return polyline.Compute(false, true);
+    }
+
+    it("Planimeter0", function() {
+      // Check fix for pole-encircling bug found 2011-03-16
+      var points, a;
+      points = [[89, 0], [89, 90], [89, 180], [89, 270]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 631819.8745, 1e-4);
+      assert.approx(a.area, 24952305678.0, 1);
+
+      points = [[-89, 0], [-89, 90], [-89, 180], [-89, 270]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 631819.8745, 1e-4);
+      assert.approx(a.area, -24952305678.0, 1);
+
+      points = [[0, -1], [-1, 0], [0, 1], [1, 0]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 627598.2731, 1e-4);
+      assert.approx(a.area, 24619419146.0, 1);
+
+      points = [[90, 0], [0, 0], [0, 90]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 30022685, 1);
+      assert.approx(a.area, 63758202715511.0, 1);
+      a = PolyLength(points);
+      assert.approx(a.perimeter, 20020719, 1);
+      assert(isNaN(a.area));
+    });
+
+    it("Planimeter5", function() {
+      // Check fix for Planimeter pole crossing bug found 2011-06-24
+      var points, a;
+      points = [[89, 0.1], [89, 90.1], [89, -179.9]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 539297, 1);
+      assert.approx(a.area, 12476152838.5, 1);
+    });
+
+    it("Planimeter6", function() {
+      // Check fix for Planimeter lon12 rounding bug found 2012-12-03
+      var points, a;
+      points = [[9, -0.00000000000001], [9, 180], [9, 0]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 36026861, 1);
+      assert.approx(a.area, 0, 1);
+      points = [[9, 0.00000000000001], [9, 0], [9, 180]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 36026861, 1);
+      assert.approx(a.area, 0, 1);
+      points = [[9, 0.00000000000001], [9, 180], [9, 0]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 36026861, 1);
+      assert.approx(a.area, 0, 1);
+      points = [[9, -0.00000000000001], [9, 0], [9, 180]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 36026861, 1);
+      assert.approx(a.area, 0, 1);
+    });
+
+    it("Planimeter12", function() {
+      // Area of arctic circle (not really -- adjunct to rhumb-area test)
+      var points, a;
+      points = [[66.562222222, 0], [66.562222222, 180]];
+      a = Planimeter(points);
+      assert.approx(a.perimeter, 10465729, 1);
+      assert.approx(a.area, 0, 1);
+    });
+
+    it("Planimeter13", function() {
+      // Check encircling pole twice
+      var points, a;
+      points = [[89,-360], [89,-240], [89,-120], [89,0], [89,120], [89,240]];
+      a =  Planimeter(points);
+      assert.approx(a.perimeter, 1160741, 1);
+      assert.approx(a.area, 32415230256.0, 1);
+    });
+
+  });
+});
diff --git a/legacy/C/CMakeLists.txt b/legacy/C/CMakeLists.txt
index 8fda0ef..5241df4 100644
--- a/legacy/C/CMakeLists.txt
+++ b/legacy/C/CMakeLists.txt
@@ -13,10 +13,13 @@ if (WIN32)
   set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
 else ()
   set (CMAKE_C_FLAGS
-    "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-array-bounds -pedantic -ansi")
+    "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-array-bounds -pedantic")
+  if (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi")
+  endif ()
 endif ()
 
-set (TOOLS direct inverse planimeter)
+set (TOOLS direct inverse planimeter geodtest)
 
 foreach (TOOL ${TOOLS})
   add_executable (${TOOL} ${TOOL}.c geodesic.c geodesic.h)
@@ -24,3 +27,9 @@ foreach (TOOL ${TOOLS})
     target_link_libraries (${TOOL} m)
   endif ()
 endforeach ()
+
+# Turn on testing
+enable_testing ()
+
+# Run the test suite
+add_test (NAME geodtest COMMAND geodtest)
diff --git a/legacy/C/geodesic.c b/legacy/C/geodesic.c
index 2dee45e..13acba3 100644
--- a/legacy/C/geodesic.c
+++ b/legacy/C/geodesic.c
@@ -146,11 +146,12 @@ static real polyval(int N, const real p[], real x) {
   return y;
 }
 
-static real minx(real x, real y)
-{ return x < y ? x : y; }
+/* mimic C++ std::min and std::max */
+static real minx(real a, real b)
+{ return (b < a) ? b : a; }
 
-static real maxx(real x, real y)
-{ return x > y ? x : y; }
+static real maxx(real a, real b)
+{ return (a < b) ? b : a; }
 
 static void swapx(real* x, real* y)
 { real t = *x; *x = *y; *y = t; }
@@ -288,7 +289,7 @@ static void accneg(real s[]);
 void geod_init(struct geod_geodesic* g, real a, real f) {
   if (!init) Init();
   g->a = a;
-  g->f = f <= 1 ? f : 1/f;
+  g->f = f;
   g->f1 = 1 - g->f;
   g->e2 = g->f * (2 - g->f);
   g->ep2 = g->e2 / sq(g->f1);   /* e2 / (1 - e2) */
@@ -663,8 +664,9 @@ real geod_geninverse(const struct geod_geodesic* g,
   /* If really close to the equator, treat as on equator. */
   lat1 = AngRound(LatFix(lat1));
   lat2 = AngRound(LatFix(lat2));
-  /* Swap points so that point with higher (abs) latitude is point 1 */
-  swapp = fabs(lat1) >= fabs(lat2) ? 1 : -1;
+  /* Swap points so that point with higher (abs) latitude is point 1
+   * If one latitude is a nan, then it becomes lat1. */
+  swapp = fabs(lat1) < fabs(lat2) ? -1 : 1;
   if (swapp < 0) {
     lonsign *= -1;
     swapx(&lat1, &lat2);
@@ -731,7 +733,7 @@ real geod_geninverse(const struct geod_geodesic* g,
     ssig2 = sbet2; csig2 = calp2 * cbet2;
 
     /* sig12 = sig2 - sig1 */
-    sig12 = atan2(maxx(csig1 * ssig2 - ssig1 * csig2, (real)(0)),
+    sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2),
                   csig1 * csig2 + ssig1 * ssig2);
     Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
             cbet1, cbet2, &s12x, &m12x, 0,
@@ -1345,11 +1347,11 @@ real Lambda12(const struct geod_geodesic* g,
   /* norm2(&somg2, &comg2); -- don't need to normalize! */
 
   /* sig12 = sig2 - sig1, limit to [0, pi] */
-  sig12 = atan2(maxx(csig1 * ssig2 - ssig1 * csig2, (real)(0)),
+  sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2),
                 csig1 * csig2 + ssig1 * ssig2);
 
   /* omg12 = omg2 - omg1, limit to [0, pi] */
-  omg12 = atan2(maxx(comg1 * somg2 - somg1 * comg2, (real)(0)),
+  omg12 = atan2(maxx((real)(0), comg1 * somg2 - somg1 * comg2),
                 comg1 * comg2 + somg1 * somg2);
   k2 = sq(calp0) * g->ep2;
   eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2);
@@ -1699,8 +1701,12 @@ void accneg(real s[]) {
 }
 
 void geod_polygon_init(struct geod_polygon* p, boolx polylinep) {
-  p->lat0 = p->lon0 = p->lat = p->lon = NaN;
   p->polyline = (polylinep != 0);
+  geod_polygon_clear(p);
+}
+
+void geod_polygon_clear(struct geod_polygon* p) {
+  p->lat0 = p->lon0 = p->lat = p->lon = NaN;
   accini(p->P);
   accini(p->A);
   p->num = p->crossings = 0;
diff --git a/legacy/C/geodesic.h b/legacy/C/geodesic.h
index 7bd8270..7340dd5 100644
--- a/legacy/C/geodesic.h
+++ b/legacy/C/geodesic.h
@@ -75,26 +75,25 @@
  * (obviously) uniquely defined.  However, in a few special cases there are
  * multiple azimuths which yield the same shortest distance.  Here is a
  * catalog of those cases:
- * - \e lat1 = −\e lat2 (with neither point at a pole).  If \e azi1 =
- *   \e azi2, the geodesic is unique.  Otherwise there are two geodesics
- *   and the second one is obtained by setting [\e azi1, \e azi2] = [\e
- *   azi2, \e azi1], [\e M12, \e M21] = [\e M21, \e M12], \e S12 =
- *   −\e S12.  (This occurs when the longitude difference is near
- *   ±180° for oblate ellipsoids.)
- * - \e lon2 = \e lon1 ± 180° (with neither point at a pole).
- *   If \e azi1 = 0° or ±180°, the geodesic is unique.
- *   Otherwise there are two geodesics and the second one is obtained by
- *   setting [\e azi1, \e azi2] = [−\e azi1, −\e azi2], \e S12
- *   = −\e S12.  (This occurs when \e lat2 is near −\e lat1 for
+ * - \e lat1 = −\e lat2 (with neither point at a pole).  If \e azi1 = \e
+ *   azi2, the geodesic is unique.  Otherwise there are two geodesics and the
+ *   second one is obtained by setting [\e azi1, \e azi2] → [\e azi2, \e
+ *   azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 → −\e
+ *   S12.  (This occurs when the longitude difference is near ±180°
+ *   for oblate ellipsoids.)
+ * - \e lon2 = \e lon1 ± 180° (with neither point at a pole).  If \e
+ *   azi1 = 0° or ±180°, the geodesic is unique.  Otherwise
+ *   there are two geodesics and the second one is obtained by setting [\e
+ *   azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 →
+ *   −\e S12.  (This occurs when \e lat2 is near −\e lat1 for
  *   prolate ellipsoids.)
- * - Points 1 and 2 at opposite poles.  There are infinitely many
- *   geodesics which can be generated by setting [\e azi1, \e azi2] =
- *   [\e azi1, \e azi2] + [\e d, −\e d], for arbitrary \e d.  (For
- *   spheres, this prescription applies when points 1 and 2 are
- *   antipodal.)
- * - \e s12 = 0 (coincident points).  There are infinitely many geodesics
- *   which can be generated by setting [\e azi1, \e azi2] = [\e azi1, \e
- *   azi2] + [\e d, \e d], for arbitrary \e d.
+ * - Points 1 and 2 at opposite poles.  There are infinitely many geodesics
+ *   which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e
+ *   azi2] + [\e d, −\e d], for arbitrary \e d.  (For spheres, this
+ *   prescription applies when points 1 and 2 are antipodal.)
+ * - \e s12 = 0 (coincident points).  There are infinitely many geodesics which
+ *   can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e azi2] +
+ *   [\e d, \e d], for arbitrary \e d.
  *
  * These routines are a simple transcription of the corresponding C++ classes
  * in <a href="http://geographiclib.sf.net"> GeographicLib</a>.  The "class
@@ -113,7 +112,7 @@
  * http://geographiclib.sourceforge.net/
  *
  * This library was distributed with
- * <a href="../index.html">GeographicLib</a> 1.44.
+ * <a href="../index.html">GeographicLib</a> 1.45.
  **********************************************************************/
 
 #if !defined(GEODESIC_H)
@@ -128,7 +127,7 @@
  * The minor version of the geodesic library.  (This tracks the version of
  * GeographicLib.)
  **********************************************************************/
-#define GEODESIC_VERSION_MINOR 44
+#define GEODESIC_VERSION_MINOR 45
 /**
  * The patch level of the geodesic library.  (This tracks the version of
  * GeographicLib.)
@@ -565,6 +564,13 @@ extern "C" {
   void geod_polygon_init(struct geod_polygon* p, int polylinep);
 
   /**
+   * Clear the polygon, allowing a new polygon to be started.
+   *
+   * @param[in,out] p a pointer to the object to be cleared.
+   **********************************************************************/
+  void geod_polygon_clear(struct geod_polygon* p);
+
+  /**
    * Add a point to the polygon or polyline.
    *
    * @param[in] g a pointer to the geod_geodesic object specifying the
@@ -630,6 +636,8 @@ extern "C" {
    * vertex.  Set \e pA or \e pP to zero, if you do not want the corresponding
    * quantity returned.
    *
+   * More points can be added to the polygon after this call.
+   *
    * Example, compute the perimeter and area of the geodesic triangle with
    * vertices (0°N,0°E), (0°N,90°E), (90°N,0°E).
    @code{.c}
diff --git a/legacy/C/geodtest.c b/legacy/C/geodtest.c
new file mode 100644
index 0000000..7500961
--- /dev/null
+++ b/legacy/C/geodtest.c
@@ -0,0 +1,635 @@
+/**
+ * \file geodtest.c
+ * \brief Test suite for the geodesic routines in C
+ *
+ * Run these tests by configuring with cmake and running "make test".
+ *
+ * Copyright (c) Charles Karney (2015) <charles at karney.com> and licensed under
+ * the MIT/X11 License.  For more information, see
+ * http://geographiclib.sourceforge.net/
+ **********************************************************************/
+
+/** @cond SKIP */
+
+#include "geodesic.h"
+#include <stdio.h>
+#include <math.h>
+
+double wgs84_a = 6378137, wgs84_f = 1/298.257223563; /* WGS84 */
+
+int assertEquals(double x, double y, double d) {
+  if (fabs(x - y) <= d)
+    return 0;
+  printf("assertEquals fails: %.7g != %.7g +/- %.7g\n", x, y, d);
+  return 1;
+}
+
+const int ncases = 20;
+double testcases[20][12] = {
+  {35.60777, -139.44815, 111.098748429560326,
+   -11.17491, -69.95921, 129.289270889708762,
+   8935244.5604818305, 80.50729714281974, 6273170.2055303837,
+   0.16606318447386067, 0.16479116945612937, 12841384694976.432},
+  {55.52454, 106.05087, 22.020059880982801,
+   77.03196, 197.18234, 109.112041110671519,
+   4105086.1713924406, 36.892740690445894, 3828869.3344387607,
+   0.80076349608092607, 0.80101006984201008, 61674961290615.615},
+  {-21.97856, 142.59065, -32.44456876433189,
+   41.84138, 98.56635, -41.84359951440466,
+   8394328.894657671, 75.62930491011522, 6161154.5773110616,
+   0.24816339233950381, 0.24930251203627892, -6637997720646.717},
+  {-66.99028, 112.2363, 173.73491240878403,
+   -12.70631, 285.90344, 2.512956620913668,
+   11150344.2312080241, 100.278634181155759, 6289939.5670446687,
+   -0.17199490274700385, -0.17722569526345708, -121287239862139.744},
+  {-17.42761, 173.34268, -159.033557661192928,
+   -15.84784, 5.93557, -20.787484651536988,
+   16076603.1631180673, 144.640108810286253, 3732902.1583877189,
+   -0.81273638700070476, -0.81299800519154474, 97825992354058.708},
+  {32.84994, 48.28919, 150.492927788121982,
+   -56.28556, 202.29132, 48.113449399816759,
+   16727068.9438164461, 150.565799985466607, 3147838.1910180939,
+   -0.87334918086923126, -0.86505036767110637, -72445258525585.010},
+  {6.96833, 52.74123, 92.581585386317712,
+   -7.39675, 206.17291, 90.721692165923907,
+   17102477.2496958388, 154.147366239113561, 2772035.6169917581,
+   -0.89991282520302447, -0.89986892177110739, -1311796973197.995},
+  {-50.56724, -16.30485, -105.439679907590164,
+   -33.56571, -94.97412, -47.348547835650331,
+   6455670.5118668696, 58.083719495371259, 5409150.7979815838,
+   0.53053508035997263, 0.52988722644436602, 41071447902810.047},
+  {-58.93002, -8.90775, 140.965397902500679,
+   -8.91104, 133.13503, 19.255429433416599,
+   11756066.0219864627, 105.755691241406877, 6151101.2270708536,
+   -0.26548622269867183, -0.27068483874510741, -86143460552774.735},
+  {-68.82867, -74.28391, 93.774347763114881,
+   -50.63005, -8.36685, 34.65564085411343,
+   3956936.926063544, 35.572254987389284, 3708890.9544062657,
+   0.81443963736383502, 0.81420859815358342, -41845309450093.787},
+  {-10.62672, -32.0898, -86.426713286747751,
+   5.883, -134.31681, -80.473780971034875,
+   11470869.3864563009, 103.387395634504061, 6184411.6622659713,
+   -0.23138683500430237, -0.23155097622286792, 4198803992123.548},
+  {-21.76221, 166.90563, 29.319421206936428,
+   48.72884, 213.97627, 43.508671946410168,
+   9098627.3986554915, 81.963476716121964, 6299240.9166992283,
+   0.13965943368590333, 0.14152969707656796, 10024709850277.476},
+  {-19.79938, -174.47484, 71.167275780171533,
+   -11.99349, -154.35109, 65.589099775199228,
+   2319004.8601169389, 20.896611684802389, 2267960.8703918325,
+   0.93427001867125849, 0.93424887135032789, -3935477535005.785},
+  {-11.95887, -116.94513, 92.712619830452549,
+   4.57352, 7.16501, 78.64960934409585,
+   13834722.5801401374, 124.688684161089762, 5228093.177931598,
+   -0.56879356755666463, -0.56918731952397221, -9919582785894.853},
+  {-87.85331, 85.66836, -65.120313040242748,
+   66.48646, 16.09921, -4.888658719272296,
+   17286615.3147144645, 155.58592449699137, 2635887.4729110181,
+   -0.90697975771398578, -0.91095608883042767, 42667211366919.534},
+  {1.74708, 128.32011, -101.584843631173858,
+   -11.16617, 11.87109, -86.325793296437476,
+   12942901.1241347408, 116.650512484301857, 5682744.8413270572,
+   -0.44857868222697644, -0.44824490340007729, 10763055294345.653},
+  {-25.72959, -144.90758, -153.647468693117198,
+   -57.70581, -269.17879, -48.343983158876487,
+   9413446.7452453107, 84.664533838404295, 6356176.6898881281,
+   0.09492245755254703, 0.09737058264766572, 74515122850712.444},
+  {-41.22777, 122.32875, 14.285113402275739,
+   -7.57291, 130.37946, 10.805303085187369,
+   3812686.035106021, 34.34330804743883, 3588703.8812128856,
+   0.82605222593217889, 0.82572158200920196, -2456961531057.857},
+  {11.01307, 138.25278, 79.43682622782374,
+   6.62726, 247.05981, 103.708090215522657,
+   11911190.819018408, 107.341669954114577, 6070904.722786735,
+   -0.29767608923657404, -0.29785143390252321, 17121631423099.696},
+  {-29.47124, 95.14681, -163.779130441688382,
+   -27.46601, -69.15955, -15.909335945554969,
+   13487015.8381145492, 121.294026715742277, 5481428.9945736388,
+   -0.51527225545373252, -0.51556587964721788, 104679964020340.318}};
+
+int testinverse() {
+  double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12;
+  double azi1a, azi2a, s12a, a12a, m12a, M12a, M21a, S12a;
+  struct geod_geodesic g;
+  int i, result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  for (i = 0; i < ncases; ++i) {
+    lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2];
+    lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5];
+    s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8];
+    M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11];
+    a12a = geod_geninverse(&g, lat1, lon1, lat2, lon2, &s12a, &azi1a, &azi2a,
+               &m12a, &M12a, &M21a, &S12a);
+    result += assertEquals(azi1, azi1a, 1e-13);
+    result += assertEquals(azi2, azi2a, 1e-13);
+    result += assertEquals(s12, s12a, 1e-8);
+    result += assertEquals(a12, a12a, 1e-13);
+    result += assertEquals(m12, m12a, 1e-8);
+    result += assertEquals(M12, M12a, 1e-15);
+    result += assertEquals(M21, M21a, 1e-15);
+    result += assertEquals(S12, S12a, 0.1);
+  }
+  return result;
+}
+
+int testdirect() {
+  double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12;
+  double lat2a, lon2a, azi2a, a12a, m12a, M12a, M21a, S12a;
+  struct geod_geodesic g;
+  int i, result = 0;
+  unsigned flags = GEOD_LONG_UNROLL;
+  geod_init(&g, wgs84_a, wgs84_f);
+  for (i = 0; i < ncases; ++i) {
+    lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2];
+    lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5];
+    s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8];
+    M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11];
+    a12a = geod_gendirect(&g, lat1, lon1, azi1, flags, s12,
+              &lat2a, &lon2a, &azi2a, 0,
+              &m12a, &M12a, &M21a, &S12a);
+    result += assertEquals(lat2, lat2a, 1e-13);
+    result += assertEquals(lon2, lon2a, 1e-13);
+    result += assertEquals(azi2, azi2a, 1e-13);
+    result += assertEquals(a12, a12a, 1e-13);
+    result += assertEquals(m12, m12a, 1e-8);
+    result += assertEquals(M12, M12a, 1e-15);
+    result += assertEquals(M21, M21a, 1e-15);
+    result += assertEquals(S12, S12a, 0.1);
+  }
+  return result;
+}
+
+int testarcdirect() {
+  double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12;
+  double lat2a, lon2a, azi2a, s12a, m12a, M12a, M21a, S12a;
+  struct geod_geodesic g;
+  int i, result = 0;
+  unsigned flags = GEOD_ARCMODE | GEOD_LONG_UNROLL;
+  geod_init(&g, wgs84_a, wgs84_f);
+  for (i = 0; i < ncases; ++i) {
+    lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2];
+    lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5];
+    s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8];
+    M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11];
+    geod_gendirect(&g, lat1, lon1, azi1, flags, a12,
+                   &lat2a, &lon2a, &azi2a, &s12a, &m12a, &M12a, &M21a, &S12a);
+    result += assertEquals(lat2, lat2a, 1e-13);
+    result += assertEquals(lon2, lon2a, 1e-13);
+    result += assertEquals(azi2, azi2a, 1e-13);
+    result += assertEquals(s12, s12a, 1e-8);
+    result += assertEquals(m12, m12a, 1e-8);
+    result += assertEquals(M12, M12a, 1e-15);
+    result += assertEquals(M21, M21a, 1e-15);
+    result += assertEquals(S12, S12a, 0.1);
+  }
+  return result;
+}
+
+int GeodSolve0() {
+  double azi1, azi2, s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 40.6, -73.8, 49.01666667, 2.55, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 53.47022, 0.5e-5);
+  result += assertEquals(azi2, 111.59367, 0.5e-5);
+  result += assertEquals(s12, 5853226, 0.5);
+  return result;
+}
+
+int GeodSolve1() {
+  double lat2, lon2, azi2;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_direct(&g, 40.63972222, -73.77888889, 53.5, 5850e3,
+              &lat2, &lon2, &azi2);
+  result += assertEquals(lat2, 49.01467, 0.5e-5);
+  result += assertEquals(lon2, 2.56106, 0.5e-5);
+  result += assertEquals(azi2, 111.62947, 0.5e-5);
+  return result;
+}
+
+int GeodSolve2() {
+  /* Check fix for antipodal prolate bug found 2010-09-04 */
+  double azi1, azi2, s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, 6.4e6, -1/150.0);
+  geod_inverse(&g, 0.07476, 0, -0.07476, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00078, 0.5e-5);
+  result += assertEquals(azi2, 90.00078, 0.5e-5);
+  result += assertEquals(s12, 20106193, 0.5);
+  geod_inverse(&g, 0.1, 0, -0.1, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00105, 0.5e-5);
+  result += assertEquals(azi2, 90.00105, 0.5e-5);
+  result += assertEquals(s12, 20106193, 0.5);
+  return result;
+}
+
+int GeodSolve4() {
+  /* Check fix for short line bug found 2010-05-21 */
+  double s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 36.493349428792, 0, 36.49334942879201, .0000008,
+               &s12, 0, 0);
+  result += assertEquals(s12, 0.072, 0.5e-3);
+  return result;
+}
+
+int GeodSolve5() {
+  /* Check fix for point2=pole bug found 2010-05-03 */
+  double lat2, lon2, azi2;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_direct(&g, 0.01777745589997, 30, 0, 10e6, &lat2, &lon2, &azi2);
+  result += assertEquals(lat2, 90, 0.5e-5);
+  if (lon2 < 0) {
+    result += assertEquals(lon2, -150, 0.5e-5);
+    result += assertEquals(azi2, -180, 0.5e-5);
+  } else {
+    result += assertEquals(lon2, 30, 0.5e-5);
+    result += assertEquals(azi2, 0, 0.5e-5);
+  }
+  return result;
+}
+
+int GeodSolve6() {
+  /* Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
+   * x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1). */
+  double s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 88.202499451857, 0,
+               -88.202499451857, 179.981022032992859592, &s12, 0, 0);
+  result += assertEquals(s12, 20003898.214, 0.5e-3);
+  geod_inverse(&g, 89.262080389218, 0,
+               -89.262080389218, 179.992207982775375662, &s12, 0, 0);
+  result += assertEquals(s12, 20003925.854, 0.5e-3);
+  geod_inverse(&g, 89.333123580033, 0,
+               -89.333123580032997687, 179.99295812360148422, &s12, 0, 0);
+  result += assertEquals(s12, 20003926.881, 0.5e-3);
+  return result;
+}
+
+int GeodSolve9() {
+  /* Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3) */
+  double s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 56.320923501171, 0,
+               -56.320923501171, 179.664747671772880215, &s12, 0, 0);
+  result += assertEquals(s12, 19993558.287, 0.5e-3);
+  return result;
+}
+
+int GeodSolve10() {
+  /* Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
+   * 10 rel + debug) */
+  double s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 52.784459512564, 0,
+               -52.784459512563990912, 179.634407464943777557, &s12, 0, 0);
+  result += assertEquals(s12, 19991596.095, 0.5e-3);
+  return result;
+}
+
+int GeodSolve11() {
+  /* Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
+   * 10 rel + debug) */
+  double s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 48.522876735459, 0,
+               -48.52287673545898293, 179.599720456223079643, &s12, 0, 0);
+  result += assertEquals(s12, 19989144.774, 0.5e-3);
+  return result;
+}
+
+int GeodSolve12() {
+  /* Check fix for inverse geodesics on extreme prolate/oblate
+   * ellipsoids Reported 2012-08-29 Stefan Guenther
+   * <stefan.gunther at embl.de>; fixed 2012-10-07 */
+  double azi1, azi2, s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, 89.8, -1.83);
+  geod_inverse(&g, 0, 0, -10, 160, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 120.27, 1e-2);
+  result += assertEquals(azi2, 105.15, 1e-2);
+  result += assertEquals(s12, 266.7, 1e-1);
+  return result;
+}
+
+int GeodSolve14() {
+  /* Check fix for inverse ignoring lon12 = nan */
+  double azi1, azi2, s12, nan = sqrt(-1.0);
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 0, 0, 1, nan, &s12, &azi1, &azi2);
+  result += azi1 == azi1 ? 1 : 0;
+  result += azi2 == azi2 ? 1 : 0;
+  result += s12 == s12 ? 1 : 0;
+  return result;
+}
+
+int GeodSolve15() {
+  /* Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+   * checks that this is fixed. */
+  double S12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, 6.4e6, -1/150.0);
+  geod_gendirect(&g, 1, 2, 3, 0, 4,
+                 0, 0, 0, 0, 0, 0, 0, &S12);
+  result += assertEquals(S12, 23700, 0.5);
+  return result;
+}
+
+int GeodSolve17() {
+  /* Check fix for LONG_UNROLL bug found on 2015-05-07 */
+  double lat2, lon2, azi2;
+  struct geod_geodesic g;
+  struct geod_geodesicline l;
+  int result = 0;
+  unsigned flags = GEOD_LONG_UNROLL;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_gendirect(&g, 40, -75, -10, flags, 2e7,
+                 &lat2, &lon2, &azi2, 0, 0, 0, 0, 0);
+  result += assertEquals(lat2, -39, 1);
+  result += assertEquals(lon2, -254, 1);
+  result += assertEquals(azi2, -170, 1);
+  geod_lineinit(&l, &g, 40, -75, -10, 0);
+  geod_genposition(&l, flags, 2e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0);
+  result += assertEquals(lat2, -39, 1);
+  result += assertEquals(lon2, -254, 1);
+  result += assertEquals(azi2, -170, 1);
+  geod_direct(&g, 40, -75, -10, 2e7, &lat2, &lon2, &azi2);
+  result += assertEquals(lat2, -39, 1);
+  result += assertEquals(lon2, 105, 1);
+  result += assertEquals(azi2, -170, 1);
+  geod_position(&l, 2e7, &lat2, &lon2, &azi2);
+  result += assertEquals(lat2, -39, 1);
+  result += assertEquals(lon2, 105, 1);
+  result += assertEquals(azi2, -170, 1);
+  return result;
+}
+
+int GeodSolve26() {
+  /* Check 0/0 problem with area calculation on sphere 2015-09-08 */
+  double S12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, 6.4e6, 0);
+  geod_geninverse(&g, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, &S12);
+  result += assertEquals(S12, 49911046115.0, 0.5);
+  return result;
+}
+
+int GeodSolve28() {
+  /* Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+   * Java implementation fixed on 2015-05-19). */
+  double a12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, 6.4e6, 0.1);
+  a12 = geod_gendirect(&g, 1, 2, 10, 0, 5e6, 0, 0, 0, 0, 0, 0, 0, 0);
+  result += assertEquals(a12, 48.55570690, 0.5e-8);
+  return result;
+}
+
+int GeodSolve33() {
+  /* Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+   * sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+   * fmod(-0.0, 360.0) = +0.0. */
+  double azi1, azi2, s12;
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00000, 0.5e-5);
+  result += assertEquals(azi2, 90.00000, 0.5e-5);
+  result += assertEquals(s12, 19926189, 0.5);
+  geod_inverse(&g, 0, 0, 0, 179.5, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 55.96650, 0.5e-5);
+  result += assertEquals(azi2, 124.03350, 0.5e-5);
+  result += assertEquals(s12, 19980862, 0.5);
+  geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 0.00000, 0.5e-5);
+  result += assertEquals(azi2, -180.00000, 0.5e-5);
+  result += assertEquals(s12, 20003931, 0.5);
+  geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 0.00000, 0.5e-5);
+  result += assertEquals(azi2, -180.00000, 0.5e-5);
+  result += assertEquals(s12, 19893357, 0.5);
+  geod_init(&g, 6.4e6, 0);
+  geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00000, 0.5e-5);
+  result += assertEquals(azi2, 90.00000, 0.5e-5);
+  result += assertEquals(s12, 19994492, 0.5);
+  geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 0.00000, 0.5e-5);
+  result += assertEquals(azi2, -180.00000, 0.5e-5);
+  result += assertEquals(s12, 20106193, 0.5);
+  geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 0.00000, 0.5e-5);
+  result += assertEquals(azi2, -180.00000, 0.5e-5);
+  result += assertEquals(s12, 19994492, 0.5);
+  geod_init(&g, 6.4e6, -1/300.0);
+  geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00000, 0.5e-5);
+  result += assertEquals(azi2, 90.00000, 0.5e-5);
+  result += assertEquals(s12, 19994492, 0.5);
+  geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 90.00000, 0.5e-5);
+  result += assertEquals(azi2, 90.00000, 0.5e-5);
+  result += assertEquals(s12, 20106193, 0.5);
+  geod_inverse(&g, 0, 0, 0.5, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 33.02493, 0.5e-5);
+  result += assertEquals(azi2, 146.97364, 0.5e-5);
+  result += assertEquals(s12, 20082617, 0.5);
+  geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2);
+  result += assertEquals(azi1, 0.00000, 0.5e-5);
+  result += assertEquals(azi2, -180.00000, 0.5e-5);
+  result += assertEquals(s12, 20027270, 0.5);
+
+  return result;
+}
+
+int GeodSolve55() {
+  /* Check fix for nan + point on equator or pole not returning all nans in
+   * Geodesic::Inverse, found 2015-09-23. */
+  double azi1, azi2, s12, nan = sqrt(-1.0);
+  struct geod_geodesic g;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  geod_inverse(&g, nan, 0, 0, 90, &s12, &azi1, &azi2);
+  result += azi1 == azi1 ? 1 : 0;
+  result += azi2 == azi2 ? 1 : 0;
+  result += s12 == s12 ? 1 : 0;
+  geod_inverse(&g, nan, 0, 90, 9, &s12, &azi1, &azi2);
+  result += azi1 == azi1 ? 1 : 0;
+  result += azi2 == azi2 ? 1 : 0;
+  result += s12 == s12 ? 1 : 0;
+  return result;
+}
+
+void planimeter(const struct geod_geodesic* g, double points[][2], int N,
+                double* perimeter, double* area) {
+  struct geod_polygon p;
+  int i;
+  geod_polygon_init(&p, 0);
+  for (i = 0; i < N; ++i)
+    geod_polygon_addpoint(g, &p, points[i][0], points[i][1]);
+  geod_polygon_compute(g, &p, 0, 1, area, perimeter);
+}
+
+void polylength(const struct geod_geodesic* g, double points[][2], int N,
+                double* perimeter) {
+  struct geod_polygon p;
+  int i;
+  geod_polygon_init(&p, 1);
+  for (i = 0; i < N; ++i)
+    geod_polygon_addpoint(g, &p, points[i][0], points[i][1]);
+  geod_polygon_compute(g, &p, 0, 1, 0, perimeter);
+}
+
+int Planimeter0() {
+  /* Check fix for pole-encircling bug found 2011-03-16 */
+  double pa[4][2] = {{89, 0}, {89, 90}, {89, 180}, {89, 270}};
+  double pb[4][2] = {{-89, 0}, {-89, 90}, {-89, 180}, {-89, 270}};
+  double pc[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
+  double pd[3][2] = {{90, 0}, {0, 0}, {0, 90}};
+  struct geod_geodesic g;
+  double perimeter, area;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+
+  planimeter(&g, pa, 4, &perimeter, &area);
+  result += assertEquals(perimeter, 631819.8745, 1e-4);
+  result += assertEquals(area, 24952305678.0, 1);
+
+  planimeter(&g, pb, 4, &perimeter, &area);
+  result += assertEquals(perimeter, 631819.8745, 1e-4);
+  result += assertEquals(area, -24952305678.0, 1);
+
+  planimeter(&g, pc, 4, &perimeter, &area);
+  result += assertEquals(perimeter, 627598.2731, 1e-4);
+  result += assertEquals(area, 24619419146.0, 1);
+
+  planimeter(&g, pd, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 30022685, 1);
+  result += assertEquals(area, 63758202715511.0, 1);
+
+  polylength(&g, pd, 3, &perimeter);
+  result += assertEquals(perimeter, 20020719, 1);
+
+  return result;
+}
+
+int Planimeter5() {
+  /* Check fix for Planimeter pole crossing bug found 2011-06-24 */
+  double points[3][2] = {{89, 0.1}, {89, 90.1}, {89, -179.9}};
+  struct geod_geodesic g;
+  double perimeter, area;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  planimeter(&g, points, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 539297, 1);
+  result += assertEquals(area, 12476152838.5, 1);
+  return result;
+}
+
+int Planimeter6() {
+  /* Check fix for Planimeter lon12 rounding bug found 2012-12-03 */
+  double pa[3][2] = {{9, -0.00000000000001}, {9, 180}, {9, 0}};
+  double pb[3][2] = {{9, 0.00000000000001}, {9, 0}, {9, 180}};
+  double pc[3][2] = {{9, 0.00000000000001}, {9, 180}, {9, 0}};
+  double pd[3][2] = {{9, -0.00000000000001}, {9, 0}, {9, 180}};
+  struct geod_geodesic g;
+  double perimeter, area;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+
+  planimeter(&g, pa, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 36026861, 1);
+  result += assertEquals(area, 0, 1);
+  planimeter(&g, pb, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 36026861, 1);
+  result += assertEquals(area, 0, 1);
+  planimeter(&g, pc, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 36026861, 1);
+  result += assertEquals(area, 0, 1);
+  planimeter(&g, pd, 3, &perimeter, &area);
+  result += assertEquals(perimeter, 36026861, 1);
+  result += assertEquals(area, 0, 1);
+  return result;
+}
+
+int Planimeter12() {
+  /* Area of arctic circle (not really -- adjunct to rhumb-area test) */
+  double points[2][2] = {{66.562222222, 0}, {66.562222222, 180}};
+  struct geod_geodesic g;
+  double perimeter, area;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  planimeter(&g, points, 2, &perimeter, &area);
+  result += assertEquals(perimeter, 10465729, 1);
+  result += assertEquals(area, 0, 1);
+  return result;
+}
+
+int Planimeter13() {
+  /* Check encircling pole twice */
+  double points[6][2] = {{89,-360}, {89,-240}, {89,-120},
+                         {89,0}, {89,120}, {89,240}};
+  struct geod_geodesic g;
+  double perimeter, area;
+  int result = 0;
+  geod_init(&g, wgs84_a, wgs84_f);
+  planimeter(&g, points, 6, &perimeter, &area);
+  result += assertEquals(perimeter, 1160741, 1);
+  result += assertEquals(area, 32415230256.0, 1);
+  return result;
+}
+
+int main() {
+  int n = 0, i;
+  if ((i = testinverse())) {++n; printf("testinverse fail: %d\n", i);}
+  if ((i = testdirect())) {++n; printf("testdirect fail: %d\n", i);}
+  if ((i = testarcdirect())) {++n; printf("testarcdirect fail: %d\n", i);}
+  if ((i = GeodSolve0())) {++n; printf("GeodSolve0 fail: %d\n", i);}
+  if ((i = GeodSolve1())) {++n; printf("GeodSolve1 fail: %d\n", i);}
+  if ((i = GeodSolve2())) {++n; printf("GeodSolve2 fail: %d\n", i);}
+  if ((i = GeodSolve4())) {++n; printf("GeodSolve4 fail: %d\n", i);}
+  if ((i = GeodSolve5())) {++n; printf("GeodSolve5 fail: %d\n", i);}
+  if ((i = GeodSolve6())) {++n; printf("GeodSolve6 fail: %d\n", i);}
+  if ((i = GeodSolve9())) {++n; printf("GeodSolve9 fail: %d\n", i);}
+  if ((i = GeodSolve10())) {++n; printf("GeodSolve10 fail: %d\n", i);}
+  if ((i = GeodSolve11())) {++n; printf("GeodSolve11 fail: %d\n", i);}
+  if ((i = GeodSolve12())) {++n; printf("GeodSolve12 fail: %d\n", i);}
+  if ((i = GeodSolve14())) {++n; printf("GeodSolve14 fail: %d\n", i);}
+  if ((i = GeodSolve15())) {++n; printf("GeodSolve15 fail: %d\n", i);}
+  if ((i = GeodSolve17())) {++n; printf("GeodSolve17 fail: %d\n", i);}
+  if ((i = GeodSolve26())) {++n; printf("GeodSolve26 fail: %d\n", i);}
+  if ((i = GeodSolve28())) {++n; printf("GeodSolve28 fail: %d\n", i);}
+  if ((i = GeodSolve33())) {++n; printf("GeodSolve33 fail: %d\n", i);}
+  if ((i = GeodSolve55())) {++n; printf("GeodSolve55 fail: %d\n", i);}
+  if ((i = Planimeter0())) {++n; printf("Planimeter0 fail: %d\n", i);}
+  if ((i = Planimeter5())) {++n; printf("Planimeter5 fail: %d\n", i);}
+  if ((i = Planimeter6())) {++n; printf("Planimeter6 fail: %d\n", i);}
+  if ((i = Planimeter12())) {++n; printf("Planimeter12 fail: %d\n", i);}
+  if ((i = Planimeter13())) {++n; printf("Planimeter13 fail: %d\n", i);}
+  return n;
+}
+
+/** @endcond */
diff --git a/legacy/Fortran/CMakeLists.txt b/legacy/Fortran/CMakeLists.txt
index 80ce998..83d5164 100644
--- a/legacy/Fortran/CMakeLists.txt
+++ b/legacy/Fortran/CMakeLists.txt
@@ -8,7 +8,7 @@ if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
   set (CMAKE_BUILD_TYPE Release)
 endif ()
 
-set (TOOLS geoddirect geodinverse planimeter)
+set (TOOLS geoddirect geodinverse planimeter geodtest)
 
 foreach (TOOL ${TOOLS})
   add_executable (${TOOL} ${TOOL}.for geodesic.for geodesic.inc)
@@ -21,3 +21,9 @@ set_target_properties (${TOOLS} PROPERTIES COMPILE_FLAGS
 # compile flags above.
 add_executable (ngsforward ngsforward.for ngscommon.for geodesic.for)
 add_executable (ngsinverse ngsinverse.for ngscommon.for geodesic.for)
+
+# Turn on testing
+enable_testing ()
+
+# Run the test suite
+add_test (NAME geodtest COMMAND geodtest)
diff --git a/legacy/Fortran/geoddirect.for b/legacy/Fortran/geoddirect.for
index c115891..5564aaa 100644
--- a/legacy/Fortran/geoddirect.for
+++ b/legacy/Fortran/geoddirect.for
@@ -24,11 +24,6 @@
 
  10   continue
       read(*, *, end=90, err=90) lat1, lon1, azi1, s12
-      if (abs(lat1) .gt. 90) then
-        print 15
- 15     format(1x, 'lat1 must be in [-90,90]')
-        go to 10
-      end if
       call direct(a, f, lat1, lon1, azi1, s12, flags,
      +    lat2, lon2, azi2, omask,
      +    dummy1, dummy2, dummy3, dummy4, dummy5)
diff --git a/legacy/Fortran/geodesic.for b/legacy/Fortran/geodesic.for
index 242a301..27f3791 100644
--- a/legacy/Fortran/geodesic.for
+++ b/legacy/Fortran/geodesic.for
@@ -78,26 +78,27 @@
 *! is (obviously) uniquely defined.  However, in a few special cases
 *! there are multiple azimuths which yield the same shortest distance.
 *! Here is a catalog of those cases:
-*! - \e lat1 = −\e lat2 (with neither point at a pole).  If \e azi1 = \e
-*!   azi2, the geodesic is unique.  Otherwise there are two geodesics
-*!   and the second one is obtained by setting [\e azi1, \e azi2] = [\e
-*!   azi2, \e azi1], [\e MM12, \e MM21] = [\e MM21, \e MM12], \e SS12 =
-*!   −\e SS12.  (This occurs when the longitude difference is near
-*!   ±180° for oblate ellipsoids.)
-*! - \e lon2 = \e lon1 ± 180° (with neither point at a pole).  If
-*!   \e azi1 = 0° or ±180°, the geodesic is unique.
+*! - \e lat1 = −\e lat2 (with neither point at a pole).  If \e
+*!   azi1 = \e azi2, the geodesic is unique.  Otherwise there are two
+*!   geodesics and the second one is obtained by setting [\e azi1, \e
+*!   azi2] → [\e azi2, \e azi1], [\e MM12, \e MM21] → [\e
+*!   MM21, \e MM12], \e SS12 → −\e SS12.  (This occurs when
+*!   the longitude difference is near ±180° for oblate
+*!   ellipsoids.)
+*! - \e lon2 = \e lon1 ± 180° (with neither point at a pole).
+*!   If \e azi1 = 0° or ±180°, the geodesic is unique.
 *!   Otherwise there are two geodesics and the second one is obtained by
-*!   setting [\e azi1, \e azi2] = [−\e azi1, −\e azi2], \e
-*!   SS12 = −\e SS12.  (This occurs when \e lat2 is near
+*!   setting [\e azi1, \e azi2] → [−\e azi1, −\e azi2],
+*!   \e SS12 → −\e SS12.  (This occurs when \e lat2 is near
 *!   −\e lat1 for prolate ellipsoids.)
 *! - Points 1 and 2 at opposite poles.  There are infinitely many
-*!   geodesics which can be generated by setting [\e azi1, \e azi2] =
-*!   [\e azi1, \e azi2] + [\e d, −\e d], for arbitrary \e d.  (For
-*!   spheres, this prescription applies when points 1 and 2 are
+*!   geodesics which can be generated by setting [\e azi1, \e azi2]
+*!   → [\e azi1, \e azi2] + [\e d, −\e d], for arbitrary \e
+*!   d.  (For spheres, this prescription applies when points 1 and 2 are
 *!   antipodal.)
 *! - \e s12 = 0 (coincident points).  There are infinitely many
-*!   geodesics which can be generated by setting [\e azi1, \e azi2] =
-*!   [\e azi1, \e azi2] + [\e d, \e d], for arbitrary \e d.
+*!   geodesics which can be generated by setting [\e azi1, \e azi2]
+*!   → [\e azi1, \e azi2] + [\e d, \e d], for arbitrary \e d.
 *!
 *! These routines are a simple transcription of the corresponding C++
 *! classes in <a href="http://geographiclib.sf.net"> GeographicLib</a>.
@@ -117,7 +118,7 @@
 *! http://geographiclib.sourceforge.net/
 *!
 *! This library was distributed with
-*! <a href="../index.html">GeographicLib</a> 1.44.
+*! <a href="../index.html">GeographicLib</a> 1.45.
 
 *> Solve the direct geodesic problem
 *!
@@ -198,8 +199,8 @@
       double precision A3x(0:nA3x-1), C3x(0:nC3x-1), C4x(0:nC4x-1),
      +    C1a(nC1), C1pa(nC1p), C2a(nC2), C3a(nC3-1), C4a(0:nC4-1)
 
-      double precision csmgt, atanhx, hypotx,
-     +    AngNm, AngRnd, TrgSum, A1m1f, A2m1f, A3f, atn2dx
+      double precision atanhx, hypotx,
+     +    AngNm, AngRnd, TrgSum, A1m1f, A2m1f, A3f, atn2dx, LatFix
       logical arcmod, unroll, arcp, redlp, scalp, areap
       double precision e2, f1, ep2, n, b, c2,
      +    salp0, calp0, k2, eps,
@@ -226,13 +227,13 @@
       b = a * f1
       c2 = 0
 
-      arcmod = mod(flags/1, 2) == 1
-      unroll = mod(flags/2, 2) == 1
+      arcmod = mod(flags/1, 2) .eq. 1
+      unroll = mod(flags/2, 2) .eq. 1
 
-      arcp = mod(omask/1, 2) == 1
-      redlp = mod(omask/2, 2) == 1
-      scalp = mod(omask/4, 2) == 1
-      areap = mod(omask/8, 2) == 1
+      arcp = mod(omask/1, 2) .eq. 1
+      redlp = mod(omask/2, 2) .eq. 1
+      scalp = mod(omask/4, 2) .eq. 1
+      areap = mod(omask/8, 2) .eq. 1
 
       if (areap) then
         if (e2 .eq. 0) then
@@ -251,7 +252,7 @@
 * Guard against underflow in salp0
       call sncsdx(AngRnd(azi1), salp1, calp1)
 
-      call sncsdx(AngRnd(lat1), sbet1, cbet1)
+      call sncsdx(AngRnd(LatFix(lat1)), sbet1, cbet1)
       sbet1 = f1 * sbet1
       call norm2(sbet1, cbet1)
 * Ensure cbet1 = +dbleps at poles
@@ -275,7 +276,11 @@
 * With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
       ssig1 = sbet1
       somg1 = salp0 * sbet1
-      csig1 = csmgt(cbet1 * calp1, 1d0, sbet1 .ne. 0 .or. calp1 .ne. 0)
+      if (sbet1 .ne. 0 .or. calp1 .ne. 0) then
+        csig1 = cbet1 * calp1
+      else
+        csig1 = 1
+      end if
       comg1 = csig1
 * sig1 in (-pi, pi]
       call norm2(ssig1, csig1)
@@ -459,17 +464,24 @@
 * else
 *   csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
 * No need to normalize
-          salp12 = calp0 * salp0 *
-     +        csmgt(csig1 * (1 - csig12) + ssig12 * ssig1,
-     +        ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1),
-     +        csig12 .le. 0)
+          if (csig12 .le. 0) then
+            salp12 = csig1 * (1 - csig12) + ssig12 * ssig1
+          else
+            salp12 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
+          end if
+          salp12 = calp0 * salp0 * salp12
           calp12 = salp0**2 + calp0**2 * csig1 * csig2
         end if
         SS12 = c2 * atan2(salp12, calp12) + A4 * (B42 - B41)
       end if
 
-      if (arcp) a12s12 = csmgt(b * ((1 + A1m1) * sig12 + AB1),
-     +    sig12 / degree, arcmod)
+      if (arcp) then
+        if (arcmod) then
+          a12s12 = b * ((1 + A1m1) * sig12 + AB1)
+        else
+          a12s12 = sig12 / degree
+        end if
+      end if
 
       return
       end
@@ -537,8 +549,8 @@
       double precision A3x(0:nA3x-1), C3x(0:nC3x-1), C4x(0:nC4x-1),
      +    Ca(nC)
 
-      double precision csmgt, atanhx, hypotx,
-     +    AngDif, AngRnd, TrgSum, Lam12f, InvSta, atn2dx
+      double precision atanhx, hypotx,
+     +    AngDif, AngRnd, TrgSum, Lam12f, InvSta, atn2dx, LatFix
       integer latsgn, lonsgn, swapp, numit
       logical arcp, redlp, scalp, areap, merid, tripn, tripb
 
@@ -568,10 +580,10 @@
       b = a * f1
       c2 = 0
 
-      arcp = mod(omask/1, 2) == 1
-      redlp = mod(omask/2, 2) == 1
-      scalp = mod(omask/4, 2) == 1
-      areap = mod(omask/8, 2) == 1
+      arcp = mod(omask/1, 2) .eq. 1
+      redlp = mod(omask/2, 2) .eq. 1
+      scalp = mod(omask/4, 2) .eq. 1
+      areap = mod(omask/8, 2) .eq. 1
       if (scalp) then
         lmask = 16 + 2 + 4
       else
@@ -605,13 +617,14 @@
       end if
       lon12 = lon12 * lonsgn
 * If really close to the equator, treat as on equator.
-      lat1x = AngRnd(lat1)
-      lat2x = AngRnd(lat2)
+      lat1x = AngRnd(LatFix(lat1))
+      lat2x = AngRnd(LatFix(lat2))
 * Swap points so that point with higher (abs) latitude is point 1
-      if (abs(lat1x) .ge. abs(lat2x)) then
-        swapp = 1
-      else
+* If one latitude is a nan, then it becomes lat1.
+      if (abs(lat1x) .lt. abs(lat2x)) then
         swapp = -1
+      else
+        swapp = 1
       end if
       if (swapp .lt. 0) then
         lonsgn = -lonsgn
@@ -672,7 +685,7 @@
 
 * Suppress bogus warnings about unitialized variables
       a12x = 0
-      merid = lat1x .eq. -90 .or. slam12 == 0
+      merid = lat1x .eq. -90 .or. slam12 .eq. 0
 
       if (merid) then
 
@@ -693,7 +706,7 @@
         csig2 = calp2 * cbet2
 
 * sig12 = sig2 - sig1
-        sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, 0d0),
+        sig12 = atan2(0d0 + max(0d0, csig1 * ssig2 - ssig1 * csig2),
      +      csig1 * csig2 + ssig1 * ssig2)
         call Lengs(n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
      +      cbet1, cbet2, lmask,
@@ -788,8 +801,12 @@
      +          Ca) - lam12
 * 2 * tol0 is approximately 1 ulp for a number in [0, pi].
 * Reversed test to allow escape with NaNs
-            if (tripb .or.
-     +          .not. (abs(v) .ge. csmgt(8d0, 2d0, tripn) * tol0))
+            if (tripn) then
+              dummy = 8
+            else
+              dummy = 2
+            end if
+            if (tripb .or. .not. (abs(v) .ge. dummy * tol0))
      +          go to 20
 * Update bracketing values
             if (v .gt. 0 .and. (numit .gt. maxit1 .or.
@@ -1013,7 +1030,7 @@
       integer major, minor, patch
 
       major = 1
-      minor = 44
+      minor = 45
       patch = 0
 
       return
@@ -1045,7 +1062,11 @@
 
       pi = atan2(0d0, -1d0)
       degree = pi/180
-      tiny = sqrt(dblmin)
+* This is about cbrt(dblmin).  With other implementations, sqrt(dblmin)
+* is used.  The larger value is used here to avoid complaints about a
+* IEEE_UNDERFLOW_FLAG IEEE_DENORMAL signal.  This is triggered when
+* invers is called with points at opposite poles.
+      tiny = 0.5d0**((1022+2)/3)
       tol0 = dbleps
 * Increase multiplier in defn of tol1 from 100 to 200 to fix inverse
 * case 52.784459512564 0 -52.784459512563990912 179.634407464943777557
@@ -1086,9 +1107,9 @@
 * Return m12b = (reduced length)/b; also calculate s12b = distance/b,
 * and m0 = coefficient of secular term in expression for reduced length.
 
-      distp = mod(omask/16, 2) == 1
-      redlp = mod(omask/2, 2) == 1
-      scalp = mod(omask/4, 2) == 1
+      distp = (mod(omask/16, 2) .eq. 1)
+      redlp = (mod(omask/2, 2) .eq. 1)
+      scalp = (mod(omask/4, 2) .eq. 1)
 
 * Suppress compiler warnings
       m0x = 0
@@ -1148,7 +1169,7 @@
 * input
       double precision x, y
 
-      double precision cbrt, csmgt
+      double precision cbrt
       double precision k, p, q, r, S, r2, r3, disc, u,
      +    T3, T, ang, v, uv, w
 
@@ -1172,7 +1193,12 @@
 * of precision due to cancellation.  The result is unchanged because
 * of the way the T is used in definition of u.
 * T3 = (r * t)^3
-          T3 = T3 + csmgt(-sqrt(disc), sqrt(disc), T3 .lt. 0)
+          if (T3 .lt. 0) then
+            disc = -sqrt(disc)
+          else
+            disc = sqrt(disc)
+          end if
+          T3 = T3 + disc
 * N.B. cbrt always returns the real root.  cbrt(-8) = -2.
 * T = r * t
           T = cbrt(T3)
@@ -1189,7 +1215,11 @@
         v = sqrt(u**2 + q)
 * Avoid loss of accuracy when u < 0.
 * u+v, guaranteed positive
-        uv = csmgt(q / (v - u), u + v, u .lt. 0)
+        if (u .lt. 0) then
+          uv = q / (v - u)
+        else
+          uv = u + v
+        end if
 * positive?
         w = (uv - q) / (2 * v)
 * Rearrange expression for k to avoid loss of accuracy due to
@@ -1222,7 +1252,7 @@
 * temporary
       double precision Ca(*)
 
-      double precision csmgt, hypotx, A3f, Astrd
+      double precision hypotx, A3f, Astrd
       logical shortp
       double precision f1, e2, ep2, n, etol2, k2, eps, sig12,
      +    sbet12, cbet12, sbt12a, omg12, somg12, comg12, ssig12, csig12,
@@ -1276,9 +1306,11 @@
       comg12 = cos(omg12)
 
       salp1 = cbet2 * somg12
-      calp1 = csmgt(sbet12 + cbet2 * sbet1 * somg12**2 / (1 + comg12),
-     +    sbt12a - cbet2 * sbet1 * somg12**2 / (1 - comg12),
-     +    comg12 .ge. 0)
+      if (comg12 .ge. 0) then
+        calp1 = sbet12 + cbet2 * sbet1 * somg12**2 / (1 + comg12)
+      else
+        calp1 = sbt12a - cbet2 * sbet1 * somg12**2 / (1 - comg12)
+      end if
 
       ssig12 = hypotx(salp1, calp1)
       csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12
@@ -1286,8 +1318,12 @@
       if (shortp .and. ssig12 .lt. etol2) then
 * really short lines
         salp2 = cbet1 * somg12
-        calp2 = sbet12 - cbet1 * sbet2 *
-     +      csmgt(somg12**2 / (1 + comg12), 1 - comg12, comg12 .ge. 0)
+        if (comg12 .ge. 0) then
+          calp2 = somg12**2 / (1 + comg12)
+        else
+          calp2 = 1 - comg12
+        end if
+        calp2 = sbet12 - cbet1 * sbet2 * calp2
         call norm2(salp2, calp2)
 * Set return value
         sig12 = atan2(ssig12, csig12)
@@ -1316,8 +1352,11 @@
      +        sbet1, -cbet1, dn1, sbet2, cbet2, dn2, cbet1, cbet2, 2,
      +        dummy, m12b, m0, dummy, dummy, ep2, Ca)
           x = -1 + m12b / (cbet1 * cbet2 * m0 * pi)
-          betscl = csmgt(sbt12a / x, -f * cbet1**2 * pi,
-     +        x .lt. -0.01d0)
+          if (x .lt. -0.01d0) then
+            betscl = sbt12a / x
+          else
+            betscl = -f * cbet1**2 * pi
+          end if
           lamscl = betscl / cbet1
           y = (lam12 - pi) / lamscl
         end if
@@ -1328,7 +1367,12 @@
             salp1 = min(1d0, -x)
             calp1 = - sqrt(1 - salp1**2)
           else
-            calp1 = max(csmgt(0d0, 1d0, x .gt. -tol1), x)
+            if (x .gt. -tol1) then
+              calp1 = 0
+            else
+              calp1 = 1
+            end if
+            calp1 = max(calp1, x)
             salp1 = sqrt(1 - calp1**2)
           end if
         else
@@ -1367,8 +1411,12 @@
 *
 * Because omg12 is near pi, estimate work with omg12a = pi - omg12
           k = Astrd(x, y)
-          omg12a = lamscl *
-     +        csmgt(-x * k/(1 + k), -y * (1 + k)/k, f .ge. 0)
+          if (f .ge. 0) then
+            omg12a = -x * k/(1 + k)
+          else
+            omg12a = -y * (1 + k)/k
+          end if
+          omg12a = lamscl * omg12a
           somg12 = sin(omg12a)
           comg12 = -cos(omg12a)
 * Update spherical estimate of alp1 using omg12 instead of lam12
@@ -1407,7 +1455,7 @@
       integer ord, nC3
       parameter (ord = 6, nC3 = ord)
 
-      double precision csmgt, hypotx, A3f, TrgSum
+      double precision hypotx, A3f, TrgSum
 
       double precision f1, e2, ep2, salp0, calp0,
      +    somg1, comg1, somg2, comg2, omg12, lam12, B312, h0, k2, dummy
@@ -1444,16 +1492,25 @@
 * about this case, since this can yield singularities in the Newton
 * iteration.
 * sin(alp2) * cos(bet2) = sin(alp0)
-      salp2 = csmgt(salp0 / cbet2, salp1, cbet2 .ne. cbet1)
+      if (cbet2 .ne. cbet1) then
+        salp2 = salp0 / cbet2
+      else
+        salp2 = salp1
+      end if
 * calp2 = sqrt(1 - sq(salp2))
 *       = sqrt(sq(calp0) - sq(sbet2)) / cbet2
 * and subst for calp0 and rearrange to give (choose positive sqrt
 * to give alp2 in [0, pi/2]).
-      calp2 = csmgt(sqrt((calp1 * cbet1)**2 +
-     +    csmgt((cbet2 - cbet1) * (cbet1 + cbet2),
-     +    (sbet1 - sbet2) * (sbet1 + sbet2),
-     +    cbet1 .lt. -sbet1)) / cbet2,
-     +    abs(calp1), cbet2 .ne. cbet1 .or. abs(sbet2) .ne. -sbet1)
+      if (cbet2 .ne. cbet1 .or. abs(sbet2) .ne. -sbet1) then
+        if (cbet1 .lt. -sbet1) then
+          calp2 = (cbet2 - cbet1) * (cbet1 + cbet2)
+        else
+          calp2 = (sbet1 - sbet2) * (sbet1 + sbet2)
+        end if
+        calp2 = sqrt((calp1 * cbet1)**2 + calp2) / cbet2
+      else
+        calp2 = abs(calp1)
+      end if
 * tan(bet2) = tan(sig2) * cos(alp2)
 * tan(omg2) = sin(alp0) * tan(sig2).
       ssig2 = sbet2
@@ -1464,11 +1521,11 @@
 * norm2(somg2, comg2); -- don't need to normalize!
 
 * sig12 = sig2 - sig1, limit to [0, pi]
-      sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, 0d0),
+      sig12 = atan2(0d0 + max(0d0, csig1 * ssig2 - ssig1 * csig2),
      +    csig1 * csig2 + ssig1 * ssig2)
 
 * omg12 = omg2 - omg1, limit to [0, pi]
-      omg12 = atan2(max(comg1 * somg2 - somg1 * comg2, 0d0),
+      omg12 = atan2(0d0 + max(0d0, comg1 * somg2 - somg1 * comg2),
      +    comg1 * comg2 + somg1 * somg2)
       k2 = calp0**2 * ep2
       eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2)
@@ -1849,6 +1906,18 @@
       return
       end
 
+      double precision function LatFix(x)
+* input
+      double precision x
+
+      LatFix = x
+      if (.not. (abs(x) .gt. 90)) return
+* concoct a NaN
+      LatFix = sqrt(90 - abs(x))
+
+      return
+      end
+
       double precision function AngDif(x, y)
 * Compute y - x.  x and y must both lie in [-180, 180].  The result is
 * equivalent to computing the difference exactly, reducing it to (-180,
@@ -1924,10 +1993,14 @@
 * input
       double precision x
 
-      double precision csmgt, y, z
+      double precision y, z
       y = 1 + x
       z = y - 1
-      log1px = csmgt(x, x * log(y) / z, z .eq. 0)
+      if (z .eq. 0) then
+        log1px = x
+      else
+        log1px = x * log(y) / z
+      end if
 
       return
       end
@@ -1953,20 +2026,6 @@
       return
       end
 
-      double precision function csmgt(x, y, p)
-* input
-      double precision x, y
-      logical p
-
-      if (p) then
-        csmgt = x
-      else
-        csmgt = y
-      end if
-
-      return
-      end
-
       double precision function TrgSum(sinp, sinx, cosx, c, n)
 * Evaluate
 * y = sinp ? sum(c[i] * sin( 2*i    * x), i, 1, n) :
@@ -2149,7 +2208,7 @@
         polval = 0
       else
         polval = p(0)
-      endif
+      end if
       do 10 i = 1, N
         polval = polval * x + p(i)
  10   continue
diff --git a/legacy/Fortran/geodesic.inc b/legacy/Fortran/geodesic.inc
index 585066b..22c1950 100644
--- a/legacy/Fortran/geodesic.inc
+++ b/legacy/Fortran/geodesic.inc
@@ -9,6 +9,9 @@
 
       interface
 
+* omask bits: 1 = a12; 2 = m12; 4 = MM12 + MM21; 8 = SS12
+* flags bits: 1 = arcmode; 2 = unroll
+
         subroutine direct(a, f, lat1, lon1, azi1, s12a12, flags,
      +      lat2, lon2, azi2, omask, a12s12, m12, MM12, MM21, SS12)
         double precision, intent(in) :: a, f, lat1, lon1, azi1, s12a12
diff --git a/legacy/Fortran/geodinverse.for b/legacy/Fortran/geodinverse.for
index 60a347e..6d4365b 100644
--- a/legacy/Fortran/geodinverse.for
+++ b/legacy/Fortran/geodinverse.for
@@ -23,11 +23,6 @@
 
  10   continue
       read(*, *, end=90, err=90) lat1, lon1, lat2, lon2
-      if (abs(lat1) .gt. 90 .or. abs(lat2) .gt. 90) then
-        print 15
- 15     format(1x,'lat1 and lat2 must be in [-90,90]')
-        go to 10
-      end if
       call invers(a, f, lat1, lon1, lat2, lon2,
      +    s12, azi1, azi2, omask,
      +    dummy1, dummy2, dummy3, dummy4 , dummy5)
diff --git a/legacy/Fortran/geodtest.for b/legacy/Fortran/geodtest.for
new file mode 100644
index 0000000..321258f
--- /dev/null
+++ b/legacy/Fortran/geodtest.for
@@ -0,0 +1,1046 @@
+*> @file geodtest.for
+*! @brief Test suite for the geodesic routines in Fortran
+*!
+*! Run these tests by configuring with cmake and running "make test".
+*!
+*! Copyright (c) Charles Karney (2015) <charles at karney.com> and licensed under
+*! the MIT/X11 License.  For more information, see
+*! http://geographiclib.sourceforge.net/
+
+*> @cond SKIP
+
+      block data tests
+
+      integer j
+      double precision tstdat(20, 12)
+      common /tstcom/ tstdat
+      data (tstdat(1,j), j = 1,12) /
+     +    35.60777d0,-139.44815d0,111.098748429560326d0,
+     +    -11.17491d0,-69.95921d0,129.289270889708762d0,
+     +    8935244.5604818305d0,80.50729714281974d0,6273170.2055303837d0,
+     +    0.16606318447386067d0,0.16479116945612937d0,
+     +    12841384694976.432d0 /
+      data (tstdat(2,j), j = 1,12) /
+     +    55.52454d0,106.05087d0,22.020059880982801d0,
+     +    77.03196d0,197.18234d0,109.112041110671519d0,
+     +    4105086.1713924406d0,36.892740690445894d0,
+     +    3828869.3344387607d0,
+     +    0.80076349608092607d0,0.80101006984201008d0,
+     +    61674961290615.615d0 /
+      data (tstdat(3,j), j = 1,12) /
+     +    -21.97856d0,142.59065d0,-32.44456876433189d0,
+     +    41.84138d0,98.56635d0,-41.84359951440466d0,
+     +    8394328.894657671d0,75.62930491011522d0,6161154.5773110616d0,
+     +    0.24816339233950381d0,0.24930251203627892d0,
+     +    -6637997720646.717d0 /
+      data (tstdat(4,j), j = 1,12) /
+     +    -66.99028d0,112.2363d0,173.73491240878403d0,
+     +    -12.70631d0,285.90344d0,2.512956620913668d0,
+     +    11150344.2312080241d0,100.278634181155759d0,
+     +    6289939.5670446687d0,
+     +    -0.17199490274700385d0,-0.17722569526345708d0,
+     +    -121287239862139.744d0 /
+      data (tstdat(5,j), j = 1,12) /
+     +    -17.42761d0,173.34268d0,-159.033557661192928d0,
+     +    -15.84784d0,5.93557d0,-20.787484651536988d0,
+     +    16076603.1631180673d0,144.640108810286253d0,
+     +    3732902.1583877189d0,
+     +    -0.81273638700070476d0,-0.81299800519154474d0,
+     +    97825992354058.708d0 /
+      data (tstdat(6,j), j = 1,12) /
+     +    32.84994d0,48.28919d0,150.492927788121982d0,
+     +    -56.28556d0,202.29132d0,48.113449399816759d0,
+     +    16727068.9438164461d0,150.565799985466607d0,
+     +    3147838.1910180939d0,
+     +    -0.87334918086923126d0,-0.86505036767110637d0,
+     +    -72445258525585.010d0 /
+      data (tstdat(7,j), j = 1,12) /
+     +    6.96833d0,52.74123d0,92.581585386317712d0,
+     +    -7.39675d0,206.17291d0,90.721692165923907d0,
+     +    17102477.2496958388d0,154.147366239113561d0,
+     +    2772035.6169917581d0,
+     +    -0.89991282520302447d0,-0.89986892177110739d0,
+     +    -1311796973197.995d0 /
+      data (tstdat(8,j), j = 1,12) /
+     +    -50.56724d0,-16.30485d0,-105.439679907590164d0,
+     +    -33.56571d0,-94.97412d0,-47.348547835650331d0,
+     +    6455670.5118668696d0,58.083719495371259d0,
+     +    5409150.7979815838d0,
+     +    0.53053508035997263d0,0.52988722644436602d0,
+     +    41071447902810.047d0 /
+      data (tstdat(9,j), j = 1,12) /
+     +    -58.93002d0,-8.90775d0,140.965397902500679d0,
+     +    -8.91104d0,133.13503d0,19.255429433416599d0,
+     +    11756066.0219864627d0,105.755691241406877d0,
+     +    6151101.2270708536d0,
+     +    -0.26548622269867183d0,-0.27068483874510741d0,
+     +    -86143460552774.735d0 /
+      data (tstdat(10,j), j = 1,12) /
+     +    -68.82867d0,-74.28391d0,93.774347763114881d0,
+     +    -50.63005d0,-8.36685d0,34.65564085411343d0,
+     +    3956936.926063544d0,35.572254987389284d0,3708890.9544062657d0,
+     +    0.81443963736383502d0,0.81420859815358342d0,
+     +    -41845309450093.787d0 /
+      data (tstdat(11,j), j = 1,12) /
+     +    -10.62672d0,-32.0898d0,-86.426713286747751d0,
+     +    5.883d0,-134.31681d0,-80.473780971034875d0,
+     +    11470869.3864563009d0,103.387395634504061d0,
+     +    6184411.6622659713d0,
+     +    -0.23138683500430237d0,-0.23155097622286792d0,
+     +    4198803992123.548d0 /
+      data (tstdat(12,j), j = 1,12) /
+     +    -21.76221d0,166.90563d0,29.319421206936428d0,
+     +    48.72884d0,213.97627d0,43.508671946410168d0,
+     +    9098627.3986554915d0,81.963476716121964d0,
+     +    6299240.9166992283d0,
+     +    0.13965943368590333d0,0.14152969707656796d0,
+     +    10024709850277.476d0 /
+      data (tstdat(13,j), j = 1,12) /
+     +    -19.79938d0,-174.47484d0,71.167275780171533d0,
+     +    -11.99349d0,-154.35109d0,65.589099775199228d0,
+     +    2319004.8601169389d0,20.896611684802389d0,
+     +    2267960.8703918325d0,
+     +    0.93427001867125849d0,0.93424887135032789d0,
+     +    -3935477535005.785d0 /
+      data (tstdat(14,j), j = 1,12) /
+     +    -11.95887d0,-116.94513d0,92.712619830452549d0,
+     +    4.57352d0,7.16501d0,78.64960934409585d0,
+     +    13834722.5801401374d0,124.688684161089762d0,
+     +    5228093.177931598d0,
+     +    -0.56879356755666463d0,-0.56918731952397221d0,
+     +    -9919582785894.853d0 /
+      data (tstdat(15,j), j = 1,12) /
+     +    -87.85331d0,85.66836d0,-65.120313040242748d0,
+     +    66.48646d0,16.09921d0,-4.888658719272296d0,
+     +    17286615.3147144645d0,155.58592449699137d0,
+     +    2635887.4729110181d0,
+     +    -0.90697975771398578d0,-0.91095608883042767d0,
+     +    42667211366919.534d0 /
+      data (tstdat(16,j), j = 1,12) /
+     +    1.74708d0,128.32011d0,-101.584843631173858d0,
+     +    -11.16617d0,11.87109d0,-86.325793296437476d0,
+     +    12942901.1241347408d0,116.650512484301857d0,
+     +    5682744.8413270572d0,
+     +    -0.44857868222697644d0,-0.44824490340007729d0,
+     +    10763055294345.653d0 /
+      data (tstdat(17,j), j = 1,12) /
+     +    -25.72959d0,-144.90758d0,-153.647468693117198d0,
+     +    -57.70581d0,-269.17879d0,-48.343983158876487d0,
+     +    9413446.7452453107d0,84.664533838404295d0,
+     +    6356176.6898881281d0,
+     +    0.09492245755254703d0,0.09737058264766572d0,
+     +    74515122850712.444d0 /
+      data (tstdat(18,j), j = 1,12) /
+     +    -41.22777d0,122.32875d0,14.285113402275739d0,
+     +    -7.57291d0,130.37946d0,10.805303085187369d0,
+     +    3812686.035106021d0,34.34330804743883d0,3588703.8812128856d0,
+     +    0.82605222593217889d0,0.82572158200920196d0,
+     +    -2456961531057.857d0 /
+      data (tstdat(19,j), j = 1,12) /
+     +    11.01307d0,138.25278d0,79.43682622782374d0,
+     +    6.62726d0,247.05981d0,103.708090215522657d0,
+     +    11911190.819018408d0,107.341669954114577d0,
+     +    6070904.722786735d0,
+     +    -0.29767608923657404d0,-0.29785143390252321d0,
+     +    17121631423099.696d0 /
+      data (tstdat(20,j), j = 1,12) /
+     +    -29.47124d0,95.14681d0,-163.779130441688382d0,
+     +    -27.46601d0,-69.15955d0,-15.909335945554969d0,
+     +    13487015.8381145492d0,121.294026715742277d0,
+     +    5481428.9945736388d0,
+     +    -0.51527225545373252d0,-0.51556587964721788d0,
+     +    104679964020340.318d0 /
+      end
+
+      integer function assert(x, y, d)
+      double precision x, y, d
+
+      if (abs(x - y) .le. d) then
+        assert = 0
+      else
+        assert = 1
+        print 10, x, y, d
+ 10     format(1x, 'assert fails: ',
+     +      g14.7, ' != ', g14.7, ' +/- ', g10.3)
+      end if
+
+      return
+      end
+
+      integer function tstinv()
+      double precision tstdat(20, 12)
+      common /tstcom/ tstdat
+      double precision lat1, lon1, azi1, lat2, lon2, azi2,
+     +    s12, a12, m12, MM12, MM21, SS12
+      double precision azi1a, azi2a, s12a, a12a,
+     +    m12a, MM12a, MM21a, SS12a
+      double precision a, f
+      integer r, assert, i, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 1+2+4+8
+      r = 0
+
+      do i = 1,20
+        lat1 = tstdat(i, 1)
+        lon1 = tstdat(i, 2)
+        azi1 = tstdat(i, 3)
+        lat2 = tstdat(i, 4)
+        lon2 = tstdat(i, 5)
+        azi2 = tstdat(i, 6)
+        s12 = tstdat(i, 7)
+        a12 = tstdat(i, 8)
+        m12 = tstdat(i, 9)
+        MM12 = tstdat(i, 10)
+        MM21 = tstdat(i, 11)
+        SS12 = tstdat(i, 12)
+        call invers(a, f, lat1, lon1, lat2, lon2,
+     +      s12a, azi1a, azi2a, omask, a12a, m12a, MM12a, MM21a, SS12a)
+        r = r + assert(azi1, azi1a, 1d-13)
+        r = r + assert(azi2, azi2a, 1d-13)
+        r = r + assert(s12, s12a, 1d-8)
+        r = r + assert(a12, a12a, 1d-13)
+        r = r + assert(m12, m12a, 1d-8)
+        r = r + assert(MM12, MM12a, 1d-15)
+        r = r + assert(MM21, MM21a, 1d-15)
+        r = r + assert(SS12, SS12a, 0.1d0)
+      end do
+
+      tstinv = r
+      return
+      end
+
+      integer function tstdir()
+      double precision tstdat(20, 12)
+      common /tstcom/ tstdat
+      double precision lat1, lon1, azi1, lat2, lon2, azi2,
+     +    s12, a12, m12, MM12, MM21, SS12
+      double precision lat2a, lon2a, azi2a, a12a,
+     +    m12a, MM12a, MM21a, SS12a
+      double precision a, f
+      integer r, assert, i, omask, flags
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 1+2+4+8
+      flags = 2
+      r = 0
+
+      do i = 1,20
+        lat1 = tstdat(i, 1)
+        lon1 = tstdat(i, 2)
+        azi1 = tstdat(i, 3)
+        lat2 = tstdat(i, 4)
+        lon2 = tstdat(i, 5)
+        azi2 = tstdat(i, 6)
+        s12 = tstdat(i, 7)
+        a12 = tstdat(i, 8)
+        m12 = tstdat(i, 9)
+        MM12 = tstdat(i, 10)
+        MM21 = tstdat(i, 11)
+        SS12 = tstdat(i, 12)
+        call direct(a, f, lat1, lon1, azi1, s12, flags,
+     +    lat2a, lon2a, azi2a, omask, a12a, m12a, MM12a, MM21a, SS12a)
+        r = r + assert(lat2, lat2a, 1d-13)
+        r = r + assert(lon2, lon2a, 1d-13)
+        r = r + assert(azi2, azi2a, 1d-13)
+        r = r + assert(a12, a12a, 1d-13)
+        r = r + assert(m12, m12a, 1d-8)
+        r = r + assert(MM12, MM12a, 1d-15)
+        r = r + assert(MM21, MM21a, 1d-15)
+        r = r + assert(SS12, SS12a, 0.1d0)
+      end do
+
+      tstdir = r
+      return
+      end
+
+      integer function tstarc()
+      double precision tstdat(20, 12)
+      common /tstcom/ tstdat
+      double precision lat1, lon1, azi1, lat2, lon2, azi2,
+     +    s12, a12, m12, MM12, MM21, SS12
+      double precision lat2a, lon2a, azi2a, s12a,
+     +    m12a, MM12a, MM21a, SS12a
+      double precision a, f
+      integer r, assert, i, omask, flags
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 1+2+4+8
+      flags = 1+2
+      r = 0
+
+      do i = 1,20
+        lat1 = tstdat(i, 1)
+        lon1 = tstdat(i, 2)
+        azi1 = tstdat(i, 3)
+        lat2 = tstdat(i, 4)
+        lon2 = tstdat(i, 5)
+        azi2 = tstdat(i, 6)
+        s12 = tstdat(i, 7)
+        a12 = tstdat(i, 8)
+        m12 = tstdat(i, 9)
+        MM12 = tstdat(i, 10)
+        MM21 = tstdat(i, 11)
+        SS12 = tstdat(i, 12)
+        call direct(a, f, lat1, lon1, azi1, a12, flags,
+     +    lat2a, lon2a, azi2a, omask, s12a, m12a, MM12a, MM21a, SS12a)
+        r = r + assert(lat2, lat2a, 1d-13)
+        r = r + assert(lon2, lon2a, 1d-13)
+        r = r + assert(azi2, azi2a, 1d-13)
+        r = r + assert(s12, s12a, 1d-8)
+        r = r + assert(m12, m12a, 1d-8)
+        r = r + assert(MM12, MM12a, 1d-15)
+        r = r + assert(MM21, MM21a, 1d-15)
+        r = r + assert(SS12, SS12a, 0.1d0)
+      end do
+
+      tstarc = r
+      return
+      end
+
+      integer function notnan(x)
+      double precision x
+      if (x .eq. x) then
+        notnan = 1
+      else
+        notnan = 0
+      end if
+
+      return
+      end
+
+      integer function tstg0()
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 40.6d0, -73.8d0, 49.01666667d0, 2.55d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 53.47022d0, 0.5d-5)
+      r = r + assert(azi2, 111.59367d0, 0.5d-5)
+      r = r + assert(s12, 5853226d0, 0.5d0)
+
+      tstg0 = r
+      return
+      end
+
+      integer function tstg1()
+      double precision lat2, lon2, azi2, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask, flags
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      flags = 0
+      r = 0
+      call direct(a, f, 40.63972222d0, -73.77888889d0, 53.5d0, 5850d3,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(lat2, 49.01467d0, 0.5d-5)
+      r = r + assert(lon2, 2.56106d0, 0.5d-5)
+      r = r + assert(azi2, 111.62947d0, 0.5d-5)
+
+      tstg1 = r
+      return
+      end
+
+      integer function tstg2()
+* Check fix for antipodal prolate bug found 2010-09-04
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+      a = 6.4d6
+      f = -1/150d0
+      omask = 0
+      r = 0
+      call invers(a, f, 0.07476d0, 0d0, -0.07476d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00078d0, 0.5d-5)
+      r = r + assert(azi2, 90.00078d0, 0.5d-5)
+      r = r + assert(s12, 20106193d0, 0.5d0)
+      call invers(a, f, 0.1d0, 0d0, -0.1d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00105d0, 0.5d-5)
+      r = r + assert(azi2, 90.00105d0, 0.5d-5)
+      r = r + assert(s12, 20106193d0, 0.5d0)
+
+      tstg2 = r
+      return
+      end
+
+      integer function tstg4()
+* Check fix for short line bug found 2010-05-21
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f,
+     +    36.493349428792d0, 0d0, 36.49334942879201d0, .0000008d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 0.072d0, 0.5d-3)
+
+      tstg4 = r
+      return
+      end
+
+      integer function tstg5()
+      double precision lat2, lon2, azi2, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask, flags
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      flags = 0
+      r = 0
+      call direct(a, f, 0.01777745589997d0, 30d0, 0d0, 10d6,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      if (lon2 .lt. 0) then
+        r = r + assert(lon2, -150d0, 0.5d-5)
+        r = r + assert(azi2, -180d0, 0.5d-5)
+      else
+        r = r + assert(lon2, 30d0, 0.5d-5)
+        r = r + assert(azi2, 0d0, 0.5d-5)
+      end if
+
+      tstg5 = r
+      return
+      end
+
+      integer function tstg6()
+* Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4d0.4d0
+* x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6d0.1d0).
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 88.202499451857d0, 0d0,
+     +    -88.202499451857d0, 179.981022032992859592d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 20003898.214d0, 0.5d-3)
+      call invers(a, f, 89.262080389218d0, 0d0,
+     +    -89.262080389218d0, 179.992207982775375662d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 20003925.854d0, 0.5d-3)
+      call invers(a, f, 89.333123580033d0, 0d0,
+     +    -89.333123580032997687d0, 179.99295812360148422d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 20003926.881d0, 0.5d-3)
+
+      tstg6 = r
+      return
+      end
+
+      integer function tstg9()
+* Check fix for volatile x bug found 2011-06-25 (gcc 4.4d0.4d0 x86 -O3)
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 56.320923501171d0, 0d0,
+     +    -56.320923501171d0, 179.664747671772880215d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 19993558.287d0, 0.5d-3)
+
+      tstg9 = r
+      return
+      end
+
+      integer function tstg10()
+* Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio 10 rel
+* + debug)
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 52.784459512564d0, 0d0,
+     +    -52.784459512563990912d0, 179.634407464943777557d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 19991596.095d0, 0.5d-3)
+
+      tstg10 = r
+      return
+      end
+
+      integer function tstg11()
+* Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio 10 rel
+* + debug)
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 48.522876735459d0, 0d0,
+     +    -48.52287673545898293d0, 179.599720456223079643d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(s12, 19989144.774d0, 0.5d-3)
+
+      tstg11 = r
+      return
+      end
+
+      integer function tstg12()
+* Check fix for inverse geodesics on extreme prolate/oblate ellipsoids
+* Reported 2012-08-29 Stefan Guenther <stefan.gunther at embl.de>; fixed
+* 2012-10-07
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+      a = 89.8d0
+      f = -1.83d0
+      omask = 0
+      r = 0
+      call invers(a, f, 0d0, 0d0, -10d0, 160d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 120.27d0, 1d-2)
+      r = r + assert(azi2, 105.15d0, 1d-2)
+      r = r + assert(s12, 266.7d0, 1d-1)
+
+      tstg12 = r
+      return
+      end
+
+      integer function tstg14()
+* Check fix for inverse ignoring lon12 = nan
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f, LatFix
+      integer r, notnan, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 0d0, 0d0, 1d0, LatFix(91d0),
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + notnan(azi1)
+      r = r + notnan(azi2)
+      r = r + notnan(s12)
+      tstg14 = r
+      return
+      end
+
+      integer function tstg15()
+* Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+* checks that this is fixed.
+      double precision lat2, lon2, azi2, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask, flags
+      include 'geodesic.inc'
+
+      a = 6.4d6
+      f = -1/150.0d0
+      omask = 8
+      flags = 0
+      r = 0
+      call direct(a, f, 1d0, 2d0, 3d0, 4d0,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(SS12, 23700d0, 0.5d0)
+
+      tstg15 = r
+      return
+      end
+
+      integer function tstg17()
+* Check fix for LONG_UNROLL bug found on 2015-05-07
+      double precision lat2, lon2, azi2, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask, flags
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 8
+      flags = 2
+      r = 0
+      call direct(a, f, 40d0, -75d0, -10d0, 2d7,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(lat2, -39d0, 1d0)
+      r = r + assert(lon2, -254d0, 1d0)
+      r = r + assert(azi2, -170d0, 1d0)
+      flags = 0
+      call direct(a, f, 40d0, -75d0, -10d0, 2d7,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(lat2, -39d0, 1d0)
+      r = r + assert(lon2, 105d0, 1d0)
+      r = r + assert(azi2, -170d0, 1d0)
+
+      tstg17 = r
+      return
+      end
+
+      integer function tstg26()
+* Check 0/0 problem with area calculation on sphere 2015-09-08
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+      a = 6.4d6
+      f = 0
+      omask = 8
+      r = 0
+      call invers(a, f, 1d0, 2d0, 3d0, 4d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(SS12, 49911046115.0d0, 0.5d0)
+
+      tstg26 = r
+      return
+      end
+
+      integer function tstg28()
+* Check fix for LONG_UNROLL bug found on 2015-05-07
+      double precision lat2, lon2, azi2, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask, flags
+      include 'geodesic.inc'
+
+      a = 6.4d6
+      f = 0.1d0
+      omask = 1 + 2 + 4 + 8
+      flags = 0
+      r = 0
+      call direct(a, f, 1d0, 2d0, 10d0, 5d6,
+     +    flags, lat2, lon2, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(a12, 48.55570690d0, 0.5d-8)
+
+      tstg28 = r
+      return
+      end
+
+      integer function tstg33()
+* Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+* sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+* fmod(-0.0, 360.0) = +0.0.
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, assert, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 0d0, 0d0, 0d0, 179d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00000d0, 0.5d-5)
+      r = r + assert(azi2, 90.00000d0, 0.5d-5)
+      r = r + assert(s12, 19926189d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 0d0, 179.5d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 55.96650d0, 0.5d-5)
+      r = r + assert(azi2, 124.03350d0, 0.5d-5)
+      r = r + assert(s12, 19980862d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 0d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 0.00000d0, 0.5d-5)
+      r = r + assert(azi2, -180.00000d0, 0.5d-5)
+      r = r + assert(s12, 20003931d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 1d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 0.00000d0, 0.5d-5)
+      r = r + assert(azi2, -180.00000d0, 0.5d-5)
+      r = r + assert(s12, 19893357d0, 0.5d0)
+      a = 6.4d6
+      f = 0
+      call invers(a, f, 0d0, 0d0, 0d0, 179d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00000d0, 0.5d-5)
+      r = r + assert(azi2, 90.00000d0, 0.5d-5)
+      r = r + assert(s12, 19994492d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 0d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 0.00000d0, 0.5d-5)
+      r = r + assert(azi2, -180.00000d0, 0.5d-5)
+      r = r + assert(s12, 20106193d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 1d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 0.00000d0, 0.5d-5)
+      r = r + assert(azi2, -180.00000d0, 0.5d-5)
+      r = r + assert(s12, 19994492d0, 0.5d0)
+      a = 6.4d6
+      f = -1/300.0d0
+      call invers(a, f, 0d0, 0d0, 0d0, 179d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00000d0, 0.5d-5)
+      r = r + assert(azi2, 90.00000d0, 0.5d-5)
+      r = r + assert(s12, 19994492d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 0d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 90.00000d0, 0.5d-5)
+      r = r + assert(azi2, 90.00000d0, 0.5d-5)
+      r = r + assert(s12, 20106193d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 0.5d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 33.02493d0, 0.5d-5)
+      r = r + assert(azi2, 146.97364d0, 0.5d-5)
+      r = r + assert(s12, 20082617d0, 0.5d0)
+      call invers(a, f, 0d0, 0d0, 1d0, 180d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + assert(azi1, 0.00000d0, 0.5d-5)
+      r = r + assert(azi2, -180.00000d0, 0.5d-5)
+      r = r + assert(s12, 20027270d0, 0.5d0)
+
+      tstg33 = r
+      return
+      end
+
+      integer function tstg55()
+* Check fix for nan + point on equator or pole not returning all nans in
+* Geodesic::Inverse, found 2015-09-23.
+      double precision azi1, azi2, s12, a12, m12, MM12, MM21, SS12
+      double precision a, f
+      integer r, notnan, omask
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      omask = 0
+      r = 0
+      call invers(a, f, 91d0, 0d0, 0d0, 90d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + notnan(azi1)
+      r = r + notnan(azi2)
+      r = r + notnan(s12)
+      call invers(a, f, 91d0, 0d0, 90d0, 9d0,
+     +    s12, azi1, azi2, omask, a12, m12, MM12, MM21, SS12)
+      r = r + notnan(azi1)
+      r = r + notnan(azi2)
+      r = r + notnan(s12)
+      tstg55 = r
+      return
+      end
+
+      integer function tstp0()
+* Check fix for pole-encircling bug found 2011-03-16
+      double precision lata(4), lona(4)
+      data lata / 89d0, 89d0, 89d0, 89d0 /
+      data lona / 0d0, 90d0, 180d0, 270d0 /
+      double precision latb(4), lonb(4)
+      data latb / -89d0, -89d0, -89d0, -89d0 /
+      data lonb / 0d0, 90d0, 180d0, 270d0 /
+      double precision latc(4), lonc(4)
+      data latc / 0d0, -1d0, 0d0, 1d0 /
+      data lonc / -1d0, 0d0, 1d0, 0d0 /
+      double precision latd(3), lond(3)
+      data latd / 90d0, 0d0, 0d0 /
+      data lond / 0d0, 0d0, 90d0 /
+      double precision a, f, AA, PP
+      integer r, assert
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      r = 0
+
+      call area(a, f, lata, lona, 4, AA, PP)
+      r = r + assert(PP, 631819.8745d0, 1d-4)
+      r = r + assert(AA, 24952305678.0d0, 1d0)
+
+      call area(a, f, latb, lonb, 4, AA, PP)
+      r = r + assert(PP, 631819.8745d0, 1d-4)
+      r = r + assert(AA, -24952305678.0d0, 1d0)
+
+      call area(a, f, latc, lonc, 4, AA, PP)
+      r = r + assert(PP, 627598.2731d0, 1d-4)
+      r = r + assert(AA, 24619419146.0d0, 1d0)
+
+      call area(a, f, latd, lond, 3, AA, PP)
+      r = r + assert(PP, 30022685d0, 1d0)
+      r = r + assert(AA, 63758202715511.0d0, 1d0)
+
+      tstp0 = r
+      return
+      end
+
+      integer function tstp5()
+* Check fix for Planimeter pole crossing bug found 2011-06-24
+      double precision lat(3), lon(3)
+      data lat / 89d0, 89d0, 89d0 /
+      data lon / 0.1d0, 90.1d0, -179.9d0 /
+      double precision a, f, AA, PP
+      integer r, assert
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      r = 0
+
+      call area(a, f, lat, lon, 3, AA, PP)
+      r = r + assert(PP, 539297d0, 1d0)
+      r = r + assert(AA, 12476152838.5d0, 1d0)
+
+      tstp5 = r
+      return
+      end
+
+      integer function tstp6()
+* Check fix for pole-encircling bug found 2011-03-16
+      double precision lata(3), lona(3)
+      data lata / 9d0, 9d0, 9d0 /
+      data lona / -0.00000000000001d0, 180d0, 0d0 /
+      double precision latb(3), lonb(3)
+      data latb / 9d0, 9d0, 9d0 /
+      data lonb / 0.00000000000001d0, 0d0, 180d0 /
+      double precision latc(3), lonc(3)
+      data latc / 9d0, 9d0, 9d0 /
+      data lonc / 0.00000000000001d0, 180d0, 0d0 /
+      double precision latd(3), lond(3)
+      data latd / 9d0, 9d0, 9d0 /
+      data lond / -0.00000000000001d0, 0d0, 180d0 /
+      double precision a, f, AA, PP
+      integer r, assert
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      r = 0
+
+      call area(a, f, lata, lona, 3, AA, PP)
+      r = r + assert(PP, 36026861d0, 1d0)
+      r = r + assert(AA, 0d0, 1d0)
+
+      tstp6 = r
+      return
+      end
+
+      integer function tstp12()
+* AA of arctic circle (not really -- adjunct to rhumb-AA test)
+      double precision lat(2), lon(2)
+      data lat / 66.562222222d0, 66.562222222d0 /
+      data lon / 0d0, 180d0 /
+      double precision a, f, AA, PP
+      integer r, assert
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      r = 0
+
+      call area(a, f, lat, lon, 2, AA, PP)
+      r = r + assert(PP, 10465729d0, 1d0)
+      r = r + assert(AA, 0d0, 1d0)
+
+      tstp12 = r
+      return
+      end
+
+      integer function tstp13()
+* Check encircling pole twice
+      double precision lat(6), lon(6)
+      data lat / 89d0, 89d0, 89d0, 89d0, 89d0, 89d0 /
+      data lon / -360d0, -240d0, -120d0, 0d0, 120d0, 240d0 /
+      double precision a, f, AA, PP
+      integer r, assert
+      include 'geodesic.inc'
+
+* WGS84 values
+      a = 6378137d0
+      f = 1/298.257223563d0
+      r = 0
+
+      call area(a, f, lat, lon, 6, AA, PP)
+      r = r + assert(PP, 1160741d0, 1d0)
+      r = r + assert(AA, 32415230256.0d0, 1d0)
+
+      tstp13 = r
+      return
+      end
+
+      program geodtest
+      integer n, i
+      integer tstinv, tstdir, tstarc,
+     +    tstg0, tstg1, tstg2, tstg5, tstg6, tstg9, tstg10, tstg11,
+     +    tstg12, tstg14, tstg15, tstg17, tstg26, tstg28, tstg33,
+     +    tstg55, tstp0, tstp5, tstp6, tstp12, tstp13
+
+      n = 0
+      i = tstinv()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstinv fail:', i
+      end if
+      i = tstdir()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstdir fail:', i
+      end if
+      i = tstarc()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstarc fail:', i
+      end if
+      i = tstg0()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg0 fail:', i
+      end if
+      i = tstg1()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg1 fail:', i
+      end if
+      i = tstg2()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg2 fail:', i
+      end if
+      i = tstg5()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg5 fail:', i
+      end if
+      i = tstg6()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg6 fail:', i
+      end if
+      i = tstg9()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg9 fail:', i
+      end if
+      i = tstg10()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg10 fail:', i
+      end if
+      i = tstg11()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg11 fail:', i
+      end if
+      i = tstg12()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg12 fail:', i
+      end if
+      i = tstg14()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg14 fail:', i
+      end if
+      i = tstg15()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg15 fail:', i
+      end if
+      i = tstg17()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg17 fail:', i
+      end if
+      i = tstg26()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg26 fail:', i
+      end if
+      i = tstg28()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg28 fail:', i
+      end if
+      i = tstg33()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg33 fail:', i
+      end if
+      i = tstg55()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstg55 fail:', i
+      end if
+      i = tstp0()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstp0 fail:', i
+      end if
+      i = tstp5()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstp5 fail:', i
+      end if
+      i = tstp6()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstp6 fail:', i
+      end if
+      i = tstp12()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstp12 fail:', i
+      end if
+      i = tstp13()
+      if (i .gt. 0) then
+        n = n + 1
+        print *, 'tstp13 fail:', i
+      end if
+
+      if (n .gt. 0) then
+        stop 1
+      end if
+
+      stop
+      end
+
+*> @endcond SKIP
diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt
index 70fd897..3f3929f 100644
--- a/man/CMakeLists.txt
+++ b/man/CMakeLists.txt
@@ -93,9 +93,7 @@ if (MAINTAINER)
   add_custom_command (TARGET distrib-man
     COMMAND
       for f in ${MANPAGES} ${USAGE} ${HTMLMAN}\; do
-        cmp "$$f" ${CMAKE_CURRENT_SOURCE_DIR}/`basename "$$f" `
-        >/dev/null 2>&1 ||
-        install -m 644 "$$f" ${CMAKE_CURRENT_SOURCE_DIR}\; done
+        install -C -m 644 "$$f" ${CMAKE_CURRENT_SOURCE_DIR}\; done
     COMMENT "Installing man documentation page in source tree")
 else ()
   add_custom_target (htmlman ALL DEPENDS ${HTMLMAN})
diff --git a/man/CartConvert.1 b/man/CartConvert.1
index 1111f23..d5aca33 100644
--- a/man/CartConvert.1
+++ b/man/CartConvert.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "CARTCONVERT 1"
-.TH CARTCONVERT 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH CARTCONVERT 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -185,9 +185,8 @@ geographic coordinates, provided that it appears before \fB\-l\fR.
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.
 .IP "\fB\-w\fR" 4
 .IX Item "-w"
 on input and output, longitude precedes latitude (except that, on input,
diff --git a/man/CartConvert.1.html b/man/CartConvert.1.html
index d44a714..1cea662 100644
--- a/man/CartConvert.1.html
+++ b/man/CartConvert.1.html
@@ -44,7 +44,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
 
 </dd>
 <dt id="w"><b>-w</b></dt>
diff --git a/man/CartConvert.pod b/man/CartConvert.pod
index 79073b3..e80b322 100644
--- a/man/CartConvert.pod
+++ b/man/CartConvert.pod
@@ -53,9 +53,8 @@ geographic coordinates, provided that it appears before B<-l>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.
 
 =item B<-w>
 
diff --git a/man/CartConvert.usage b/man/CartConvert.usage
index d6723b2..195e5b6 100644
--- a/man/CartConvert.usage
+++ b/man/CartConvert.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    CartConvert --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/CartConvert.1.html\n";
+"    http://geographiclib.sf.net/1.45/CartConvert.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -54,9 +54,8 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.\n"
 "\n"
 "       -w  on input and output, longitude precedes latitude (except that, on\n"
 "           input, this can be overridden by a hemisphere designator, N, S, E,\n"
diff --git a/man/ConicProj.1 b/man/ConicProj.1
index 252778a..b9e3f6a 100644
--- a/man/ConicProj.1
+++ b/man/ConicProj.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "CONICPROJ 1"
-.TH CONICPROJ 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH CONICPROJ 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -209,9 +209,8 @@ input and each line of standard output gives \fIlatitude\fR, \fIlongitude\fR,
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.
 .IP "\fB\-w\fR" 4
 .IX Item "-w"
 on input and output, longitude precedes latitude (except that, on input,
diff --git a/man/ConicProj.1.html b/man/ConicProj.1.html
index 0ef4bb9..e07e501 100644
--- a/man/ConicProj.1.html
+++ b/man/ConicProj.1.html
@@ -64,7 +64,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
 
 </dd>
 <dt id="w"><b>-w</b></dt>
diff --git a/man/ConicProj.pod b/man/ConicProj.pod
index a6d54de..a59c348 100644
--- a/man/ConicProj.pod
+++ b/man/ConicProj.pod
@@ -80,9 +80,8 @@ I<gamma>, and I<k>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.
 
 =item B<-w>
 
diff --git a/man/ConicProj.usage b/man/ConicProj.usage
index 487ee22..e9ad2e6 100644
--- a/man/ConicProj.usage
+++ b/man/ConicProj.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    ConicProj --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/ConicProj.1.html\n";
+"    http://geographiclib.sf.net/1.45/ConicProj.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -72,9 +72,8 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.\n"
 "\n"
 "       -w  on input and output, longitude precedes latitude (except that, on\n"
 "           input, this can be overridden by a hemisphere designator, N, S, E,\n"
diff --git a/man/GeoConvert.1 b/man/GeoConvert.1
index 367e678..6d1c914 100644
--- a/man/GeoConvert.1
+++ b/man/GeoConvert.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "GEOCONVERT 1"
-.TH GEOCONVERT 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH GEOCONVERT 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff --git a/man/GeoConvert.usage b/man/GeoConvert.usage
index 67c8b6f..215185a 100644
--- a/man/GeoConvert.usage
+++ b/man/GeoConvert.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    GeoConvert --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/GeoConvert.1.html\n";
+"    http://geographiclib.sf.net/1.45/GeoConvert.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
diff --git a/man/GeodSolve.1 b/man/GeodSolve.1
index 5bf7fb2..be19c70 100644
--- a/man/GeodSolve.1
+++ b/man/GeodSolve.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "GEODSOLVE 1"
-.TH GEODSOLVE 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH GEODSOLVE 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -191,9 +191,8 @@ length (in degrees) on the auxiliary sphere.  See \*(L"\s-1AUXILIARY SPHERE\*(R"
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.
 .IP "\fB\-u\fR" 4
 .IX Item "-u"
 unroll the longitude.  Normally, on output longitudes are reduced to lie
diff --git a/man/GeodSolve.1.html b/man/GeodSolve.1.html
index f03a446..fba339a 100644
--- a/man/GeodSolve.1.html
+++ b/man/GeodSolve.1.html
@@ -63,7 +63,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
 
 </dd>
 <dt id="u"><b>-u</b></dt>
diff --git a/man/GeodSolve.pod b/man/GeodSolve.pod
index e4d561b..02f9929 100644
--- a/man/GeodSolve.pod
+++ b/man/GeodSolve.pod
@@ -70,9 +70,8 @@ length (in degrees) on the auxiliary sphere.  See L</AUXILIARY SPHERE>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.
 
 =item B<-u>
 
diff --git a/man/GeodSolve.usage b/man/GeodSolve.usage
index f856ced..7b2143b 100644
--- a/man/GeodSolve.usage
+++ b/man/GeodSolve.usage
@@ -10,7 +10,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    GeodSolve --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/GeodSolve.1.html\n";
+"    http://geographiclib.sf.net/1.45/GeodSolve.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -58,9 +58,8 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.\n"
 "\n"
 "       -u  unroll the longitude.  Normally, on output longitudes are reduced\n"
 "           to lie in [-180deg,180deg).  However with this option, the returned\n"
diff --git a/man/GeodesicProj.1 b/man/GeodesicProj.1
index c36ad97..18a8835 100644
--- a/man/GeodesicProj.1
+++ b/man/GeodesicProj.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "GEODESICPROJ 1"
-.TH GEODESICPROJ 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH GEODESICPROJ 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -199,9 +199,8 @@ input and each line of standard output gives \fIlatitude\fR, \fIlongitude\fR,
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.
 .IP "\fB\-w\fR" 4
 .IX Item "-w"
 on input and output, longitude precedes latitude (except that, on input,
diff --git a/man/GeodesicProj.1.html b/man/GeodesicProj.1.html
index febd151..b76a0a1 100644
--- a/man/GeodesicProj.1.html
+++ b/man/GeodesicProj.1.html
@@ -56,7 +56,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
 
 </dd>
 <dt id="w"><b>-w</b></dt>
diff --git a/man/GeodesicProj.pod b/man/GeodesicProj.pod
index addfef4..d904966 100644
--- a/man/GeodesicProj.pod
+++ b/man/GeodesicProj.pod
@@ -69,9 +69,8 @@ I<azi>, and I<rk>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.
 
 =item B<-w>
 
diff --git a/man/GeodesicProj.usage b/man/GeodesicProj.usage
index 1d1e2ec..91e93c4 100644
--- a/man/GeodesicProj.usage
+++ b/man/GeodesicProj.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    GeodesicProj --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/GeodesicProj.1.html\n";
+"    http://geographiclib.sf.net/1.45/GeodesicProj.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -65,9 +65,8 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.\n"
 "\n"
 "       -w  on input and output, longitude precedes latitude (except that, on\n"
 "           input, this can be overridden by a hemisphere designator, N, S, E,\n"
diff --git a/man/GeoidEval.1 b/man/GeoidEval.1
index b7358f9..c5afa3f 100644
--- a/man/GeoidEval.1
+++ b/man/GeoidEval.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "GEOIDEVAL 1"
-.TH GEOIDEVAL 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH GEOIDEVAL 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff --git a/man/GeoidEval.usage b/man/GeoidEval.usage
index d3bb615..30bdbe2 100644
--- a/man/GeoidEval.usage
+++ b/man/GeoidEval.usage
@@ -10,7 +10,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    GeoidEval --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/GeoidEval.1.html\n";
+"    http://geographiclib.sf.net/1.45/GeoidEval.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
diff --git a/man/Gravity.1 b/man/Gravity.1
index f52ff8d..a93796b 100644
--- a/man/Gravity.1
+++ b/man/Gravity.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "GRAVITY 1"
-.TH GRAVITY 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH GRAVITY 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff --git a/man/Gravity.usage b/man/Gravity.usage
index d235042..b50f2ab 100644
--- a/man/Gravity.usage
+++ b/man/Gravity.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    Gravity --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/Gravity.1.html\n";
+"    http://geographiclib.sf.net/1.45/Gravity.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
diff --git a/man/MagneticField.1 b/man/MagneticField.1
index 134f87a..407ee1e 100644
--- a/man/MagneticField.1
+++ b/man/MagneticField.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "MAGNETICFIELD 1"
-.TH MAGNETICFIELD 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH MAGNETICFIELD 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff --git a/man/MagneticField.usage b/man/MagneticField.usage
index cc9a189..d8e9862 100644
--- a/man/MagneticField.usage
+++ b/man/MagneticField.usage
@@ -10,7 +10,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    MagneticField --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/MagneticField.1.html\n";
+"    http://geographiclib.sf.net/1.45/MagneticField.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
diff --git a/man/Planimeter.1 b/man/Planimeter.1
index a47af16..199a4ab 100644
--- a/man/Planimeter.1
+++ b/man/Planimeter.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "PLANIMETER 1"
-.TH PLANIMETER 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH PLANIMETER 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -207,11 +207,11 @@ not reported.
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.  If entering vertices as \s-1UTM/UPS\s0 or \s-1MGRS\s0
-coordinates, use the default ellipsoid, since the conversion of these
-coordinates to latitude and longitude always uses the \s-1WGS84\s0 parameters.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.  If entering vertices as \s-1UTM/UPS\s0 or
+\&\s-1MGRS\s0 coordinates, use the default ellipsoid, since the conversion of
+these coordinates to latitude and longitude always uses the \s-1WGS84\s0
+parameters.
 .IP "\fB\-w\fR" 4
 .IX Item "-w"
 when reading geographic coordinates, longitude precedes latitude (this
diff --git a/man/Planimeter.1.html b/man/Planimeter.1.html
index 3cb3c25..6c4af45 100644
--- a/man/Planimeter.1.html
+++ b/man/Planimeter.1.html
@@ -56,7 +56,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563. If entering vertices as UTM/UPS or MGRS coordinates, use the default ellip [...]
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563. If entering vertices as UTM/UPS or MGRS coordinates, use the default ellipsoid, since the conversion of these coordinates to latitude and l [...]
 
 </dd>
 <dt id="w"><b>-w</b></dt>
diff --git a/man/Planimeter.pod b/man/Planimeter.pod
index 4e01d03..7abb3e7 100644
--- a/man/Planimeter.pod
+++ b/man/Planimeter.pod
@@ -76,11 +76,11 @@ not reported.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.  If entering vertices as UTM/UPS or MGRS
-coordinates, use the default ellipsoid, since the conversion of these
-coordinates to latitude and longitude always uses the WGS84 parameters.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.  If entering vertices as UTM/UPS or
+MGRS coordinates, use the default ellipsoid, since the conversion of
+these coordinates to latitude and longitude always uses the WGS84
+parameters.
 
 =item B<-w>
 
diff --git a/man/Planimeter.usage b/man/Planimeter.usage
index c51982d..adc9e4a 100644
--- a/man/Planimeter.usage
+++ b/man/Planimeter.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    Planimeter --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/Planimeter.1.html\n";
+"    http://geographiclib.sf.net/1.45/Planimeter.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -73,12 +73,11 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.  If entering vertices as UTM/UPS or MGRS\n"
-"           coordinates, use the default ellipsoid, since the conversion of\n"
-"           these coordinates to latitude and longitude always uses the WGS84\n"
-"           parameters.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.  If entering vertices as UTM/UPS or\n"
+"           MGRS coordinates, use the default ellipsoid, since the conversion\n"
+"           of these coordinates to latitude and longitude always uses the\n"
+"           WGS84 parameters.\n"
 "\n"
 "       -w  when reading geographic coordinates, longitude precedes latitude\n"
 "           (this can be overridden by a hemisphere designator, N, S, E, W).\n"
diff --git a/man/RhumbSolve.1 b/man/RhumbSolve.1
index 37d872c..cbc5fbc 100644
--- a/man/RhumbSolve.1
+++ b/man/RhumbSolve.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RHUMBSOLVE 1"
-.TH RHUMBSOLVE 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH RHUMBSOLVE 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -200,9 +200,8 @@ it appears before \fB\-l\fR.
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.
 .IP "\fB\-d\fR" 4
 .IX Item "-d"
 output angles as degrees, minutes, seconds instead of decimal degrees.
diff --git a/man/RhumbSolve.1.html b/man/RhumbSolve.1.html
index bc52287..751992e 100644
--- a/man/RhumbSolve.1.html
+++ b/man/RhumbSolve.1.html
@@ -59,7 +59,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563.</p>
 
 </dd>
 <dt id="d"><b>-d</b></dt>
diff --git a/man/RhumbSolve.pod b/man/RhumbSolve.pod
index 34ce44e..94ce957 100644
--- a/man/RhumbSolve.pod
+++ b/man/RhumbSolve.pod
@@ -78,9 +78,8 @@ it appears before B<-l>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.
 
 =item B<-d>
 
diff --git a/man/RhumbSolve.usage b/man/RhumbSolve.usage
index 0c2ef70..d72eaab 100644
--- a/man/RhumbSolve.usage
+++ b/man/RhumbSolve.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    RhumbSolve --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/RhumbSolve.1.html\n";
+"    http://geographiclib.sf.net/1.45/RhumbSolve.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -65,9 +65,8 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.\n"
 "\n"
 "       -d  output angles as degrees, minutes, seconds instead of decimal\n"
 "           degrees.\n"
diff --git a/man/TransverseMercatorProj.1 b/man/TransverseMercatorProj.1
index b61d431..ae70524 100644
--- a/man/TransverseMercatorProj.1
+++ b/man/TransverseMercatorProj.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "TRANSVERSEMERCATORPROJ 1"
-.TH TRANSVERSEMERCATORPROJ 1 "2015-08-14" "GeographicLib 1.44" "GeographicLib Utilities"
+.TH TRANSVERSEMERCATORPROJ 1 "2015-09-30" "GeographicLib 1.45" "GeographicLib Utilities"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -192,10 +192,9 @@ input and each line of standard output gives \fIlatitude\fR, \fIlongitude\fR,
 specify the ellipsoid via \fIa\fR \fIf\fR; the equatorial radius is \fIa\fR and
 the flattening is \fIf\fR.  Setting \fIf\fR = 0 results in a sphere.  Specify
 \&\fIf\fR < 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for \fIf\fR.  (Also, if \fIf\fR > 1, the flattening is set to
-1/\fIf\fR.)  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR = 6378137 m,
-\&\fIf\fR = 1/298.257223563.  If the exact algorithm is used, \fIf\fR must be
-positive.
+is allowed for \fIf\fR.  By default, the \s-1WGS84\s0 ellipsoid is used, \fIa\fR =
+6378137 m, \fIf\fR = 1/298.257223563.  If the exact algorithm is used, \fIf\fR
+must be positive.
 .IP "\fB\-w\fR" 4
 .IX Item "-w"
 on input and output, longitude precedes latitude (except that on input
diff --git a/man/TransverseMercatorProj.1.html b/man/TransverseMercatorProj.1.html
index 6c94c52..3923e09 100644
--- a/man/TransverseMercatorProj.1.html
+++ b/man/TransverseMercatorProj.1.html
@@ -62,7 +62,7 @@
 <dt id="e"><b>-e</b></dt>
 <dd>
 
-<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. (Also, if <i>f</i> > 1, the flattening is set to 1/<i>f</i>.) By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563. If the exact algorithm is used, <i>f</i> must be positive.</p>
+<p>specify the ellipsoid via <i>a</i> <i>f</i>; the equatorial radius is <i>a</i> and the flattening is <i>f</i>. Setting <i>f</i> = 0 results in a sphere. Specify <i>f</i> < 0 for a prolate ellipsoid. A simple fraction, e.g., 1/297, is allowed for <i>f</i>. By default, the WGS84 ellipsoid is used, <i>a</i> = 6378137 m, <i>f</i> = 1/298.257223563. If the exact algorithm is used, <i>f</i> must be positive.</p>
 
 </dd>
 <dt id="w"><b>-w</b></dt>
diff --git a/man/TransverseMercatorProj.pod b/man/TransverseMercatorProj.pod
index 95669ad..9662bb9 100644
--- a/man/TransverseMercatorProj.pod
+++ b/man/TransverseMercatorProj.pod
@@ -63,10 +63,9 @@ I<gamma>, and I<k>.
 specify the ellipsoid via I<a> I<f>; the equatorial radius is I<a> and
 the flattening is I<f>.  Setting I<f> = 0 results in a sphere.  Specify
 I<f> E<lt> 0 for a prolate ellipsoid.  A simple fraction, e.g., 1/297,
-is allowed for I<f>.  (Also, if I<f> E<gt> 1, the flattening is set to
-1/I<f>.)  By default, the WGS84 ellipsoid is used, I<a> = 6378137 m,
-I<f> = 1/298.257223563.  If the exact algorithm is used, I<f> must be
-positive.
+is allowed for I<f>.  By default, the WGS84 ellipsoid is used, I<a> =
+6378137 m, I<f> = 1/298.257223563.  If the exact algorithm is used, I<f>
+must be positive.
 
 =item B<-w>
 
diff --git a/man/TransverseMercatorProj.usage b/man/TransverseMercatorProj.usage
index 504b243..5906472 100644
--- a/man/TransverseMercatorProj.usage
+++ b/man/TransverseMercatorProj.usage
@@ -9,7 +9,7 @@ int usage(int retval, bool brief) {
 "For full documentation type:\n"
 "    TransverseMercatorProj --help\n"
 "or visit:\n"
-"    http://geographiclib.sf.net/1.44/TransverseMercatorProj.1.html\n";
+"    http://geographiclib.sf.net/1.45/TransverseMercatorProj.1.html\n";
   else
     ( retval ? std::cerr : std::cout ) << "Man page:\n"
 "NAME\n"
@@ -55,10 +55,9 @@ int usage(int retval, bool brief) {
 "       -e  specify the ellipsoid via a f; the equatorial radius is a and the\n"
 "           flattening is f.  Setting f = 0 results in a sphere.  Specify f < 0\n"
 "           for a prolate ellipsoid.  A simple fraction, e.g., 1/297, is\n"
-"           allowed for f.  (Also, if f > 1, the flattening is set to 1/f.)  By\n"
-"           default, the WGS84 ellipsoid is used, a = 6378137 m, f =\n"
-"           1/298.257223563.  If the exact algorithm is used, f must be\n"
-"           positive.\n"
+"           allowed for f.  By default, the WGS84 ellipsoid is used, a =\n"
+"           6378137 m, f = 1/298.257223563.  If the exact algorithm is used, f\n"
+"           must be positive.\n"
 "\n"
 "       -w  on input and output, longitude precedes latitude (except that on\n"
 "           input this can be overridden by a hemisphere designator, N, S, E,\n"
diff --git a/matlab/Makefile.am b/matlab/Makefile.am
index 4447607..e0626d9 100644
--- a/matlab/Makefile.am
+++ b/matlab/Makefile.am
@@ -5,6 +5,7 @@
 
 MATLAB_FILES = \
 $(srcdir)/geographiclib/Contents.m \
+$(srcdir)/geographiclib/geographiclib_test.m \
 $(srcdir)/geographiclib/cassini_fwd.m \
 $(srcdir)/geographiclib/cassini_inv.m \
 $(srcdir)/geographiclib/defaultellipsoid.m \
diff --git a/matlab/Makefile.in b/matlab/Makefile.in
index 36169b5..2d7ac0a 100644
--- a/matlab/Makefile.in
+++ b/matlab/Makefile.in
@@ -253,6 +253,7 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 MATLAB_FILES = \
 $(srcdir)/geographiclib/Contents.m \
+$(srcdir)/geographiclib/geographiclib_test.m \
 $(srcdir)/geographiclib/cassini_fwd.m \
 $(srcdir)/geographiclib/cassini_inv.m \
 $(srcdir)/geographiclib/defaultellipsoid.m \
diff --git a/matlab/geographiclib-legacy/Contents.m b/matlab/geographiclib-legacy/Contents.m
index 061cd74..89230c1 100644
--- a/matlab/geographiclib-legacy/Contents.m
+++ b/matlab/geographiclib-legacy/Contents.m
@@ -34,5 +34,3 @@
 %   geographiclibinterface - Compile interface code (DEPRECATED)
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
diff --git a/matlab/geographiclib-legacy/geocentricforward.m b/matlab/geographiclib-legacy/geocentricforward.m
index 38f724b..dab49c0 100644
--- a/matlab/geographiclib-legacy/geocentricforward.m
+++ b/matlab/geographiclib-legacy/geocentricforward.m
@@ -25,8 +25,6 @@ function [geocentric, rot] = geocentricforward(geodetic, a, f)
 %   See also GEOCENT_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 2)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/geocentricreverse.m b/matlab/geographiclib-legacy/geocentricreverse.m
index 7c3c65e..6e05829 100644
--- a/matlab/geographiclib-legacy/geocentricreverse.m
+++ b/matlab/geographiclib-legacy/geocentricreverse.m
@@ -30,8 +30,6 @@ function [geodetic, rot] = geocentricreverse(geocentric, a, f)
 %   See also GEOCENT_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 2)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/geodesicdirect.m b/matlab/geographiclib-legacy/geodesicdirect.m
index d4ddb28..5aac8ec 100644
--- a/matlab/geographiclib-legacy/geodesicdirect.m
+++ b/matlab/geographiclib-legacy/geodesicdirect.m
@@ -32,8 +32,6 @@ function [latlong, aux] = geodesicdirect(geodesic, a, f)
 %   See also GEODRECKON.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 2)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/geodesicinverse.m b/matlab/geographiclib-legacy/geodesicinverse.m
index 09074a0..ef9d687 100644
--- a/matlab/geographiclib-legacy/geodesicinverse.m
+++ b/matlab/geographiclib-legacy/geodesicinverse.m
@@ -32,8 +32,6 @@ function [geodesic, aux] = geodesicinverse(latlong, a, f)
 %   See also GEODDISTANCE.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 2)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/geodesicline.m b/matlab/geographiclib-legacy/geodesicline.m
index e0dd680..2d7ed42 100644
--- a/matlab/geographiclib-legacy/geodesicline.m
+++ b/matlab/geographiclib-legacy/geodesicline.m
@@ -35,8 +35,6 @@ function [latlong, aux] = geodesicline(lat1, lon1, azi1, distances, a, f)
 %   See also GEODRECKON.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 5)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/geoidheight.m b/matlab/geographiclib-legacy/geoidheight.m
index 2800ed9..0d1acfa 100644
--- a/matlab/geographiclib-legacy/geoidheight.m
+++ b/matlab/geographiclib-legacy/geoidheight.m
@@ -26,8 +26,6 @@ function height = geoidheight(latlong, geoidname, geoiddir)
 %   See also GEOID_HEIGHT.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if nargin < 2
     geoidname = '';
diff --git a/matlab/geographiclib-legacy/localcartesianforward.m b/matlab/geographiclib-legacy/localcartesianforward.m
index 9de21c8..34cddc1 100644
--- a/matlab/geographiclib-legacy/localcartesianforward.m
+++ b/matlab/geographiclib-legacy/localcartesianforward.m
@@ -34,8 +34,6 @@ function [cartesian, rot] = localcartesianforward(origin, geodetic, a, f)
 %   See also LOCCART_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 3)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/localcartesianreverse.m b/matlab/geographiclib-legacy/localcartesianreverse.m
index d9067a4..65d1f25 100644
--- a/matlab/geographiclib-legacy/localcartesianreverse.m
+++ b/matlab/geographiclib-legacy/localcartesianreverse.m
@@ -34,8 +34,6 @@ function [geodetic, rot] = localcartesianreverse(origin, cartesian, a, f)
 %   See also LOCCART_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 3)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/mgrsforward.m b/matlab/geographiclib-legacy/mgrsforward.m
index c6bd551..6c4cbf9 100644
--- a/matlab/geographiclib-legacy/mgrsforward.m
+++ b/matlab/geographiclib-legacy/mgrsforward.m
@@ -24,8 +24,6 @@ function mgrs = mgrsforward(utmups, prec)
 %   See also MGRS_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if nargin < 2
     prec = 5;
diff --git a/matlab/geographiclib-legacy/mgrsreverse.m b/matlab/geographiclib-legacy/mgrsreverse.m
index 4a86024..95b4512 100644
--- a/matlab/geographiclib-legacy/mgrsreverse.m
+++ b/matlab/geographiclib-legacy/mgrsreverse.m
@@ -29,8 +29,6 @@ function [utmups, prec] = mgrsreverse(mgrs)
 %   See also MGRS_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   [x, y, z, h, prec] = mgrs_inv(mgrs);
   utmups = [x, y, z, h];
diff --git a/matlab/geographiclib-legacy/polygonarea.m b/matlab/geographiclib-legacy/polygonarea.m
index ad15a74..387202d 100644
--- a/matlab/geographiclib-legacy/polygonarea.m
+++ b/matlab/geographiclib-legacy/polygonarea.m
@@ -28,8 +28,6 @@ function [area, perimeter] = polygonarea(latlong, a, f)
 %   See also GEODAREA.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if (nargin < 2)
     ellipsoid = defaultellipsoid;
diff --git a/matlab/geographiclib-legacy/utmupsforward.m b/matlab/geographiclib-legacy/utmupsforward.m
index 7ab1f8a..4038889 100644
--- a/matlab/geographiclib-legacy/utmupsforward.m
+++ b/matlab/geographiclib-legacy/utmupsforward.m
@@ -33,8 +33,6 @@ function [utmups, scale] = utmupsforward(latlong, setzone)
 %   See also UTMUPS_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   if nargin < 2
     setzone = -1;
diff --git a/matlab/geographiclib-legacy/utmupsreverse.m b/matlab/geographiclib-legacy/utmupsreverse.m
index cb94a1d..2511fa0 100644
--- a/matlab/geographiclib-legacy/utmupsreverse.m
+++ b/matlab/geographiclib-legacy/utmupsreverse.m
@@ -26,8 +26,6 @@ function [latlong, scale] = utmupsreverse(utmups)
 %   See also UTMUPS_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   [lat, lon, gam, k] = ...
       utmups_inv(utmups(:,1), utmups(:,2), utmups(:,3), utmups(:,4));
diff --git a/matlab/geographiclib/Contents.m b/matlab/geographiclib/Contents.m
index c053af5..c42d3a6 100644
--- a/matlab/geographiclib/Contents.m
+++ b/matlab/geographiclib/Contents.m
@@ -1,5 +1,5 @@
 % GeographicLib toolbox
-% Version 1.44 2015-08-14
+% Version 1.45 2015-09-30
 %
 %   This toolbox provides native MATLAB implementations of a subset of the
 %   C++ library, GeographicLib.  Key components of this toolbox are
@@ -83,6 +83,7 @@
 %   defaultellipsoid - Return the WGS84 ellipsoid
 %   ecc2flat         - Convert eccentricity to flattening
 %   flat2ecc         - Convert flattening to eccentricity
+%   geographiclib_test - The test suite for the geographiclib package
 %
 % Documentation
 %   geoddoc          - Geodesics on an ellipsoid of revolution
@@ -90,5 +91,3 @@
 %   gedoc            - Great ellipses on an ellipsoid of revolution
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
diff --git a/matlab/geographiclib/cassini_fwd.m b/matlab/geographiclib/cassini_fwd.m
index eac89c1..99cb504 100644
--- a/matlab/geographiclib/cassini_fwd.m
+++ b/matlab/geographiclib/cassini_fwd.m
@@ -24,8 +24,6 @@ function [x, y, azi, rk] = cassini_fwd(lat0, lon0, lat, lon, ellipsoid)
 %   See also PROJDOC, CASSINI_INV, GEODDISTANCE, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/cassini_inv.m b/matlab/geographiclib/cassini_inv.m
index e54f6d9..a914525 100644
--- a/matlab/geographiclib/cassini_inv.m
+++ b/matlab/geographiclib/cassini_inv.m
@@ -24,8 +24,6 @@ function [lat, lon, azi, rk] = cassini_inv(lat0, lon0, x, y, ellipsoid)
 %   See also PROJDOC, CASSINI_FWD, GEODRECKON, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/eqdazim_fwd.m b/matlab/geographiclib/eqdazim_fwd.m
index 90c1233..a67bd75 100644
--- a/matlab/geographiclib/eqdazim_fwd.m
+++ b/matlab/geographiclib/eqdazim_fwd.m
@@ -35,8 +35,6 @@ function [x, y, azi, rk] = eqdazim_fwd(lat0, lon0, lat, lon, ellipsoid)
 %   See also PROJDOC, EQDAZIM_INV, GEODDISTANCE, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/eqdazim_inv.m b/matlab/geographiclib/eqdazim_inv.m
index 0485f1f..6e7579e 100644
--- a/matlab/geographiclib/eqdazim_inv.m
+++ b/matlab/geographiclib/eqdazim_inv.m
@@ -35,8 +35,6 @@ function [lat, lon, azi, rk] = eqdazim_inv(lat0, lon0, x, y, ellipsoid)
 %   See also PROJDOC, EQDAZIM_FWD, GEODRECKON, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/gedistance.m b/matlab/geographiclib/gedistance.m
index dbad171..eafd043 100644
--- a/matlab/geographiclib/gedistance.m
+++ b/matlab/geographiclib/gedistance.m
@@ -27,8 +27,6 @@ function [s12, azi1, azi2, S12] = gedistance(lat1, lon1, lat2, lon2, ellipsoid)
 %   See also GEDOC, GERECKON, DEFAULTELLIPSOID, GEODDISTANCE, GEODRECKON.
 
 % Copyright (c) Charles Karney (2014-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
@@ -80,6 +78,7 @@ function [s12, azi1, azi2, S12] = gedistance(lat1, lon1, lat2, lon2, ellipsoid)
   cgam0 = hypot(cgam1, sgam1 .* sbet1);
 
   ssig1 = sbet1; csig1 = cbet1 .* cgam1;
+  csig1(ssig1 == 0 & csig1 == 0) = 1;
   [ssig1, csig1] = norm2(ssig1, csig1);
   ssig2 = ssig1 .* csig12 + csig1 .* ssig12;
   csig2 = csig1 .* csig12 - ssig1 .* ssig12;
diff --git a/matlab/geographiclib/gedoc.m b/matlab/geographiclib/gedoc.m
index 8e1b1ee..78cb600 100644
--- a/matlab/geographiclib/gedoc.m
+++ b/matlab/geographiclib/gedoc.m
@@ -115,8 +115,6 @@ function gedoc
 %     GEODDISTANCE, GEODRECKON.
 
 % Copyright (c) Charles Karney (2014-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   help gedoc
 end
diff --git a/matlab/geographiclib/geocent_fwd.m b/matlab/geographiclib/geocent_fwd.m
index 8cfe11f..e585abb 100644
--- a/matlab/geographiclib/geocent_fwd.m
+++ b/matlab/geographiclib/geocent_fwd.m
@@ -21,8 +21,6 @@ function [X, Y, Z, M] = geocent_fwd(lat, lon, h, ellipsoid)
 %   See also GEOCENT_INV, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(2, 4)
   if nargin < 3, h = 0; end
diff --git a/matlab/geographiclib/geocent_inv.m b/matlab/geographiclib/geocent_inv.m
index df7c21b..2208e50 100644
--- a/matlab/geographiclib/geocent_inv.m
+++ b/matlab/geographiclib/geocent_inv.m
@@ -19,8 +19,6 @@ function [lat, lon, h, M] = geocent_inv(X, Y, Z, ellipsoid)
 %   See also GEOCENT_FWD, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(3, 4)
   if nargin < 4, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/geodarea.m b/matlab/geographiclib/geodarea.m
index 7c96ee0..4d53559 100644
--- a/matlab/geographiclib/geodarea.m
+++ b/matlab/geographiclib/geodarea.m
@@ -34,8 +34,6 @@ function [A, P, N] = geodarea(lats, lons, ellipsoid)
 %     DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   narginchk(2, 3)
   if nargin < 3, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/geoddistance.m b/matlab/geographiclib/geoddistance.m
index 55142b5..2a5ac48 100644
--- a/matlab/geographiclib/geoddistance.m
+++ b/matlab/geographiclib/geoddistance.m
@@ -44,8 +44,6 @@ function [s12, azi1, azi2, S12, m12, M12, M21, a12] = geoddistance ...
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
 %
-% This file was distributed with GeographicLib 1.44.
-%
 % This is a straightforward transcription of the C++ implementation in
 % GeographicLib and the C++ source should be consulted for additional
 % documentation.  This is a vector implementation and the results returned
@@ -105,7 +103,7 @@ function [s12, azi1, azi2, S12, m12, M12, M21, a12] = geoddistance ...
   lon12 = lonsign .* lon12;
   lat1 = AngRound(LatFix(lat1(:)));
   lat2 = AngRound(LatFix(lat2(:)));
-  swapp = 2 * (abs(lat1) >= abs(lat2)) - 1;
+  swapp = 2 * ~(abs(lat1) < abs(lat2)) - 1;
   lonsign(swapp < 0) = - lonsign(swapp < 0);
   [lat1(swapp < 0), lat2(swapp < 0)] = swap(lat1(swapp < 0), lat2(swapp < 0));
 
@@ -142,8 +140,9 @@ function [s12, azi1, azi2, S12, m12, M12, M21, a12] = geoddistance ...
     ssig1(m) = sbet1(m); csig1(m) = calp1(m) .* cbet1(m);
     ssig2(m) = sbet2(m); csig2(m) = calp2(m) .* cbet2(m);
 
-    sig12(m) = atan2(max(csig1(m) .* ssig2(m) - ssig1(m) .* csig2(m), 0), ...
-                     csig1(m) .* csig2(m) + ssig1(m) .* ssig2(m));
+    sig12(m) = ...
+        atan2(0 + max(0, csig1(m) .* ssig2(m) - ssig1(m) .* csig2(m)), ...
+              csig1(m) .* csig2(m) + ssig1(m) .* ssig2(m));
 
     [s12(m), m12(m), ~, M12(m), M21(m)] = ...
         Lengths(n, sig12(m), ...
@@ -189,68 +188,70 @@ function [s12, azi1, azi2, S12, m12, M12, M21, a12] = geoddistance ...
     numit = Z;
     tripn = Z > 0;
     tripb = tripn;
-    gsave = g;
-    for k = 0 : maxit2 - 1
-      if k == 0 && ~any(g), break, end
-      numit(g) = k;
-      [v(g), dv(g), ...
-       salp2(g), calp2(g), sig12(g), ...
-       ssig1(g), csig1(g), ssig2(g), csig2(g), epsi(g), omg12(g)] = ...
-          Lambda12(sbet1(g), cbet1(g), dn1(g), ...
-                   sbet2(g), cbet2(g), dn2(g), ...
-                   salp1(g), calp1(g), f, A3x, C3x);
-      v = v - lam12;
-      g = g & ~(tripb | ~(abs(v) >= ((tripn * 6) + 2) * tol0));
-      if ~any(g), break, end
-
-      c = g & v > 0;
-      if k <= maxit1
-        c = c & calp1 ./ salp1 > calp1b ./ salp1b;
+    if any(g)
+      gsave = g;
+      for k = 0 : maxit2 - 1
+        if k == 0 && ~any(g), break, end
+        numit(g) = k;
+        [v(g), dv(g), ...
+         salp2(g), calp2(g), sig12(g), ...
+         ssig1(g), csig1(g), ssig2(g), csig2(g), epsi(g), omg12(g)] = ...
+            Lambda12(sbet1(g), cbet1(g), dn1(g), ...
+                     sbet2(g), cbet2(g), dn2(g), ...
+                     salp1(g), calp1(g), f, A3x, C3x);
+        v = v - lam12;
+        g = g & ~(tripb | ~(abs(v) >= ((tripn * 6) + 2) * tol0));
+        if ~any(g), break, end
+
+        c = g & v > 0;
+        if k <= maxit1
+          c = c & calp1 ./ salp1 > calp1b ./ salp1b;
+        end
+        salp1b(c) = salp1(c); calp1b(c) = calp1(c);
+
+        c = g & v < 0;
+        if k <= maxit1
+          c = c & calp1 ./ salp1 < calp1a ./ salp1a;
+        end
+        salp1a(c) = salp1(c); calp1a(c) = calp1(c);
+
+        if k == maxit1, tripn(g) = false; end
+        if k < maxit1
+          dalp1 = -v ./ dv;
+          sdalp1 = sin(dalp1); cdalp1 = cos(dalp1);
+          nsalp1 = salp1 .* cdalp1 + calp1 .* sdalp1;
+          calp1(g) = calp1(g) .* cdalp1(g) - salp1(g) .* sdalp1(g);
+          salp1(g) = nsalp1(g);
+          tripn = g & abs(v) <= 16 * tol0;
+          c = g & ~(dv > 0 & nsalp1 > 0 & abs(dalp1) < pi);
+          tripn(c) = false;
+        else
+          c = g;
+        end
+
+        salp1(c) = (salp1a(c) + salp1b(c))/2;
+        calp1(c) = (calp1a(c) + calp1b(c))/2;
+        [salp1(g), calp1(g)] = norm2(salp1(g), calp1(g));
+        tripb(c) = abs(salp1a(c)-salp1(c)) + (calp1a(c)-calp1(c)) < tolb ...
+            |      abs(salp1(c)-salp1b(c)) + (calp1(c)-calp1b(c)) < tolb;
       end
-      salp1b(c) = salp1(c); calp1b(c) = calp1(c);
 
-      c = g & v < 0;
-      if k <= maxit1
-        c = c & calp1 ./ salp1 < calp1a ./ salp1a;
-      end
-      salp1a(c) = salp1(c); calp1a(c) = calp1(c);
-
-      if k == maxit1, tripn(g) = false; end
-      if k < maxit1
-        dalp1 = -v ./ dv;
-        sdalp1 = sin(dalp1); cdalp1 = cos(dalp1);
-        nsalp1 = salp1 .* cdalp1 + calp1 .* sdalp1;
-        calp1(g) = calp1(g) .* cdalp1(g) - salp1(g) .* sdalp1(g);
-        salp1(g) = nsalp1(g);
-        tripn = g & abs(v) <= 16 * tol0;
-        c = g & ~(dv > 0 & nsalp1 > 0 & abs(dalp1) < pi);
-        tripn(c) = false;
-      else
-        c = g;
+      g = gsave;
+      if bitand(2+4, lengthmask)
+        % set distance bit if redp or scalp, so that J12 is computed in a
+        % canonical way.
+        lengthmask = bitor(1, lengthmask);
       end
 
-      salp1(c) = (salp1a(c) + salp1b(c))/2;
-      calp1(c) = (calp1a(c) + calp1b(c))/2;
-      [salp1(g), calp1(g)] = norm2(salp1(g), calp1(g));
-      tripb(c) = abs(salp1a(c) - salp1(c)) + (calp1a(c) - calp1(c)) < tolb | ...
-          abs(salp1(c) - salp1b(c)) + (calp1(c) - calp1b(c)) < tolb;
-    end
+      [s12(g), m12(g), ~, M12(g), M21(g)] = ...
+          Lengths(epsi(g), sig12(g), ...
+                  ssig1(g), csig1(g), dn1(g), ssig2(g), csig2(g), dn2(g), ...
+                  cbet1(g), cbet2(g), lengthmask, ep2);
 
-    g = gsave;
-    if bitand(2+4, lengthmask)
-      % set distance bit if redp or scalp, so that J12 is computed in a
-      % canonical way.
-      lengthmask = bitor(1, lengthmask);
+      m12(g) = m12(g) * b;
+      s12(g) = s12(g) * b;
+      omg12(g) = lam12(g) - omg12(g);
     end
-
-    [s12(g), m12(g), ~, M12(g), M21(g)] = ...
-        Lengths(epsi(g), sig12(g), ...
-                ssig1(g), csig1(g), dn1(g), ssig2(g), csig2(g), dn2(g), ...
-                cbet1(g), cbet2(g), lengthmask, ep2);
-
-    m12(g) = m12(g) * b;
-    s12(g) = s12(g) * b;
-    omg12(g) = lam12(g) - omg12(g);
   end
 
   s12 = 0 + s12;
@@ -347,8 +348,8 @@ function [sig12, salp1, calp1, salp2, calp2, dnm] = ...
 
   salp1 = cbet2 .* somg12;
   t = cbet2 .* sbet1 .* somg12.^2;
-  calp1 = cvmgt(sbet12  + t ./ (1 + comg12), ...
-                sbet12a - t ./ (1 - comg12), ...
+  calp1 = cvmgt(sbet12  + t ./ max(1, 1 + comg12), ...
+                sbet12a - t ./ max(1, 1 - comg12), ...
                 comg12 >= 0);
 
   ssig12 = hypot(salp1, calp1);
@@ -380,11 +381,14 @@ function [sig12, salp1, calp1, salp2, calp2, dnm] = ...
                   sbet1(s), -cbet1(s), dn1(s), sbet2(s), cbet2(s), dn2(s), ...
                   cbet1(s), cbet2(s), 2);
       x = -1 + m12b ./ (cbet1(s) .* cbet2(s) .* m0 * pi);
-      betscale = cvmgt(sbet12a(s) ./ x, - f * cbet1(s).^2 * pi, x < -0.01);
-      lamscale = betscale ./ cbet1(s);
+      betscale = cvmgt(sbet12a(s) ./ min(-0.01, x), - f * cbet1(s).^2 * pi, ...
+                       x < -0.01);
+     lamscale = betscale ./ cbet1(s);
       y = (lam12(s) - pi) ./ lamscale;
     end
     k = Astroid(x, y);
+    str = y > -tol1 & x > -1 - xthresh;
+    k(str) = 1;
     if f >= 0
       omg12a = -x .* k ./ (1 + k);
     else
@@ -395,7 +399,6 @@ function [sig12, salp1, calp1, salp2, calp2, dnm] = ...
     salp1(s) = cbet2(s) .* somg12;
     calp1(s) = sbet12a(s) - cbet2(s) .* sbet1(s) .* somg12.^2 ./ (1 - comg12);
 
-    str = y > -tol1 & x > -1 - xthresh;
     if any(str)
       salp1s = salp1(s); calp1s = calp1(s);
       if f >= 0
@@ -480,10 +483,10 @@ function [lam12, dlam12, ...
   csig2 = calp2 .* cbet2;  comg2 = csig2;
   [ssig2, csig2] = norm2(ssig2, csig2);
 
-  sig12 = atan2(max(csig1 .* ssig2 - ssig1 .* csig2, 0), ...
+  sig12 = atan2(0 + max(0, csig1 .* ssig2 - ssig1 .* csig2), ...
                 csig1 .* csig2 + ssig1 .* ssig2);
 
-  omg12 = atan2(max(comg1 .* somg2 - somg1 .* comg2, 0), ...
+  omg12 = atan2(0 + max(0, comg1 .* somg2 - somg1 .* comg2), ...
                 comg1 .* comg2 + somg1 .* somg2);
   k2 = calp0.^2 * ep2;
   epsi = k2 ./ (2 * (1 + sqrt(1 + k2)) + k2);
@@ -497,8 +500,8 @@ function [lam12, dlam12, ...
   [~, dlam12] = ...
       Lengths(epsi, sig12, ...
               ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2, 2);
-  dlam12 = dlam12 .* f1 ./ (calp2 .* cbet2);
   z = calp2 == 0;
+  dlam12(~z) = dlam12(~z) .* f1 ./ (calp2(~z) .* cbet2(~z));
   dlam12(z) = - 2 * f1 .* dn1(z) ./ sbet1(z);
 end
 
diff --git a/matlab/geographiclib/geoddoc.m b/matlab/geographiclib/geoddoc.m
index a04e801..182dd8c 100644
--- a/matlab/geographiclib/geoddoc.m
+++ b/matlab/geographiclib/geoddoc.m
@@ -82,7 +82,7 @@ function geoddoc
 %     * s13 = s12 + s23
 %     * a13 = a12 + a23
 %     * S13 = S12 + S23
-%     * m13 = m12*m23 + m23*m21
+%     * m13 = m12*M23 + m23*M21
 %     * M13 = M12*M23 - (1 - M12*M21) * m23/m12
 %     * M31 = M32*M21 - (1 - M23*M32) * m12/m23
 %
@@ -94,7 +94,7 @@ function geoddoc
 %       curves on the surface.
 %     * Similarly, the spherical arc length, a12, is unrestricted.
 %     * The equatorial radius, a, must be positive.
-%     * The eccentricity, e, should be satisfy abs(e) < 0.2 in order to
+%     * The eccentricity, e, should satisfy abs(e) < 0.2 in order to
 %       retain full accuracy (this corresponds to flattenings satisfying
 %       abs(f) <= 1/50, approximately).  This condition holds for most
 %       applications in geodesy.
@@ -167,8 +167,6 @@ function geoddoc
 %     GEODESICDIRECT, GEODESICLINE, GEODESICINVERSE, POLYGONAREA.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   help geoddoc
 end
diff --git a/matlab/geographiclib/geodreckon.m b/matlab/geographiclib/geodreckon.m
index 62c757b..b196dbd 100644
--- a/matlab/geographiclib/geodreckon.m
+++ b/matlab/geographiclib/geodreckon.m
@@ -69,8 +69,6 @@ function [lat2, lon2, azi2, S12, m12, M12, M21, a12_s12] = geodreckon ...
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
 %
-% This file was distributed with GeographicLib 1.44.
-%
 % This is a straightforward transcription of the C++ implementation in
 % GeographicLib and the C++ source should be consulted for additional
 % documentation.  This is a vector implementation and the results returned
diff --git a/matlab/geographiclib/geographiclib_test.m b/matlab/geographiclib/geographiclib_test.m
new file mode 100644
index 0000000..c975738
--- /dev/null
+++ b/matlab/geographiclib/geographiclib_test.m
@@ -0,0 +1,554 @@
+function geographiclib_test
+%GEOGRAPHICLIB_TEST   The test suite for the geographiclib package
+%
+%   GEOGRAPHICLIB_TEST
+%
+%   runs a variety of tests and produces no output it they are successful.
+
+  n = 0;
+  i = testrand; if i, n=n+1; fprintf('testrand fail: %d\n', i); end
+  i = GeodSolve0; if i, n=n+1; fprintf('GeodSolve0 fail: %d\n', i); end
+  i = GeodSolve1; if i, n=n+1; fprintf('GeodSolve1 fail: %d\n', i); end
+  i = GeodSolve2; if i, n=n+1; fprintf('GeodSolve2 fail: %d\n', i); end
+  i = GeodSolve4; if i, n=n+1; fprintf('GeodSolve4 fail: %d\n', i); end
+  i = GeodSolve5; if i, n=n+1; fprintf('GeodSolve5 fail: %d\n', i); end
+  i = GeodSolve6; if i, n=n+1; fprintf('GeodSolve6 fail: %d\n', i); end
+  i = GeodSolve9; if i, n=n+1; fprintf('GeodSolve9 fail: %d\n', i); end
+  i = GeodSolve10; if i, n=n+1; fprintf('GeodSolve10 fail: %d\n', i); end
+  i = GeodSolve11; if i, n=n+1; fprintf('GeodSolve11 fail: %d\n', i); end
+  i = GeodSolve12; if i, n=n+1; fprintf('GeodSolve12 fail: %d\n', i); end
+  i = GeodSolve14; if i, n=n+1; fprintf('GeodSolve14 fail: %d\n', i); end
+  i = GeodSolve15; if i, n=n+1; fprintf('GeodSolve15 fail: %d\n', i); end
+  i = GeodSolve17; if i, n=n+1; fprintf('GeodSolve17 fail: %d\n', i); end
+  i = GeodSolve26; if i, n=n+1; fprintf('GeodSolve26 fail: %d\n', i); end
+  i = GeodSolve28; if i, n=n+1; fprintf('GeodSolve28 fail: %d\n', i); end
+  i = GeodSolve33; if i, n=n+1; fprintf('GeodSolve33 fail: %d\n', i); end
+  i = GeodSolve55; if i, n=n+1; fprintf('GeodSolve55 fail: %d\n', i); end
+  i = Planimeter0; if i, n=n+1; fprintf('Planimeter0 fail: %d\n', i); end
+  i = Planimeter5; if i, n=n+1; fprintf('Planimeter5 fail: %d\n', i); end
+  i = Planimeter6; if i, n=n+1; fprintf('Planimeter6 fail: %d\n', i); end
+  i = Planimeter12; if i, n=n+1; fprintf('Planimeter12 fail: %d\n', i); end
+  i = Planimeter13; if i, n=n+1; fprintf('Planimeter13 fail: %d\n', i); end
+  i = gedistance0; if i, n=n+1; fprintf('gedistance0 fail: %d\n', i); end
+  i = tranmerc0; if i, n=n+1; fprintf('tranmerc0 fail: %d\n', i); end
+  i = mgrs0; if i, n=n+1; fprintf('mgrs0 fail: %d\n', i); end
+  i = mgrs1; if i, n=n+1; fprintf('mgrs1 fail: %d\n', i); end
+  i = mgrs2; if i, n=n+1; fprintf('mgrs2 fail: %d\n', i); end
+  i = mgrs3; if i, n=n+1; fprintf('mgrs3 fail: %d\n', i); end
+  i = mgrs4; if i, n=n+1; fprintf('mgrs4 fail: %d\n', i); end
+  assert(n == 0);
+end
+
+function n = assertEquals(x, y, d)
+  n = abs(x - y) <= d;
+  n = sum(~n(:));
+end
+
+function n = assertNaN(x)
+  n = isnan(x);
+  n = sum(~n(:));
+end
+
+function n = testrand
+  n = 0;
+  testcases = [
+      35.60777, -139.44815, 111.098748429560326, ...
+      -11.17491, -69.95921, 129.289270889708762, ...
+      8935244.5604818305, 80.50729714281974, 6273170.2055303837, ...
+      0.16606318447386067, 0.16479116945612937, 12841384694976.432;
+      55.52454, 106.05087, 22.020059880982801, ...
+      77.03196, 197.18234, 109.112041110671519, ...
+      4105086.1713924406, 36.892740690445894, 3828869.3344387607, ...
+      0.80076349608092607, 0.80101006984201008, 61674961290615.615;
+      -21.97856, 142.59065, -32.44456876433189, ...
+      41.84138, 98.56635, -41.84359951440466, ...
+      8394328.894657671, 75.62930491011522, 6161154.5773110616, ...
+      0.24816339233950381, 0.24930251203627892, -6637997720646.717;
+      -66.99028, 112.2363, 173.73491240878403, ...
+      -12.70631, 285.90344, 2.512956620913668, ...
+      11150344.2312080241, 100.278634181155759, 6289939.5670446687, ...
+      -0.17199490274700385, -0.17722569526345708, -121287239862139.744;
+      -17.42761, 173.34268, -159.033557661192928, ...
+      -15.84784, 5.93557, -20.787484651536988, ...
+      16076603.1631180673, 144.640108810286253, 3732902.1583877189, ...
+      -0.81273638700070476, -0.81299800519154474, 97825992354058.708;
+      32.84994, 48.28919, 150.492927788121982, ...
+      -56.28556, 202.29132, 48.113449399816759, ...
+      16727068.9438164461, 150.565799985466607, 3147838.1910180939, ...
+      -0.87334918086923126, -0.86505036767110637, -72445258525585.010;
+      6.96833, 52.74123, 92.581585386317712, ...
+      -7.39675, 206.17291, 90.721692165923907, ...
+      17102477.2496958388, 154.147366239113561, 2772035.6169917581, ...
+      -0.89991282520302447, -0.89986892177110739, -1311796973197.995;
+      -50.56724, -16.30485, -105.439679907590164, ...
+      -33.56571, -94.97412, -47.348547835650331, ...
+      6455670.5118668696, 58.083719495371259, 5409150.7979815838, ...
+      0.53053508035997263, 0.52988722644436602, 41071447902810.047;
+      -58.93002, -8.90775, 140.965397902500679, ...
+      -8.91104, 133.13503, 19.255429433416599, ...
+      11756066.0219864627, 105.755691241406877, 6151101.2270708536, ...
+      -0.26548622269867183, -0.27068483874510741, -86143460552774.735;
+      -68.82867, -74.28391, 93.774347763114881, ...
+      -50.63005, -8.36685, 34.65564085411343, ...
+      3956936.926063544, 35.572254987389284, 3708890.9544062657, ...
+      0.81443963736383502, 0.81420859815358342, -41845309450093.787;
+      -10.62672, -32.0898, -86.426713286747751, ...
+      5.883, -134.31681, -80.473780971034875, ...
+      11470869.3864563009, 103.387395634504061, 6184411.6622659713, ...
+      -0.23138683500430237, -0.23155097622286792, 4198803992123.548;
+      -21.76221, 166.90563, 29.319421206936428, ...
+      48.72884, 213.97627, 43.508671946410168, ...
+      9098627.3986554915, 81.963476716121964, 6299240.9166992283, ...
+      0.13965943368590333, 0.14152969707656796, 10024709850277.476;
+      -19.79938, -174.47484, 71.167275780171533, ...
+      -11.99349, -154.35109, 65.589099775199228, ...
+      2319004.8601169389, 20.896611684802389, 2267960.8703918325, ...
+      0.93427001867125849, 0.93424887135032789, -3935477535005.785;
+      -11.95887, -116.94513, 92.712619830452549, ...
+      4.57352, 7.16501, 78.64960934409585, ...
+      13834722.5801401374, 124.688684161089762, 5228093.177931598, ...
+      -0.56879356755666463, -0.56918731952397221, -9919582785894.853;
+      -87.85331, 85.66836, -65.120313040242748, ...
+      66.48646, 16.09921, -4.888658719272296, ...
+      17286615.3147144645, 155.58592449699137, 2635887.4729110181, ...
+      -0.90697975771398578, -0.91095608883042767, 42667211366919.534;
+      1.74708, 128.32011, -101.584843631173858, ...
+      -11.16617, 11.87109, -86.325793296437476, ...
+      12942901.1241347408, 116.650512484301857, 5682744.8413270572, ...
+      -0.44857868222697644, -0.44824490340007729, 10763055294345.653;
+      -25.72959, -144.90758, -153.647468693117198, ...
+      -57.70581, -269.17879, -48.343983158876487, ...
+      9413446.7452453107, 84.664533838404295, 6356176.6898881281, ...
+      0.09492245755254703, 0.09737058264766572, 74515122850712.444;
+      -41.22777, 122.32875, 14.285113402275739, ...
+      -7.57291, 130.37946, 10.805303085187369, ...
+      3812686.035106021, 34.34330804743883, 3588703.8812128856, ...
+      0.82605222593217889, 0.82572158200920196, -2456961531057.857;
+      11.01307, 138.25278, 79.43682622782374, ...
+      6.62726, 247.05981, 103.708090215522657, ...
+      11911190.819018408, 107.341669954114577, 6070904.722786735, ...
+      -0.29767608923657404, -0.29785143390252321, 17121631423099.696;
+      -29.47124, 95.14681, -163.779130441688382, ...
+      -27.46601, -69.15955, -15.909335945554969, ...
+      13487015.8381145492, 121.294026715742277, 5481428.9945736388, ...
+      -0.51527225545373252, -0.51556587964721788, 104679964020340.318];
+
+  lat1 = testcases(:,1); lon1 = testcases(:,2); azi1 = testcases(:,3);
+  lat2 = testcases(:,4); lon2 = testcases(:,5); azi2 = testcases(:,6);
+  s12 = testcases(:,7); a12 = testcases(:,8); m12 = testcases(:,9);
+  M12 = testcases(:,10); M21 = testcases(:,11); S12 = testcases(:,12);
+  [s12a, azi1a, azi2a, S12a, m12a, M12a, M21a, a12a] = ...
+      geoddistance(lat1, lon1, lat2, lon2);
+  n = n + assertEquals(azi1, azi1a, 1e-13);
+  n = n + assertEquals(azi2, azi2a, 1e-13);
+  n = n + assertEquals(s12, s12a, 1e-8);
+  n = n + assertEquals(a12, a12a, 1e-13);
+  n = n + assertEquals(m12, m12a, 1e-8);
+  n = n + assertEquals(M12, M12a, 1e-15);
+  n = n + assertEquals(M21, M21a, 1e-15);
+  n = n + assertEquals(S12, S12a, 0.1);
+
+  [lat2a, lon2a, azi2a, S12a, m12a, M12a, M21a, a12a] = ...
+      geodreckon(lat1, lon1, s12, azi1, 2);
+  n = n + assertEquals(lat2, lat2a, 1e-13);
+  n = n + assertEquals(lon2, lon2a, 1e-13);
+  n = n + assertEquals(azi2, azi2a, 1e-13);
+  n = n + assertEquals(a12, a12a, 1e-13);
+  n = n + assertEquals(m12, m12a, 1e-8);
+  n = n + assertEquals(M12, M12a, 1e-15);
+  n = n + assertEquals(M21, M21a, 1e-15);
+  n = n + assertEquals(S12, S12a, 0.1);
+
+  [lat2a, lon2a, azi2a, S12a, m12a, M12a, M21a, s12a] = ...
+      geodreckon(lat1, lon1, a12, azi1, 1+2);
+  n = n + assertEquals(lat2, lat2a, 1e-13);
+  n = n + assertEquals(lon2, lon2a, 1e-13);
+  n = n + assertEquals(azi2, azi2a, 1e-13);
+  n = n + assertEquals(s12, s12a, 1e-8);
+  n = n + assertEquals(m12, m12a, 1e-8);
+  n = n + assertEquals(M12, M12a, 1e-15);
+  n = n + assertEquals(M21, M21a, 1e-15);
+  n = n + assertEquals(S12, S12a, 0.1);
+end
+
+function ell = ellipsoid(a, f)
+  ell = [a, flat2ecc(f)];
+end
+
+function n = GeodSolve0
+  n = 0;
+  [s12, azi1, azi2] = geoddistance(40.6, -73.8, 49.01666667, 2.55);
+  n = n + assertEquals(azi1, 53.47022, 0.5e-5);
+  n = n + assertEquals(azi2, 111.59367, 0.5e-5);
+  n = n + assertEquals(s12, 5853226, 0.5);
+end
+
+function n = GeodSolve1
+  n = 0;
+  [lat2, lon2, azi2] = geodreckon(40.63972222, -73.77888889, 5850e3, 53.5);
+  n = n + assertEquals(lat2, 49.01467, 0.5e-5);
+  n = n + assertEquals(lon2, 2.56106, 0.5e-5);
+  n = n + assertEquals(azi2, 111.62947, 0.5e-5);
+end
+
+function n = GeodSolve2
+% Check fix for antipodal prolate bug found 2010-09-04
+  n = 0;
+  ell = ellipsoid(6.4e6, -1/150.0);
+  [s12, azi1, azi2] = geoddistance(0.07476, 0, -0.07476, 180, ell);
+  n = n + assertEquals(azi1, 90.00078, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00078, 0.5e-5);
+  n = n + assertEquals(s12, 20106193, 0.5);
+  [s12, azi1, azi2] = geoddistance(0.1, 0, -0.1, 180, ell);
+  n = n + assertEquals(azi1, 90.00105, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00105, 0.5e-5);
+  n = n + assertEquals(s12, 20106193, 0.5);
+end
+
+function n = GeodSolve4
+% Check fix for short line bug found 2010-05-21
+% This also checks the MATLAB specific bug:
+% Ensure that Lengths in geoddistance is not invoked with zero-length
+% vectors, 2015-08-25.
+  n = 0;
+  s12 = geoddistance(36.493349428792, 0, 36.49334942879201, .0000008);
+  n = n + assertEquals(s12, 0.072, 0.5e-3);
+end
+
+function n = GeodSolve5
+% Check fix for point2=pole bug found 2010-05-03
+  n = 0;
+  [lat2, lon2, azi2] = geodreckon(0.01777745589997, 30, 10e6, 0);
+  n = n + assertEquals(lat2, 90, 0.5e-5);
+  if lon2 < 0
+    n = n + assertEquals(lon2, -150, 0.5e-5);
+    n = n + assertEquals(azi2, -180, 0.5e-5);
+  else
+    n = n + assertEquals(lon2, 30, 0.5e-5);
+    n = n + assertEquals(azi2, 0, 0.5e-5);
+  end
+end
+
+function n = GeodSolve6
+% Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
+% x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1).
+  n = 0;
+  s12 = geoddistance(88.202499451857, 0, ...
+                     -88.202499451857, 179.981022032992859592);
+  n = n + assertEquals(s12, 20003898.214, 0.5e-3);
+  s12 = geoddistance(89.262080389218, 0, ...
+                     -89.262080389218, 179.992207982775375662);
+  n = n + assertEquals(s12, 20003925.854, 0.5e-3);
+  s12 = geoddistance(89.333123580033, 0, ...
+                     -89.333123580032997687, 179.99295812360148422);
+  n = n + assertEquals(s12, 20003926.881, 0.5e-3);
+end
+
+function n = GeodSolve9
+% Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3)
+  n = 0;
+  s12 = geoddistance(56.320923501171, 0, ...
+                     -56.320923501171, 179.664747671772880215);
+  n = n + assertEquals(s12, 19993558.287, 0.5e-3);
+end
+
+function n = GeodSolve10
+% Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
+% 10 rel + debug)
+  n = 0;
+  s12 = geoddistance(52.784459512564, 0, ...
+                     -52.784459512563990912, 179.634407464943777557);
+  n = n + assertEquals(s12, 19991596.095, 0.5e-3);
+end
+
+function n = GeodSolve11
+% Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
+% 10 rel + debug)
+  n = 0;
+  s12 = geoddistance(48.522876735459, 0, ...
+                     -48.52287673545898293, 179.599720456223079643);
+  n = n + assertEquals(s12, 19989144.774, 0.5e-3);
+end
+
+function n = GeodSolve12
+% Check fix for inverse geodesics on extreme prolate/oblate
+% ellipsoids Reported 2012-08-29 Stefan Guenther
+% <stefan.gunther at embl.de>; fixed 2012-10-07
+  n = 0;
+  ell = ellipsoid(89.8, -1.83);
+  [s12, azi1, azi2] = geoddistance(0, 0, -10, 160, ell);
+  n = n + assertEquals(azi1, 120.27, 1e-2);
+  n = n + assertEquals(azi2, 105.15, 1e-2);
+  n = n + assertEquals(s12, 266.7, 1e-1);
+end
+
+function n = GeodSolve14
+% Check fix for inverse ignoring lon12 = nan
+  n = 0;
+  [s12, azi1, azi2] = geoddistance(0, 0, 1, NaN);
+  n = n + assertNaN(azi1);
+  n = n + assertNaN(azi2);
+  n = n + assertNaN(s12);
+end
+
+function n = GeodSolve15
+% Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+% checks that this is fixed.
+  n = 0;
+  ell = ellipsoid(6.4e6, -1/150.0);
+  [~, ~, ~, S12] = geodreckon(1, 2, 4, 3, ell);
+  n = n + assertEquals(S12, 23700, 0.5);
+end
+
+function n = GeodSolve17
+% Check fix for LONG_UNROLL bug found on 2015-05-07
+  n = 0;
+  [lat2, lon2, azi2] = geodreckon(40, -75, 2e7, -10, 2);
+  n = n + assertEquals(lat2, -39, 1);
+  n = n + assertEquals(lon2, -254, 1);
+  n = n + assertEquals(azi2, -170, 1);
+  [lat2, lon2, azi2] = geodreckon(40, -75, 2e7, -10);
+  n = n + assertEquals(lat2, -39, 1);
+  n = n + assertEquals(lon2, 105, 1);
+  n = n + assertEquals(azi2, -170, 1);
+end
+
+function n = GeodSolve26
+% Check 0/0 problem with area calculation on sphere 2015-09-08
+  n = 0;
+  ell = ellipsoid(6.4e6, 0);
+  [~, ~, ~, S12] = geoddistance(1, 2, 3, 4, ell);
+  n = n + assertEquals(S12, 49911046115.0, 0.5);
+end
+
+function n = GeodSolve28
+% Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+% Java implementation fixed on 2015-05-19).
+  n = 0;
+  ell = ellipsoid(6.4e6, 0.1);
+  [~, ~, ~, ~, ~, ~, ~, a12] = geodreckon(1, 2, 5e6, 10, ell);
+  n = n + assertEquals(a12, 48.55570690, 0.5e-8);
+end
+
+function n = GeodSolve33
+% Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+% sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+% fmod(-0.0, 360.0) = +0.0.
+  n = 0;
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 179);
+  n = n + assertEquals(azi1, 90.00000, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00000, 0.5e-5);
+  n = n + assertEquals(s12, 19926189, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 179.5);
+  n = n + assertEquals(azi1, 55.96650, 0.5e-5);
+  n = n + assertEquals(azi2, 124.03350, 0.5e-5);
+  n = n + assertEquals(s12, 19980862, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 180);
+  n = n + assertEquals(azi1, 0.00000, 0.5e-5);
+  n = n + assertEquals(azi2, -180.00000, 0.5e-5);
+  n = n + assertEquals(s12, 20003931, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 1, 180);
+  n = n + assertEquals(azi1, 0.00000, 0.5e-5);
+  n = n + assertEquals(azi2, -180.00000, 0.5e-5);
+  n = n + assertEquals(s12, 19893357, 0.5);
+  ell = ellipsoid(6.4e6, 0);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 179, ell);
+  n = n + assertEquals(azi1, 90.00000, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00000, 0.5e-5);
+  n = n + assertEquals(s12, 19994492, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 180, ell);
+  n = n + assertEquals(azi1, 0.00000, 0.5e-5);
+  n = n + assertEquals(azi2, -180.00000, 0.5e-5);
+  n = n + assertEquals(s12, 20106193, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 1, 180, ell);
+  n = n + assertEquals(azi1, 0.00000, 0.5e-5);
+  n = n + assertEquals(azi2, -180.00000, 0.5e-5);
+  n = n + assertEquals(s12, 19994492, 0.5);
+  ell = ellipsoid(6.4e6, -1/300.0);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 179, ell);
+  n = n + assertEquals(azi1, 90.00000, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00000, 0.5e-5);
+  n = n + assertEquals(s12, 19994492, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0, 180, ell);
+  n = n + assertEquals(azi1, 90.00000, 0.5e-5);
+  n = n + assertEquals(azi2, 90.00000, 0.5e-5);
+  n = n + assertEquals(s12, 20106193, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 0.5, 180, ell);
+  n = n + assertEquals(azi1, 33.02493, 0.5e-5);
+  n = n + assertEquals(azi2, 146.97364, 0.5e-5);
+  n = n + assertEquals(s12, 20082617, 0.5);
+  [s12, azi1, azi2] = geoddistance(0, 0, 1, 180, ell);
+  n = n + assertEquals(azi1, 0.00000, 0.5e-5);
+  n = n + assertEquals(azi2, -180.00000, 0.5e-5);
+  n = n + assertEquals(s12, 20027270, 0.5);
+  % Check also octave-specific versions of this problem.
+  % In 1.44 this returned [-2.0004e+07, -2.0004e+07, 0.0000e+00, 0.0000e+00]
+  s12 = geoddistance(0,0,0,[179.5, 179.6, 180, 180]);
+  n = n + assertEquals(s12, [19980862, 19989165, 20003931, 20003931], 0.5);
+end
+
+function n = GeodSolve55
+% Check fix for nan + point on equator or pole not returning all nans in
+% Geodesic::Inverse, found 2015-09-23.
+  n = 0;
+  [s12, azi1, azi2] = geoddistance(NaN, 0, 0, 90);
+  n = n + assertNaN(azi1);
+  n = n + assertNaN(azi2);
+  n = n + assertNaN(s12);
+  [s12, azi1, azi2] = geoddistance(NaN, 0, 90, 9);
+  n = n + assertNaN(azi1);
+  n = n + assertNaN(azi2);
+  n = n + assertNaN(s12);
+end
+
+function n = Planimeter0
+% Check fix for pole-encircling bug found 2011-03-16
+  n = 0;
+  pa = [89, 0; 89, 90; 89, 180; 89, 270];
+  pb = [-89, 0; -89, 90; -89, 180; -89, 270];
+  pc = [0, -1; -1, 0; 0, 1; 1, 0];
+  pd = [90, 0; 0, 0; 0, 90];
+
+  [area, perimeter] = geodarea(pa(:,1), pa(:,2));
+  n = n + assertEquals(perimeter, 631819.8745, 1e-4);
+  n = n + assertEquals(area, 24952305678.0, 1);
+
+  [area, perimeter] = geodarea(pb(:,1), pb(:,2));
+  n = n + assertEquals(perimeter, 631819.8745, 1e-4);
+  n = n + assertEquals(area, -24952305678.0, 1);
+
+  [area, perimeter] = geodarea(pc(:,1), pc(:,2));
+  n = n + assertEquals(perimeter, 627598.2731, 1e-4);
+  n = n + assertEquals(area, 24619419146.0, 1);
+
+  [area, perimeter] = geodarea(pd(:,1), pd(:,2));
+  n = n + assertEquals(perimeter, 30022685, 1);
+  n = n + assertEquals(area, 63758202715511.0, 1);
+
+end
+
+function n = Planimeter5
+% Check fix for Planimeter pole crossing bug found 2011-06-24
+  n = 0;
+  points = [89, 0.1; 89, 90.1; 89, -179.9];
+  [area, perimeter] = geodarea(points(:,1), points(:,2));
+  n = n + assertEquals(perimeter, 539297, 1);
+  n = n + assertEquals(area, 12476152838.5, 1);
+end
+
+function n = Planimeter6
+% Check fix for Planimeter lon12 rounding bug found 2012-12-03
+  n = 0;
+  pa = [9, -0.00000000000001; 9, 180; 9, 0];
+  pb = [9, 0.00000000000001; 9, 0; 9, 180];
+  pc = [9, 0.00000000000001; 9, 180; 9, 0];
+  pd = [9, -0.00000000000001; 9, 0; 9, 180];
+
+  [area, perimeter] = geodarea(pa(:,1), pa(:,2));
+  n = n + assertEquals(perimeter, 36026861, 1);
+  n = n + assertEquals(area, 0, 1);
+  [area, perimeter] = geodarea(pb(:,1), pb(:,2));
+  n = n + assertEquals(perimeter, 36026861, 1);
+  n = n + assertEquals(area, 0, 1);
+  [area, perimeter] = geodarea(pc(:,1), pc(:,2));
+  n = n + assertEquals(perimeter, 36026861, 1);
+  n = n + assertEquals(area, 0, 1);
+  [area, perimeter] = geodarea(pd(:,1), pd(:,2));
+  n = n + assertEquals(perimeter, 36026861, 1);
+  n = n + assertEquals(area, 0, 1);
+end
+
+function n = Planimeter12
+% Area of arctic circle (not really -- adjunct to rhumb-area test)
+  n = 0;
+  points = [66.562222222, 0; 66.562222222, 180];
+  [area, perimeter] = geodarea(points(:,1), points(:,2));
+  n = n + assertEquals(perimeter, 10465729, 1);
+  n = n + assertEquals(area, 0, 1);
+end
+
+function n = Planimeter13
+% Check encircling pole twice
+  n = 0;
+  points = [89,-360; 89,-240; 89,-120; 89,0; 89,120; 89,240];
+  [area, perimeter] = geodarea(points(:,1), points(:,2));
+  n = n + assertEquals(perimeter, 1160741, 1);
+  n = n + assertEquals(area, 32415230256.0, 1);
+end
+
+function n = gedistance0
+% gedistance(0, 0, 0, 100) wrongly return nan; 2015-09-23
+  n = 0;
+  s12 = gedistance(0, 0, 0, 100);
+  n = n + assertEquals(s12, 11131949, 0.5);
+end
+
+function n = tranmerc0
+% In 1.44, tranmerc_{fwd,inv} didn't work with array arguments.
+  n = 0;
+  % This used to result in an error
+  [x, y, gam, k] = tranmerc_fwd(0, 0, [90,90;85,85], [10,20;10,20]);
+  k0 = 0.9996;
+  n = n + assertEquals(x, [0, 0; 96820.412637, 190740.935334]/k0, 0.5e-6);
+  n = n + assertEquals(y, [9997964.943021, 9997964.943021;  ...
+                      9448171.516284, 9473242.646190]/k0, 0.5e-6);
+  n = n + assertEquals(gam, [10, 20; ...
+                      9.962710901776, 19.929896900550], 0.5e-12);
+  n = n + assertEquals(k, [0.9996, 0.9996; ...
+                      0.999714504947, 1.000044424775]/k0, 0.5e-12);
+  % This used to result in NaNs
+  [x, y, gam, k] = tranmerc_fwd(0, 0, [90,90;85,85]', [10,20;10,20]');
+  k0 = 0.9996;
+  n = n + assertEquals(x, [0, 0; 96820.412637, 190740.935334]'/k0, 0.5e-6);
+  n = n + assertEquals(y, [9997964.943021, 9997964.943021;  ...
+                      9448171.516284, 9473242.646190]'/k0, 0.5e-6);
+  n = n + assertEquals(gam, [10, 20; ...
+                      9.962710901776, 19.929896900550]', 0.5e-12);
+  n = n + assertEquals(k, [0.9996, 0.9996; ...
+                      0.999714504947, 1.000044424775]'/k0, 0.5e-12);
+end
+
+function n = mgrs0
+% In 1.43, mgrs_inv didn't detect illegal letter combinations.
+  n = 0;
+  % This used to result in finite x, y
+  [x, y, zone, northp] = mgrs_inv('38RMB');
+  n = n + assertNaN(x) + assertNaN(y);
+  n = n + assertEquals(zone, -4, 0);
+  n = n + assertEquals(northp, false, 0);
+end
+
+function n = mgrs1
+% In 1.44, mgrs_fwd gives the wrong results with prec = 10 or 11 in octave
+  n = 0;
+  % This used to result in '38SMB-1539607552-1539607552'
+  mgrs = mgrs_fwd(450000, 3650000, 38, 1, 11);
+  n = n + assertEquals(mgrs{1}, '38SMB5000000000050000000000', 0);
+end
+
+function n = mgrs2
+% In 1.43, mgrs_inv doesn't decode prec 11 string correctly
+  n = 0;
+  % This used to result in x = y = NaN
+  [x, y, zone, northp] = mgrs_inv('38SMB5000000000050000000000');
+  n = n + assertEquals(x, 450000.0000005, 0.5e-6);
+  n = n + assertEquals(y, 3650000.0000005, 0.5e-6);
+  n = n + assertEquals(zone, 38, 0);
+  n = n + assertEquals(northp, true, 0);
+end
+
+function n = mgrs3
+% GeoConvert16: Check MGRS::Forward improved rounding fix, 2015-07-22
+  n = 0;
+  mgrs = mgrs_fwd(444140.6, 3684706.3, 38, 1, 8);
+  n = n + assertEquals(mgrs{1}, '38SMB4414060084706300', 0);
+end
+
+function n = mgrs4
+% GeoConvert17: Check MGRS::Forward digit consistency fix, 2015-07-23
+  n = 0;
+  mgrs = mgrs_fwd(500000, 63.811, 38, 1, 8);
+  n = n + assertEquals(mgrs{1}, '38NNF0000000000063811', 0);
+  mgrs = mgrs_fwd(500000, 63.811, 38, 1, 9);
+  n = n + assertEquals(mgrs{1}, '38NNF000000000000638110', 0);
+end
diff --git a/matlab/geographiclib/geoid_height.m b/matlab/geographiclib/geoid_height.m
index 814f710..d495b40 100644
--- a/matlab/geographiclib/geoid_height.m
+++ b/matlab/geographiclib/geoid_height.m
@@ -60,8 +60,6 @@ function N = geoid_height(lat, lon, geoidname, geoiddir)
 %   See also GEOID_LOAD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   persistent saved_geoid
   if nargin == 1 && isempty(lat)
diff --git a/matlab/geographiclib/geoid_load.m b/matlab/geographiclib/geoid_load.m
index a8eb6a8..7c05622 100644
--- a/matlab/geographiclib/geoid_load.m
+++ b/matlab/geographiclib/geoid_load.m
@@ -53,8 +53,6 @@ function geoid = geoid_load(name, dir)
 %   See also GEOID_HEIGHT.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   if nargin < 1
     file = geoid_file;
diff --git a/matlab/geographiclib/gereckon.m b/matlab/geographiclib/gereckon.m
index c2913ce..554c663 100644
--- a/matlab/geographiclib/gereckon.m
+++ b/matlab/geographiclib/gereckon.m
@@ -31,8 +31,6 @@ function [lat2, lon2, azi2, S12] = gereckon(lat1, lon1, s12, azi1, ellipsoid)
 %   See also GEDOC, GEDISTANCE, DEFAULTELLIPSOID, GEODDISTANCE, GEODRECKON.
 
 % Copyright (c) Charles Karney (2014-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/gnomonic_fwd.m b/matlab/geographiclib/gnomonic_fwd.m
index 3690be0..9bcee75 100644
--- a/matlab/geographiclib/gnomonic_fwd.m
+++ b/matlab/geographiclib/gnomonic_fwd.m
@@ -43,8 +43,6 @@ function [x, y, azi, rk] = gnomonic_fwd(lat0, lon0, lat, lon, ellipsoid)
 %   See also PROJDOC, GNOMONIC_INV, GEODDISTANCE, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/gnomonic_inv.m b/matlab/geographiclib/gnomonic_inv.m
index 3a5edd1..11cff3a 100644
--- a/matlab/geographiclib/gnomonic_inv.m
+++ b/matlab/geographiclib/gnomonic_inv.m
@@ -44,8 +44,6 @@ function [lat, lon, azi, rk] = gnomonic_inv(lat0, lon0, x, y, ellipsoid)
 %   See also PROJDOC, GNOMONIC_FWD, GEODRECKON, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/loccart_fwd.m b/matlab/geographiclib/loccart_fwd.m
index adcc519..3082f5e 100644
--- a/matlab/geographiclib/loccart_fwd.m
+++ b/matlab/geographiclib/loccart_fwd.m
@@ -23,8 +23,6 @@ function [x, y, z, M] = loccart_fwd(lat0, lon0, h0, lat, lon, h, ellipsoid)
 %   See also LOCCART_INV, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(5, 7)
   if nargin < 6, h = 0; end
diff --git a/matlab/geographiclib/loccart_inv.m b/matlab/geographiclib/loccart_inv.m
index a88ec5d..c5e56b1 100644
--- a/matlab/geographiclib/loccart_inv.m
+++ b/matlab/geographiclib/loccart_inv.m
@@ -22,8 +22,6 @@ function [lat, lon, h, M] = loccart_inv(lat0, lon0, h0, x, y, z, ellipsoid)
 %   See also LOCCART_FWD, DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(6, 7)
   if nargin < 7, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/mgrs_fwd.m b/matlab/geographiclib/mgrs_fwd.m
index 56f1dee..aa764a8 100644
--- a/matlab/geographiclib/mgrs_fwd.m
+++ b/matlab/geographiclib/mgrs_fwd.m
@@ -31,8 +31,6 @@ function mgrs = mgrs_fwd(x, y, zone, isnorth, prec)
 %   See also MGRS_INV, UTMUPS_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(4, 5)
   if nargin < 5, prec = 5; end
@@ -165,8 +163,8 @@ function xy = formatnum(x, xh, y, yh, prec)
   x = x - xh * 1e11; y = y - yh * 1e11;
   d = 10 ^ (11 - prec);
   x = floor(x / d); y = floor(y / d);
-  xy = [num2str(x, ['%0', int2str(prec), 'd']), ...
-        num2str(y, ['%0', int2str(prec), 'd'])];
+  xy = [num2str(x, ['%0', int2str(prec), '.0f']), ...
+        num2str(y, ['%0', int2str(prec), '.0f'])];
 end
 
 function band = LatitudeBand(lat)
diff --git a/matlab/geographiclib/mgrs_inv.m b/matlab/geographiclib/mgrs_inv.m
index eb1c7d7..8ef75b6 100644
--- a/matlab/geographiclib/mgrs_inv.m
+++ b/matlab/geographiclib/mgrs_inv.m
@@ -23,8 +23,6 @@ function [x, y, zone, isnorth, prec] = mgrs_inv(mgrs, center)
 %   See also MGRS_FWD, UTMUPS_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.43.
 
   narginchk(1, 2)
   if nargin < 2
diff --git a/matlab/geographiclib/polarst_fwd.m b/matlab/geographiclib/polarst_fwd.m
index a05e01c..30c6ab3 100644
--- a/matlab/geographiclib/polarst_fwd.m
+++ b/matlab/geographiclib/polarst_fwd.m
@@ -25,8 +25,6 @@ function [x, y, gam, k] = polarst_fwd(isnorth, lat, lon, ellipsoid)
 %     DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(3, 4)
   if nargin < 4, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/polarst_inv.m b/matlab/geographiclib/polarst_inv.m
index d20611b..d4d1729 100644
--- a/matlab/geographiclib/polarst_inv.m
+++ b/matlab/geographiclib/polarst_inv.m
@@ -25,8 +25,6 @@ function [lat, lon, gam, k] = polarst_inv(isnorth, x, y, ellipsoid)
 %     DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(3, 4)
   if nargin < 4, ellipsoid = defaultellipsoid; end
diff --git a/matlab/geographiclib/private/atan2dx.m b/matlab/geographiclib/private/atan2dx.m
index 30ef64f..0488cb7 100644
--- a/matlab/geographiclib/private/atan2dx.m
+++ b/matlab/geographiclib/private/atan2dx.m
@@ -2,7 +2,8 @@ function z = atan2dx(y, x)
 %ATAN2DX  Compute 2 argument arctangent with result in degrees
 %
 %   z = ATAN2DX(y, x) compute atan2(y, x) with result in degrees in
-%   [-180,180) and quadrant symmetries enforced.
+%   [-180,180) and quadrant symmetries enforced.  x and y must be the same
+%   shape.
 
   persistent octavep
   if isempty(octavep)
diff --git a/matlab/geographiclib/private/sincosdx.m b/matlab/geographiclib/private/sincosdx.m
index 8b265d7..e8b4dc2 100644
--- a/matlab/geographiclib/private/sincosdx.m
+++ b/matlab/geographiclib/private/sincosdx.m
@@ -19,7 +19,8 @@ function [sinx, cosx] = sincosdx(x)
     q = floor(r / 90 + 0.5);
     r = r - 90 * q;
     q = mod(q, 4);
-    sinx = sind(r); cosx = cosd(r);
+    r = r * (pi/180);
+    sinx = sin(r); cosx = cos(r);
     t = q == 1; z = 0 - sinx(t); sinx(t) = cosx(t); cosx(t) = z;
     t = q == 2; sinx(t) = 0 - sinx(t); cosx(t) = 0 - cosx(t);
     t = q == 3; z = sinx(t); sinx(t) = 0 - cosx(t); cosx(t) = z;
diff --git a/matlab/geographiclib/projdoc.m b/matlab/geographiclib/projdoc.m
index 5aafcaa..e79e3eb 100644
--- a/matlab/geographiclib/projdoc.m
+++ b/matlab/geographiclib/projdoc.m
@@ -67,8 +67,6 @@ function projdoc
 %     GNOMONIC_FWD, GNOMONIC_INV, DEFAULTELLIPSOID, ECC2FLAT, FLAT2ECC.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   help projdoc
 end
diff --git a/matlab/geographiclib/tranmerc_fwd.m b/matlab/geographiclib/tranmerc_fwd.m
index 1f2e356..6930c7b 100644
--- a/matlab/geographiclib/tranmerc_fwd.m
+++ b/matlab/geographiclib/tranmerc_fwd.m
@@ -13,7 +13,7 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
 %   case of lat0 = 0 is treated efficiently provided that lat0 is specified
 %   as a scalar.  projdoc defines the projection and gives the restrictions
 %   on the allowed ranges of the arguments.  The inverse projection is
-%   given by tranmerc_inv.
+%   given by tranmerc_inv.  The scale on the central meridian is 1.
 %
 %   gam and k give metric properties of the projection at (lat,lon); gam is
 %   the meridian convergence at the point and k is the scale.
@@ -40,13 +40,11 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
 %     DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
   try
-    Z = zeros(size(lat0 + lon0 + lat + lon));
+    S = size(lat0 + lon0 + lat + lon);
   catch
     error('lat0, lon0, lat, lon have incompatible sizes')
   end
@@ -54,6 +52,7 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
     error('ellipsoid must be a vector of size 2')
   end
 
+  Z = zeros(prod(S),1);
   maxpow = 6;
 
   a = ellipsoid(1);
@@ -66,18 +65,19 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
   b1 = (1 - f) * (A1m1f(n) + 1);
   a1 = b1 * a;
 
-  lon = AngDiff(lon0, lon);
+  lat = LatFix(lat(:)) + Z;
+  lon = AngDiff(lon0(:), lon(:)) + Z;
 
   latsign = 1 - 2 * (lat < 0);
   lonsign = 1 - 2 * (lon < 0);
   lon = lon .* lonsign;
-  lat = LatFix(lat) .* latsign;
+  lat = lat .* latsign;
   backside = lon > 90;
   latsign(backside & lat == 0) = -1;
   lon(backside) = 180 - lon(backside);
   [sphi, cphi] = sincosdx(lat);
   [slam, clam] = sincosdx(lon);
-  tau = sphi ./ cphi;
+  tau = sphi ./ max(sqrt(realmin), cphi);
   taup = taupf(tau, e2);
   xip = atan2(taup, clam);
   etap = asinh(slam ./ hypot(taup, clam));
@@ -87,8 +87,8 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
   if any(c)
     xip(c) = pi/2;
     etap(c) = 0;
-    gam(c) = lon;
-    k = cc;
+    gam(c) = lon(c);
+    k(c) = cc;
   end
   c0 = cos(2 * xip); ch0 = cosh(2 * etap);
   s0 = sin(2 * xip); sh0 = sinh(2 * etap);
@@ -133,9 +133,11 @@ function [x, y, gam, k] = tranmerc_fwd(lat0, lon0, lat, lon, ellipsoid)
     [sbet0, cbet0] = norm2((1-f) * sbet0, cbet0);
     y0 = a1 * (atan2(sbet0, cbet0) + ...
                SinCosSeries(true, sbet0, cbet0, C1f(n)));
-    y0 = reshape(y0, size(lat0));
   end
   y = y - y0;
+
+  x = reshape(x, S); y = reshape(y, S);
+  gam = reshape(gam, S); k = reshape(k, S);
 end
 
 function alp = alpf(n)
diff --git a/matlab/geographiclib/tranmerc_inv.m b/matlab/geographiclib/tranmerc_inv.m
index 7bac19d..b08653e 100644
--- a/matlab/geographiclib/tranmerc_inv.m
+++ b/matlab/geographiclib/tranmerc_inv.m
@@ -13,7 +13,7 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
 %   case of lat0 = 0 is treated efficiently provided that lat0 is specified
 %   as a scalar.  projdoc defines the projection and gives the restrictions
 %   on the allowed ranges of the arguments.  The forward projection is
-%   given by tranmerc_fwd.
+%   given by tranmerc_fwd.  The scale on the central meridian is 1.
 %
 %   gam and K give metric properties of the projection at (lat,lon); gam is
 %   the meridian convergence at the point and k is the scale.
@@ -40,13 +40,11 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
 %     DEFAULTELLIPSOID.
 
 % Copyright (c) Charles Karney (2012-2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.44.
 
   narginchk(4, 5)
   if nargin < 5, ellipsoid = defaultellipsoid; end
   try
-    Z = zeros(size(lat0 + lon0 + x + y));
+    S = size(lat0 + lon0 + x + y);
   catch
     error('lat0, lon0, x, y have incompatible sizes')
   end
@@ -54,6 +52,7 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
     error('ellipsoid must be a vector of size 2')
   end
 
+  Z = zeros(prod(S),1);
   maxpow = 6;
 
   a = ellipsoid(1);
@@ -73,12 +72,11 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
     [sbet0, cbet0] = norm2((1-f) * sbet0, cbet0);
     y0 = a1 * (atan2(sbet0, cbet0) + ...
                SinCosSeries(true, sbet0, cbet0, C1f(n)));
-    y0 = reshape(y0, size(lat0));
   end
-  y = y + y0;
+  y = y(:) + y0 + Z;
 
   xi = y / a1;
-  eta = x / a1;
+  eta = x(:) / a1 + Z;
   xisign = 1 - 2 * (xi < 0 );
   etasign = 1 - 2 * (eta < 0 );
   xi = xi .* xisign;
@@ -122,7 +120,7 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
   lon = atan2dx(s, c);
   sxip = sin(xip);
   tau = tauf(sxip./r, e2);
-  lat = atan2dx(tau, 1);
+  lat = atan2dx(tau, 1 + Z);
   gam = gam + atan2dx(sxip .* tanh(etap), c);
   c = r ~= 0;
   k(c) = k(c) .* sqrt(e2m + e2 ./ (1 + tau.^2)) .* ...
@@ -136,9 +134,12 @@ function [lat, lon, gam, k] = tranmerc_inv(lat0, lon0, x, y, ellipsoid)
   lat = lat .* xisign;
   lon(backside) = 180 - lon(backside);
   lon = lon .* etasign;
-  lon = AngNormalize(lon + AngNormalize(lon0));
+  lon = AngNormalize(lon + AngNormalize(lon0(:)));
   gam(backside) = 180 - gam(backside);
   gam = AngNormalize(gam .* xisign .* etasign);
+
+  lat = reshape(lat, S); lon = reshape(lon, S);
+  gam = reshape(gam, S); k = reshape(k, S);
 end
 
 function bet = betf(n)
diff --git a/matlab/geographiclib/utmups_fwd.m b/matlab/geographiclib/utmups_fwd.m
index efcac8b..22a0358 100644
--- a/matlab/geographiclib/utmups_fwd.m
+++ b/matlab/geographiclib/utmups_fwd.m
@@ -44,8 +44,6 @@ function [x, y, zone, isnorth, gam, k] = utmups_fwd(lat, lon, setzone)
 %   See also UTMUPS_INV, TRANMERC_FWD, POLARST_FWD, MGRS_FWD.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(2, 3)
   if nargin < 3, setzone = -1; end
diff --git a/matlab/geographiclib/utmups_inv.m b/matlab/geographiclib/utmups_inv.m
index 8a5692e..550ab79 100644
--- a/matlab/geographiclib/utmups_inv.m
+++ b/matlab/geographiclib/utmups_inv.m
@@ -35,8 +35,6 @@ function [lat, lon, gam, k] = utmups_inv(x, y, zone, isnorth)
 %   See also UTMUPS_FWD, TRANMERC_INV, POLARST_INV, MGRS_INV.
 
 % Copyright (c) Charles Karney (2015) <charles at karney.com>.
-%
-% This file was distributed with GeographicLib 1.42.
 
   narginchk(4, 4)
   try
diff --git a/maxima/geodesic.mac b/maxima/geodesic.mac
index 0993ddc..a5da9d7 100644
--- a/maxima/geodesic.mac
+++ b/maxima/geodesic.mac
@@ -101,7 +101,7 @@ documented at
 */
 
 /* The corresponding version of GeographicLib */
-geod_version:[1,44,0]$
+geod_version:[1,45,0]$
 
 /* Load series created by geod.mac (NEED TO UNCOMMENT THE LAST LINE OF
 geod.mac TO GENERATE THIS FILE). */
@@ -628,7 +628,7 @@ geod_geninverse(g, lat1, lon1, lat2, lon2):=block(
     ssig1 : sbet1, csig1 : calp1 * cbet1,
     ssig2 : sbet2, csig2 : calp2 * cbet2,
     /* sig12 = sig2 - sig1 */
-    sig12 : atan2x(max(csig1 * ssig2 - ssig1 * csig2, 0b0),
+    sig12 : atan2x(0b0 + max(0b0, csig1 * ssig2 - ssig1 * csig2),
       csig1 * csig2 + ssig1 * ssig2),
     block([r],
       r:Lengths(g, g[g_n], E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
@@ -1084,16 +1084,16 @@ block([salp2 : 0b0, calp2 : 0b0, sig12 : 0b0,
   block([t:norm2(ssig2, csig2)], ssig2:t[1], csig2:t[2]),
   /* norm2(&somg2, &comg2); -- don't need to normalize! */
   /* sig12 = sig2 - sig1, limit to [0, pi] */
-  sig12 : atan2x(max(csig1 * ssig2 - ssig1 * csig2, 0b0),
+  sig12 : atan2x(0b0 + max(0b0, csig1 * ssig2 - ssig1 * csig2),
     csig1 * csig2 + ssig1 * ssig2),
   /* omg12 = omg2 - omg1, limit to [0, pi] */
-  omg12 : atan2x(max(comg1 * somg2 - somg1 * comg2, 0b0),
+  omg12 : atan2x(0b0 + max(0b0, comg1 * somg2 - somg1 * comg2),
     comg1 * comg2 + somg1 * somg2),
   k2 : sq(calp0) * g[g_ep2],
   if exact then block([chi12],
     E : Ef(-k2, -g[g_ep2]),
-    chi12 : atan2x(max(cchi1 * somg2 - somg1 * cchi2, 0b0),
-                       cchi1 * cchi2 + somg1 * somg2),
+    chi12 : atan2x(0b0 + max(0b0, cchi1 * somg2 - somg1 * cchi2),
+      cchi1 * cchi2 + somg1 * somg2),
     lam12 : chi12 -
       g[g_e2]/g[g_f1] * salp0 * E[e_hc] / (pi/2) *
       (sig12 + deltah(ssig2, csig2, dn2, E[e_k2], E[e_alpha2], E[e_hc]) -
diff --git a/pom.xml b/pom.xml
index 3319b01..1790516 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
 
   <groupId>com.sri.vt</groupId>
   <artifactId>geographiclib</artifactId>
-  <version>1.44-SNAPSHOT</version>
+  <version>1.45-SNAPSHOT</version>
   <packaging>majic-cmake</packaging>
   <name>GeographicLib</name>
 
diff --git a/python/Makefile.am b/python/Makefile.am
index 97f7ea8..278a43b 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -28,4 +28,4 @@ clean-local:
 	rm -rf *.pyc $(PACKAGE)/*.pyc
 
 EXTRA_DIST = Makefile.mk $(PACKAGE)/CMakeLists.txt $(PYTHON_FILES) setup.py \
-	MANIFEST.in README.txt
+	MANIFEST.in README.txt test/__init__.py test/test_geodesic.py
diff --git a/python/Makefile.in b/python/Makefile.in
index 77c8569..0f3bfc3 100644
--- a/python/Makefile.in
+++ b/python/Makefile.in
@@ -263,7 +263,7 @@ PYTHON_FILES = \
 
 pythondir = $(libdir)/python/site-packages/$(PACKAGE)
 EXTRA_DIST = Makefile.mk $(PACKAGE)/CMakeLists.txt $(PYTHON_FILES) setup.py \
-	MANIFEST.in README.txt
+	MANIFEST.in README.txt test/__init__.py test/test_geodesic.py
 
 all: all-am
 
diff --git a/python/geographiclib/__init__.py b/python/geographiclib/__init__.py
index d6c610d..66849cb 100644
--- a/python/geographiclib/__init__.py
+++ b/python/geographiclib/__init__.py
@@ -1,4 +1,4 @@
 """geographiclib: geodesic routines from GeographicLib"""
 
-__version_info__ = (1, 44, 0)
-__version__ = "1.44"
+__version_info__ = (1, 45, 0)
+__version__ = "1.45"
diff --git a/python/geographiclib/geodesic.py b/python/geographiclib/geodesic.py
index 8858bb0..10ce9d3 100644
--- a/python/geographiclib/geodesic.py
+++ b/python/geographiclib/geodesic.py
@@ -99,6 +99,7 @@ class Geodesic(object):
   LONGITUDE     = GeodesicCapability.LONGITUDE
   AZIMUTH       = GeodesicCapability.AZIMUTH
   DISTANCE      = GeodesicCapability.DISTANCE
+  STANDARD      = GeodesicCapability.STANDARD
   DISTANCE_IN   = GeodesicCapability.DISTANCE_IN
   REDUCEDLENGTH = GeodesicCapability.REDUCEDLENGTH
   GEODESICSCALE = GeodesicCapability.GEODESICSCALE
@@ -267,7 +268,7 @@ class Geodesic(object):
     """
 
     self._a = float(a)
-    self._f = float(f) if f <= 1 else 1.0/f
+    self._f = float(f)
     self._f1 = 1 - self._f
     self._e2 = self._f * (2 - self._f)
     self._ep2 = self._e2 / Math.sq(self._f1) # e2 / (1 - e2)
@@ -645,11 +646,11 @@ class Geodesic(object):
     # Math.norm(somg2, comg2); -- don't need to normalize!
 
     # sig12 = sig2 - sig1, limit to [0, pi]
-    sig12 = math.atan2(max(csig1 * ssig2 - ssig1 * csig2, 0.0),
+    sig12 = math.atan2(max(0.0, csig1 * ssig2 - ssig1 * csig2),
                        csig1 * csig2 + ssig1 * ssig2)
 
     # omg12 = omg2 - omg1, limit to [0, pi]
-    omg12 = math.atan2(max(comg1 * somg2 - somg1 * comg2, 0.0),
+    omg12 = math.atan2(max(0.0, comg1 * somg2 - somg1 * comg2),
                        comg1 * comg2 + somg1 * somg2)
     # real B312, h0
     k2 = Math.sq(calp0) * self._ep2
@@ -693,7 +694,8 @@ class Geodesic(object):
     lat1 = Math.AngRound(Math.LatFix(lat1))
     lat2 = Math.AngRound(Math.LatFix(lat2))
     # Swap points so that point with higher (abs) latitude is point 1
-    swapp = 1 if abs(lat1) >= abs(lat2) else -1
+    # If one latitude is a nan, then it becomes lat1.
+    swapp = -1 if abs(lat1) < abs(lat2) else 1
     if swapp < 0:
       lonsign *= -1
       lat2, lat1 = lat1, lat2
@@ -765,7 +767,7 @@ class Geodesic(object):
       ssig2 = sbet2; csig2 = calp2 * cbet2
 
       # sig12 = sig2 - sig1
-      sig12 = math.atan2(max(csig1 * ssig2 - ssig1 * csig2, 0.0),
+      sig12 = math.atan2(max(0.0, csig1 * ssig2 - ssig1 * csig2),
                          csig1 * csig2 + ssig1 * ssig2)
 
       s12x, m12x, dummy, M12, M21 = self.Lengths(
@@ -981,27 +983,27 @@ class Geodesic(object):
 
   def CheckPosition(lat, lon):
     """Check that lat and lon are legal and return normalized lon"""
-    if not (abs(lat) <= 90):
+    if abs(lat) > 90:
       raise ValueError("latitude " + str(lat) + " not in [-90, 90]")
-    if not Math.isfinite(lon):
-      raise ValueError("lonitude " + str(lon) + " not a finite number")
+    # if not Math.isfinite(lon):
+    #   raise ValueError("longitude " + str(lon) + " not a finite number")
     return Math.AngNormalize(lon)
   CheckPosition = staticmethod(CheckPosition)
 
   def CheckAzimuth(azi):
     """Check that azi is legal and return normalized value"""
-    if not Math.isfinite(azi):
-      raise ValueError("azimuth " + str(azi) + " not a finite number")
+    # if not Math.isfinite(azi):
+    #   raise ValueError("azimuth " + str(azi) + " not a finite number")
     return Math.AngNormalize(azi)
   CheckAzimuth = staticmethod(CheckAzimuth)
 
   def CheckDistance(s):
     """Check that s is a legal distance"""
-    if not Math.isfinite(s):
-      raise ValueError("distance " + str(s) + " not a finite number")
+    # if not Math.isfinite(s):
+    #   raise ValueError("distance " + str(s) + " not a finite number")
   CheckDistance = staticmethod(CheckDistance)
 
-  def Inverse(self, lat1, lon1, lat2, lon2, outmask = DISTANCE | AZIMUTH):
+  def Inverse(self, lat1, lon1, lat2, lon2, outmask = STANDARD):
     """Solve the inverse geodesic problem.  Compute geodesic between (lat1,
     lon1) and (lat2, lon2).  Return a dictionary with (some) of the
     following entries:
@@ -1025,6 +1027,7 @@ class Geodesic(object):
 
       Geodesic.AZIMUTH
       Geodesic.DISTANCE
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
@@ -1035,7 +1038,7 @@ class Geodesic(object):
     lon1 indicates whether the geodesic is east going or west going.
     Otherwise lon1 and lon2 are both reduced to the range [-180,180).
 
-    The default value of outmask is DISTANCE | AZIMUTH.
+    The default value of outmask is STANDARD.
 
     """
 
@@ -1070,8 +1073,7 @@ class Geodesic(object):
       outmask | (Geodesic.EMPTY if arcmode else Geodesic.DISTANCE_IN))
     return line.GenPosition(arcmode, s12_a12, outmask)
 
-  def Direct(self, lat1, lon1, azi1, s12,
-             outmask = LATITUDE | LONGITUDE | AZIMUTH):
+  def Direct(self, lat1, lon1, azi1, s12, outmask = STANDARD):
     """Solve the direct geodesic problem.  Compute geodesic starting at
     (lat1, lon1) with azimuth azi1 and length s12.  Return a dictionary
     with (some) of the following entries:
@@ -1096,6 +1098,7 @@ class Geodesic(object):
       Geodesic.LATITUDE
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
@@ -1107,7 +1110,7 @@ class Geodesic(object):
     how many times and in what sense the geodesic encircles the
     ellipsoid.
 
-    The default value of outmask is LATITUDE | LONGITUDE | AZIMUTH.
+    The default value of outmask is STANDARD.
 
     """
 
@@ -1132,8 +1135,7 @@ class Geodesic(object):
     if outmask & Geodesic.AREA: result['S12'] = S12
     return result
 
-  def ArcDirect(self, lat1, lon1, azi1, a12,
-                outmask = LATITUDE | LONGITUDE | AZIMUTH | DISTANCE):
+  def ArcDirect(self, lat1, lon1, azi1, a12, outmask = STANDARD):
     """Solve the direct geodesic problem.  Compute geodesic starting at
     (lat1, lon1) with azimuth azi1 and spherical arc length a12.  Return
     a dictionary with (some) of the following entries:
@@ -1161,14 +1163,14 @@ class Geodesic(object):
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
       Geodesic.DISTANCE
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
       Geodesic.ALL (all of the above)
       Geodesic.LONG_UNROLL
 
-    The default value of outmask is LATITUDE | LONGITUDE | AZIMUTH |
-    DISTANCE.
+    The default value of outmask is STANDARD.
 
     """
 
@@ -1203,6 +1205,7 @@ class Geodesic(object):
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
       Geodesic.DISTANCE
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
diff --git a/python/geographiclib/geodesiccapability.py b/python/geographiclib/geodesiccapability.py
index 235568e..d2a55fb 100644
--- a/python/geographiclib/geodesiccapability.py
+++ b/python/geographiclib/geodesiccapability.py
@@ -32,6 +32,7 @@ class GeodesicCapability(object):
   LONGITUDE     = 1 << 8  | CAP_C3
   AZIMUTH       = 1 << 9  | CAP_NONE
   DISTANCE      = 1 << 10 | CAP_C1
+  STANDARD      = LATITUDE | LONGITUDE | AZIMUTH | DISTANCE
   DISTANCE_IN   = 1 << 11 | CAP_C1 | CAP_C1p
   REDUCEDLENGTH = 1 << 12 | CAP_C1 | CAP_C2
   GEODESICSCALE = 1 << 13 | CAP_C1 | CAP_C2
diff --git a/python/geographiclib/geodesicline.py b/python/geographiclib/geodesicline.py
index ca8059d..a03dc99 100644
--- a/python/geographiclib/geodesicline.py
+++ b/python/geographiclib/geodesicline.py
@@ -36,6 +36,7 @@ class GeodesicLine(object):
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
       Geodesic.DISTANCE
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
@@ -291,9 +292,7 @@ class GeodesicLine(object):
     a12 = s12_a12 if arcmode else math.degrees(sig12)
     return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
 
-  def Position(self, s12,
-               outmask = GeodesicCapability.LATITUDE |
-               GeodesicCapability.LONGITUDE | GeodesicCapability.AZIMUTH):
+  def Position(self, s12, outmask = GeodesicCapability.STANDARD):
     """Return the point a distance s12 along the geodesic line.  Return a
     dictionary with (some) of the following entries:
 
@@ -320,13 +319,14 @@ class GeodesicLine(object):
       Geodesic.LATITUDE
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
       Geodesic.ALL (all of the above)
       Geodesic.LONG_UNROLL
 
-    The default value of outmask is LATITUDE | LONGITUDE | AZIMUTH.
+    The default value of outmask is STANDARD.
 
     """
 
@@ -349,10 +349,7 @@ class GeodesicLine(object):
     if outmask & Geodesic.AREA: result['S12'] = S12
     return result
 
-  def ArcPosition(self, a12,
-                  outmask = GeodesicCapability.LATITUDE |
-                  GeodesicCapability.LONGITUDE | GeodesicCapability.AZIMUTH |
-                  GeodesicCapability.DISTANCE):
+  def ArcPosition(self, a12, outmask = GeodesicCapability.STANDARD):
     """Return the point a spherical arc length a12 along the geodesic line.
     Return a dictionary with (some) of the following entries:
 
@@ -379,14 +376,14 @@ class GeodesicLine(object):
       Geodesic.LONGITUDE
       Geodesic.AZIMUTH
       Geodesic.DISTANCE
+      Geodesic.STANDARD (all of the above)
       Geodesic.REDUCEDLENGTH
       Geodesic.GEODESICSCALE
       Geodesic.AREA
       Geodesic.ALL (all of the above)
       Geodesic.LONG_UNROLL
 
-    The default value of outmask is LATITUDE | LONGITUDE | AZIMUTH |
-    DISTANCE.
+    The default value of outmask is STANDARD.
 
     """
 
diff --git a/python/geographiclib/geomath.py b/python/geographiclib/geomath.py
index fcbd63a..0435319 100644
--- a/python/geographiclib/geomath.py
+++ b/python/geographiclib/geomath.py
@@ -142,7 +142,8 @@ class Math(object):
   def sincosd(x):
     """Compute sine and cosine of x in degrees."""
 
-    r = math.fmod(x, 360); q = int(math.floor(r / 90 + 0.5))
+    r = math.fmod(x, 360)
+    q = Math.nan if Math.isnan(r) else int(math.floor(r / 90 + 0.5))
     r -= 90 * q; r = math.radians(r)
     s = math.sin(r); c = math.cos(r)
     q = q % 4
@@ -179,3 +180,9 @@ class Math(object):
 
     return abs(x) <= Math.maxval
   isfinite = staticmethod(isfinite)
+
+  def isnan(x):
+    """Test if nan"""
+
+    return math.isnan(x) if sys.version_info > (2, 6) else x != x
+  isnan = staticmethod(isnan)
diff --git a/python/geographiclib/polygonarea.py b/python/geographiclib/polygonarea.py
index 30e3f91..5a28e60 100644
--- a/python/geographiclib/polygonarea.py
+++ b/python/geographiclib/polygonarea.py
@@ -119,7 +119,7 @@ class PolygonArea(object):
     tempsum.Add(S12)
     crossings = self._crossings + PolygonArea.transit(self._lon1, self._lon0)
     if crossings & 1:
-      tempsum.Add( (1 if tempsum < 0 else -1) * self._area0/2 )
+      tempsum.Add( (1 if tempsum.Sum() < 0 else -1) * self._area0/2 )
     # area is with the clockwise sense.  If !reverse convert to
     # counter-clockwise convention.
     if not reverse: tempsum.Negate()
diff --git a/python/setup.py b/python/setup.py
index cfa0727..b27421f 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -15,7 +15,7 @@
 from distutils.core import setup
 
 setup(name="geographiclib",
-      version="1.44",
+      version="1.45",
       description=
         "A translation of the GeographicLib::Geodesic class to Python",
       author="Charles Karney",
@@ -25,13 +25,14 @@ setup(name="geographiclib",
       data_files=[],
       license="MIT",
       keywords="gis geographical earth distance geodesic",
-      classifiers=["Development Status :: 5 - Production/Stable",
-                   "Intended Audience :: Developers",
-                   "Intended Audience :: Science/Research",
-                   "License :: OSI Approved :: MIT License",
-                   "Operating System :: OS Independent",
-                   "Programming Language :: Python",
-                   "Topic :: Scientific/Engineering :: GIS",
-                   "Topic :: Software Development :: Libraries :: Python Modules",
-                   ],
-      )
+      classifiers=[
+          "Development Status :: 5 - Production/Stable",
+          "Intended Audience :: Developers",
+          "Intended Audience :: Science/Research",
+          "License :: OSI Approved :: MIT License",
+          "Operating System :: OS Independent",
+          "Programming Language :: Python",
+          "Topic :: Scientific/Engineering :: GIS",
+          "Topic :: Software Development :: Libraries :: Python Modules",
+      ],
+)
diff --git a/python/test/__init__.py b/python/test/__init__.py
new file mode 100644
index 0000000..8dd3877
--- /dev/null
+++ b/python/test/__init__.py
@@ -0,0 +1,12 @@
+"""
+
+test_geodesic: test the geodesic routines from GeographicLib
+
+Run these tests with one of
+
+    python2 -m unittest -v test.test_geodesic
+    python3 -m unittest -v test.test_geodesic
+
+executed in this directory's parent directory.
+
+"""
diff --git a/python/test/test_geodesic.py b/python/test/test_geodesic.py
new file mode 100644
index 0000000..49ef638
--- /dev/null
+++ b/python/test/test_geodesic.py
@@ -0,0 +1,432 @@
+import unittest
+
+from geographiclib.geodesic import Geodesic
+from geographiclib.geomath import Math
+
+class GeodesicTest(unittest.TestCase):
+
+    testcases = [
+        [35.60777, -139.44815, 111.098748429560326,
+         -11.17491, -69.95921, 129.289270889708762,
+         8935244.5604818305, 80.50729714281974, 6273170.2055303837,
+         0.16606318447386067, 0.16479116945612937, 12841384694976.432],
+        [55.52454, 106.05087, 22.020059880982801,
+         77.03196, 197.18234, 109.112041110671519,
+         4105086.1713924406, 36.892740690445894, 3828869.3344387607,
+         0.80076349608092607, 0.80101006984201008, 61674961290615.615],
+        [-21.97856, 142.59065, -32.44456876433189,
+         41.84138, 98.56635, -41.84359951440466,
+         8394328.894657671, 75.62930491011522, 6161154.5773110616,
+         0.24816339233950381, 0.24930251203627892, -6637997720646.717],
+        [-66.99028, 112.2363, 173.73491240878403,
+         -12.70631, 285.90344, 2.512956620913668,
+         11150344.2312080241, 100.278634181155759, 6289939.5670446687,
+         -0.17199490274700385, -0.17722569526345708, -121287239862139.744],
+        [-17.42761, 173.34268, -159.033557661192928,
+         -15.84784, 5.93557, -20.787484651536988,
+         16076603.1631180673, 144.640108810286253, 3732902.1583877189,
+         -0.81273638700070476, -0.81299800519154474, 97825992354058.708],
+        [32.84994, 48.28919, 150.492927788121982,
+         -56.28556, 202.29132, 48.113449399816759,
+         16727068.9438164461, 150.565799985466607, 3147838.1910180939,
+         -0.87334918086923126, -0.86505036767110637, -72445258525585.010],
+        [6.96833, 52.74123, 92.581585386317712,
+         -7.39675, 206.17291, 90.721692165923907,
+         17102477.2496958388, 154.147366239113561, 2772035.6169917581,
+         -0.89991282520302447, -0.89986892177110739, -1311796973197.995],
+        [-50.56724, -16.30485, -105.439679907590164,
+         -33.56571, -94.97412, -47.348547835650331,
+         6455670.5118668696, 58.083719495371259, 5409150.7979815838,
+         0.53053508035997263, 0.52988722644436602, 41071447902810.047],
+        [-58.93002, -8.90775, 140.965397902500679,
+         -8.91104, 133.13503, 19.255429433416599,
+         11756066.0219864627, 105.755691241406877, 6151101.2270708536,
+         -0.26548622269867183, -0.27068483874510741, -86143460552774.735],
+        [-68.82867, -74.28391, 93.774347763114881,
+         -50.63005, -8.36685, 34.65564085411343,
+         3956936.926063544, 35.572254987389284, 3708890.9544062657,
+         0.81443963736383502, 0.81420859815358342, -41845309450093.787],
+        [-10.62672, -32.0898, -86.426713286747751,
+         5.883, -134.31681, -80.473780971034875,
+         11470869.3864563009, 103.387395634504061, 6184411.6622659713,
+         -0.23138683500430237, -0.23155097622286792, 4198803992123.548],
+        [-21.76221, 166.90563, 29.319421206936428,
+         48.72884, 213.97627, 43.508671946410168,
+         9098627.3986554915, 81.963476716121964, 6299240.9166992283,
+         0.13965943368590333, 0.14152969707656796, 10024709850277.476],
+        [-19.79938, -174.47484, 71.167275780171533,
+         -11.99349, -154.35109, 65.589099775199228,
+         2319004.8601169389, 20.896611684802389, 2267960.8703918325,
+         0.93427001867125849, 0.93424887135032789, -3935477535005.785],
+        [-11.95887, -116.94513, 92.712619830452549,
+         4.57352, 7.16501, 78.64960934409585,
+         13834722.5801401374, 124.688684161089762, 5228093.177931598,
+         -0.56879356755666463, -0.56918731952397221, -9919582785894.853],
+        [-87.85331, 85.66836, -65.120313040242748,
+         66.48646, 16.09921, -4.888658719272296,
+         17286615.3147144645, 155.58592449699137, 2635887.4729110181,
+         -0.90697975771398578, -0.91095608883042767, 42667211366919.534],
+        [1.74708, 128.32011, -101.584843631173858,
+         -11.16617, 11.87109, -86.325793296437476,
+         12942901.1241347408, 116.650512484301857, 5682744.8413270572,
+         -0.44857868222697644, -0.44824490340007729, 10763055294345.653],
+        [-25.72959, -144.90758, -153.647468693117198,
+         -57.70581, -269.17879, -48.343983158876487,
+         9413446.7452453107, 84.664533838404295, 6356176.6898881281,
+         0.09492245755254703, 0.09737058264766572, 74515122850712.444],
+        [-41.22777, 122.32875, 14.285113402275739,
+         -7.57291, 130.37946, 10.805303085187369,
+         3812686.035106021, 34.34330804743883, 3588703.8812128856,
+         0.82605222593217889, 0.82572158200920196, -2456961531057.857],
+        [11.01307, 138.25278, 79.43682622782374,
+         6.62726, 247.05981, 103.708090215522657,
+         11911190.819018408, 107.341669954114577, 6070904.722786735,
+         -0.29767608923657404, -0.29785143390252321, 17121631423099.696],
+        [-29.47124, 95.14681, -163.779130441688382,
+         -27.46601, -69.15955, -15.909335945554969,
+         13487015.8381145492, 121.294026715742277, 5481428.9945736388,
+         -0.51527225545373252, -0.51556587964721788, 104679964020340.318]]
+
+    def test_inverse(self):
+        for l in GeodesicTest.testcases:
+            (lat1, lon1, azi1, lat2, lon2, azi2,
+             s12, a12, m12, M12, M21, S12) = l
+            inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2,
+                                         Geodesic.ALL | Geodesic.LONG_UNROLL)
+            self.assertAlmostEqual(lon2, inv["lon2"], delta = 1e-13)
+            self.assertAlmostEqual(azi1, inv["azi1"], delta = 1e-13)
+            self.assertAlmostEqual(azi2, inv["azi2"], delta = 1e-13)
+            self.assertAlmostEqual(s12, inv["s12"], delta = 1e-8)
+            self.assertAlmostEqual(a12, inv["a12"], delta = 1e-13)
+            self.assertAlmostEqual(m12, inv["m12"], delta = 1e-8)
+            self.assertAlmostEqual(M12, inv["M12"], delta = 1e-15)
+            self.assertAlmostEqual(M21, inv["M21"], delta = 1e-15)
+            self.assertAlmostEqual(S12, inv["S12"], delta = 0.1)
+
+    def test_direct(self):
+        for l in GeodesicTest.testcases:
+            (lat1, lon1, azi1, lat2, lon2, azi2,
+             s12, a12, m12, M12, M21, S12) = l
+            dir = Geodesic.WGS84.Direct(lat1, lon1, azi1, s12,
+                                        Geodesic.ALL | Geodesic.LONG_UNROLL)
+            self.assertAlmostEqual(lat2, dir["lat2"], delta = 1e-13)
+            self.assertAlmostEqual(lon2, dir["lon2"], delta = 1e-13)
+            self.assertAlmostEqual(azi2, dir["azi2"], delta = 1e-13)
+            self.assertAlmostEqual(a12, dir["a12"], delta = 1e-13)
+            self.assertAlmostEqual(m12, dir["m12"], delta = 1e-8)
+            self.assertAlmostEqual(M12, dir["M12"], delta = 1e-15)
+            self.assertAlmostEqual(M21, dir["M21"], delta = 1e-15)
+            self.assertAlmostEqual(S12, dir["S12"], delta = 0.1)
+
+    def test_arcdirect(self):
+        for l in GeodesicTest.testcases:
+            (lat1, lon1, azi1, lat2, lon2, azi2,
+             s12, a12, m12, M12, M21, S12) = l
+            dir = Geodesic.WGS84.ArcDirect(lat1, lon1, azi1, a12,
+                                           Geodesic.ALL | Geodesic.LONG_UNROLL)
+            self.assertAlmostEqual(lat2, dir["lat2"], delta = 1e-13)
+            self.assertAlmostEqual(lon2, dir["lon2"], delta = 1e-13)
+            self.assertAlmostEqual(azi2, dir["azi2"], delta = 1e-13)
+            self.assertAlmostEqual(s12, dir["s12"], delta = 1e-8)
+            self.assertAlmostEqual(m12, dir["m12"], delta = 1e-8)
+            self.assertAlmostEqual(M12, dir["M12"], delta = 1e-15)
+            self.assertAlmostEqual(M21, dir["M21"], delta = 1e-15)
+            self.assertAlmostEqual(S12, dir["S12"], delta = 0.1)
+
+class GeodSolveTest(unittest.TestCase):
+
+    def test_GeodSolve0(self):
+        inv = Geodesic.WGS84.Inverse(40.6, -73.8, 49.01666667, 2.55)
+        self.assertAlmostEqual(inv["azi1"], 53.47022, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 111.59367, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 5853226, delta = 0.5)
+
+    def test_GeodSolve1(self):
+        dir = Geodesic.WGS84.Direct(40.63972222, -73.77888889, 53.5, 5850e3)
+        self.assertAlmostEqual(dir["lat2"], 49.01467, delta = 0.5e-5)
+        self.assertAlmostEqual(dir["lon2"], 2.56106, delta = 0.5e-5)
+        self.assertAlmostEqual(dir["azi2"], 111.62947, delta = 0.5e-5)
+
+    def test_GeodSolve2(self):
+        # Check fix for antipodal prolate bug found 2010-09-04
+        geod = Geodesic(6.4e6, -1/150.0)
+        inv = geod.Inverse(0.07476, 0, -0.07476, 180)
+        self.assertAlmostEqual(inv["azi1"], 90.00078, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00078, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20106193, delta = 0.5)
+        inv = geod.Inverse(0.1, 0, -0.1, 180)
+        self.assertAlmostEqual(inv["azi1"], 90.00105, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00105, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20106193, delta = 0.5)
+
+    def test_GeodSolve4(self):
+        # Check fix for short line bug found 2010-05-21
+        inv = Geodesic.WGS84.Inverse(36.493349428792, 0,
+                                     36.49334942879201, .0000008)
+        self.assertAlmostEqual(inv["s12"], 0.072, delta = 0.5e-3)
+
+    def test_GeodSolve5(self):
+        # Check fix for point2=pole bug found 2010-05-03
+        dir = Geodesic.WGS84.Direct(0.01777745589997, 30, 0, 10e6)
+        self.assertAlmostEqual(dir["lat2"], 90, delta = 0.5e-5)
+        if dir["lon2"] < 0:
+            self.assertAlmostEqual(dir["lon2"], -150, delta = 0.5e-5)
+            self.assertAlmostEqual(dir["azi2"], -180, delta = 0.5e-5)
+        else:
+            self.assertAlmostEqual(dir["lon2"], 30, delta = 0.5e-5)
+            self.assertAlmostEqual(dir["azi2"], 0, delta = 0.5e-5)
+
+    def test_GeodSolve6(self):
+        # Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
+        # x86 -O3).  Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1).
+        inv = Geodesic.WGS84.Inverse(88.202499451857, 0,
+                                     -88.202499451857, 179.981022032992859592)
+        self.assertAlmostEqual(inv["s12"], 20003898.214, delta = 0.5e-3)
+        inv = Geodesic.WGS84.Inverse(89.262080389218, 0,
+                                     -89.262080389218, 179.992207982775375662)
+        self.assertAlmostEqual(inv["s12"], 20003925.854, delta = 0.5e-3)
+        inv = Geodesic.WGS84.Inverse(89.333123580033, 0,
+                                     -89.333123580032997687,
+                                     179.99295812360148422)
+        self.assertAlmostEqual(inv["s12"], 20003926.881, delta = 0.5e-3)
+
+    def test_GeodSolve9(self):
+        # Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3)
+        inv = Geodesic.WGS84.Inverse(56.320923501171, 0,
+                                     -56.320923501171, 179.664747671772880215)
+        self.assertAlmostEqual(inv["s12"], 19993558.287, delta = 0.5e-3)
+
+    def test_GeodSolve10(self):
+        # Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
+        # 10 rel + debug)
+        inv = Geodesic.WGS84.Inverse(52.784459512564, 0,
+                                     -52.784459512563990912,
+                                     179.634407464943777557)
+        self.assertAlmostEqual(inv["s12"], 19991596.095, delta = 0.5e-3)
+
+    def test_GeodSolve11(self):
+        # Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
+        # 10 rel + debug)
+        inv = Geodesic.WGS84.Inverse(48.522876735459, 0,
+                                     -48.52287673545898293,
+                                     179.599720456223079643)
+        self.assertAlmostEqual(inv["s12"], 19989144.774, delta = 0.5e-3)
+
+    def test_GeodSolve12(self):
+        # Check fix for inverse geodesics on extreme prolate/oblate
+        # ellipsoids Reported 2012-08-29 Stefan Guenther
+        # <stefan.gunther at embl.de>; fixed 2012-10-07
+        geod = Geodesic(89.8, -1.83)
+        inv = geod.Inverse(0, 0, -10, 160)
+        self.assertAlmostEqual(inv["azi1"], 120.27, delta = 1e-2)
+        self.assertAlmostEqual(inv["azi2"], 105.15, delta = 1e-2)
+        self.assertAlmostEqual(inv["s12"], 266.7, delta = 1e-1)
+
+    def test_GeodSolve14(self):
+        # Check fix for inverse ignoring lon12 = nan
+        inv = Geodesic.WGS84.Inverse(0, 0, 1, Math.nan)
+        self.assertTrue(Math.isnan(inv["azi1"]))
+        self.assertTrue(Math.isnan(inv["azi2"]))
+        self.assertTrue(Math.isnan(inv["s12"]))
+
+    def test_GeodSolve15(self):
+        # Initial implementation of Math::eatanhe was wrong for e^2 < 0.  This
+        # checks that this is fixed.
+        geod = Geodesic(6.4e6, -1/150.0)
+        dir = geod.Direct(1, 2, 3, 4, Geodesic.AREA)
+        self.assertAlmostEqual(dir["S12"], 23700, delta = 0.5)
+
+    def test_GeodSolve17(self):
+        # Check fix for LONG_UNROLL bug found on 2015-05-07
+        dir = Geodesic.WGS84.Direct(40, -75, -10, 2e7,
+                                    Geodesic.STANDARD | Geodesic.LONG_UNROLL)
+        self.assertAlmostEqual(dir["lat2"], -39, delta = 1)
+        self.assertAlmostEqual(dir["lon2"], -254, delta = 1)
+        self.assertAlmostEqual(dir["azi2"], -170, delta = 1)
+        line = Geodesic.WGS84.Line(40, -75, -10)
+        dir = line.Position(2e7, Geodesic.STANDARD | Geodesic.LONG_UNROLL)
+        self.assertAlmostEqual(dir["lat2"], -39, delta = 1)
+        self.assertAlmostEqual(dir["lon2"], -254, delta = 1)
+        self.assertAlmostEqual(dir["azi2"], -170, delta = 1)
+        dir = Geodesic.WGS84.Direct(40, -75, -10, 2e7)
+        self.assertAlmostEqual(dir["lat2"], -39, delta = 1)
+        self.assertAlmostEqual(dir["lon2"], 105, delta = 1)
+        self.assertAlmostEqual(dir["azi2"], -170, delta = 1)
+        dir = line.Position(2e7)
+        self.assertAlmostEqual(dir["lat2"], -39, delta = 1)
+        self.assertAlmostEqual(dir["lon2"], 105, delta = 1)
+        self.assertAlmostEqual(dir["azi2"], -170, delta = 1)
+
+    def test_GeodSolve26(self):
+        # Check 0/0 problem with area calculation on sphere 2015-09-08
+        geod = Geodesic(6.4e6, 0)
+        inv = geod.Inverse(1, 2, 3, 4, Geodesic.AREA)
+        self.assertAlmostEqual(inv["S12"], 49911046115.0, delta = 0.5)
+
+    def test_GeodSolve28(self):
+        # Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+        # Java implementation fixed on 2015-05-19).
+        geod = Geodesic(6.4e6, 0.1)
+        dir = geod.Direct(1, 2, 10, 5e6)
+        self.assertAlmostEqual(dir["a12"], 48.55570690, delta = 0.5e-8)
+
+    def test_GeodSolve29(self):
+        # Check longitude unrolling with inverse calculation 2015-09-16
+        dir = Geodesic.WGS84.Inverse(0, 539, 0, 181)
+        self.assertAlmostEqual(dir["lon1"], 179, delta = 1e-10)
+        self.assertAlmostEqual(dir["lon2"], -179, delta = 1e-10)
+        self.assertAlmostEqual(dir["s12"], 222639, delta = 0.5)
+        dir = Geodesic.WGS84.Inverse(0, 539, 0, 181,
+                                     Geodesic.STANDARD | Geodesic.LONG_UNROLL)
+        self.assertAlmostEqual(dir["lon1"], 539, delta = 1e-10)
+        self.assertAlmostEqual(dir["lon2"], 541, delta = 1e-10)
+        self.assertAlmostEqual(dir["s12"], 222639, delta = 0.5)
+
+    def test_GeodSolve33(self):
+        # Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in
+        # Octave -- sind(-0.0) = +0.0 -- and in some version of Visual
+        # Studio -- fmod(-0.0, 360.0) = +0.0.
+        inv = Geodesic.WGS84.Inverse(0, 0, 0, 179)
+        self.assertAlmostEqual(inv["azi1"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19926189, delta = 0.5)
+        inv = Geodesic.WGS84.Inverse(0, 0, 0, 179.5)
+        self.assertAlmostEqual(inv["azi1"], 55.96650, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 124.03350, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19980862, delta = 0.5)
+        inv = Geodesic.WGS84.Inverse(0, 0, 0, 180)
+        self.assertAlmostEqual(inv["azi1"], 0.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], -180.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20003931, delta = 0.5)
+        inv = Geodesic.WGS84.Inverse(0, 0, 1, 180)
+        self.assertAlmostEqual(inv["azi1"], 0.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], -180.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19893357, delta = 0.5)
+        geod = Geodesic(6.4e6, 0)
+        inv = geod.Inverse(0, 0, 0, 179)
+        self.assertAlmostEqual(inv["azi1"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19994492, delta = 0.5)
+        inv = geod.Inverse(0, 0, 0, 180)
+        self.assertAlmostEqual(inv["azi1"], 0.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], -180.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20106193, delta = 0.5)
+        inv = geod.Inverse(0, 0, 1, 180)
+        self.assertAlmostEqual(inv["azi1"], 0.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], -180.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19994492, delta = 0.5)
+        geod = Geodesic(6.4e6, -1/300.0)
+        inv = geod.Inverse(0, 0, 0, 179)
+        self.assertAlmostEqual(inv["azi1"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 19994492, delta = 0.5)
+        inv = geod.Inverse(0, 0, 0, 180)
+        self.assertAlmostEqual(inv["azi1"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 90.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20106193, delta = 0.5)
+        inv = geod.Inverse(0, 0, 0.5, 180)
+        self.assertAlmostEqual(inv["azi1"], 33.02493, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], 146.97364, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20082617, delta = 0.5)
+        inv = geod.Inverse(0, 0, 1, 180)
+        self.assertAlmostEqual(inv["azi1"], 0.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["azi2"], -180.00000, delta = 0.5e-5)
+        self.assertAlmostEqual(inv["s12"], 20027270, delta = 0.5);
+
+    def test_GeodSolve55(self):
+        # Check fix for nan + point on equator or pole not returning all nans in
+        # Geodesic::Inverse, found 2015-09-23.
+        inv = Geodesic.WGS84.Inverse(Math.nan, 0, 0, 90)
+        self.assertTrue(Math.isnan(inv["azi1"]))
+        self.assertTrue(Math.isnan(inv["azi2"]))
+        self.assertTrue(Math.isnan(inv["s12"]))
+        inv = Geodesic.WGS84.Inverse(Math.nan, 0, 90, 9)
+        self.assertTrue(Math.isnan(inv["azi1"]))
+        self.assertTrue(Math.isnan(inv["azi2"]))
+        self.assertTrue(Math.isnan(inv["s12"]))
+
+class PlanimeterTest(unittest.TestCase):
+
+    from geographiclib.polygonarea import PolygonArea
+    polygon = PolygonArea(Geodesic.WGS84)
+    polyline = PolygonArea(Geodesic.WGS84, True)
+
+    def Planimeter(points):
+        PlanimeterTest.polygon.Clear()
+        for p in points:
+            Geodesic.CheckPosition(p[0], p[1])
+            PlanimeterTest.polygon.AddPoint(p[0], p[1])
+        return PlanimeterTest.polygon.Compute(False, True)
+    Planimeter = staticmethod(Planimeter)
+
+    def PolyLength(points):
+        PlanimeterTest.polyline.Clear()
+        for p in points:
+            Geodesic.CheckPosition(p[0], p[1])
+            PlanimeterTest.polyline.AddPoint(p[0], p[1])
+        return PlanimeterTest.polyline.Compute(False, True)
+    PolyLength = staticmethod(PolyLength)
+
+    def test_Planimeter0(self):
+        # Check fix for pole-encircling bug found 2011-03-16
+        points = [[89, 0], [89, 90], [89, 180], [89, 270]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 631819.8745, delta = 1e-4)
+        self.assertAlmostEqual(area, 24952305678.0, delta = 1)
+        points = [[-89, 0], [-89, 90], [-89, 180], [-89, 270]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 631819.8745, delta = 1e-4)
+        self.assertAlmostEqual(area, -24952305678.0, delta = 1)
+
+        points = [[0, -1], [-1, 0], [0, 1], [1, 0]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 627598.2731, delta = 1e-4)
+        self.assertAlmostEqual(area, 24619419146.0, delta = 1)
+
+        points = [[90, 0], [0, 0], [0, 90]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 30022685, delta = 1)
+        self.assertAlmostEqual(area, 63758202715511.0, delta = 1)
+        num, perimeter, area = PlanimeterTest.PolyLength(points)
+        self.assertAlmostEqual(perimeter, 20020719, delta = 1)
+        self.assertTrue(Math.isnan(area))
+
+    def test_Planimeter5(self):
+        # Check fix for Planimeter pole crossing bug found 2011-06-24
+        points = [[89, 0.1], [89, 90.1], [89, -179.9]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 539297, delta = 1)
+        self.assertAlmostEqual(area, 12476152838.5, delta = 1)
+
+    def test_Planimeter6(self):
+        # Check fix for Planimeter lon12 rounding bug found 2012-12-03
+        points = [[9, -0.00000000000001], [9, 180], [9, 0]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 36026861, delta = 1)
+        self.assertAlmostEqual(area, 0, delta = 1)
+        points = [[9, 0.00000000000001], [9, 0], [9, 180]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 36026861, delta = 1)
+        self.assertAlmostEqual(area, 0, delta = 1)
+        points = [[9, 0.00000000000001], [9, 180], [9, 0]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 36026861, delta = 1)
+        self.assertAlmostEqual(area, 0, delta = 1)
+        points = [[9, -0.00000000000001], [9, 0], [9, 180]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 36026861, delta = 1)
+        self.assertAlmostEqual(area, 0, delta = 1)
+
+    def test_Planimeter12(self):
+        # Area of arctic circle (not really -- adjunct to rhumb-area test)
+        points = [[66.562222222, 0], [66.562222222, 180]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 10465729, delta = 1)
+        self.assertAlmostEqual(area, 0, delta = 1)
+
+    def test_Planimeter13(self):
+        # Check encircling pole twice
+        points = [[89,-360], [89,-240], [89,-120], [89,0], [89,120], [89,240]]
+        num, perimeter, area = PlanimeterTest.Planimeter(points)
+        self.assertAlmostEqual(perimeter, 1160741, delta = 1)
+        self.assertAlmostEqual(area, 32415230256.0, delta = 1)
diff --git a/src/AlbersEqualArea.cpp b/src/AlbersEqualArea.cpp
index 1b3ff81..ef404d0 100644
--- a/src/AlbersEqualArea.cpp
+++ b/src/AlbersEqualArea.cpp
@@ -25,7 +25,7 @@ namespace GeographicLib {
     , tol_(sqrt(eps_))
     , tol0_(tol_ * sqrt(sqrt(eps_)))
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _e(sqrt(abs(_e2)))
@@ -54,7 +54,7 @@ namespace GeographicLib {
     , tol_(sqrt(eps_))
     , tol0_(tol_ * sqrt(sqrt(eps_)))
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _e(sqrt(abs(_e2)))
@@ -88,7 +88,7 @@ namespace GeographicLib {
     , tol_(sqrt(eps_))
     , tol0_(tol_ * sqrt(sqrt(eps_)))
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _e(sqrt(abs(_e2)))
diff --git a/src/DMS.cpp b/src/DMS.cpp
index 2ea30da..8c46c8c 100644
--- a/src/DMS.cpp
+++ b/src/DMS.cpp
@@ -26,7 +26,6 @@ namespace GeographicLib {
   const string DMS::components_[] = {"degrees", "minutes", "seconds"};
 
   Math::real DMS::Decode(const std::string& dms, flag& ind) {
-    string errormsg;
     string dmsa = dms;
     replace(dmsa, "\xc2\xb0", 'd');      // U+00b0 degree symbol
     replace(dmsa, "\xc2\xba", 'd');      // U+00ba alt symbol
diff --git a/src/Ellipsoid.cpp b/src/Ellipsoid.cpp
index ee0ebf5..6ca38bd 100644
--- a/src/Ellipsoid.cpp
+++ b/src/Ellipsoid.cpp
@@ -16,7 +16,7 @@ namespace GeographicLib {
   Ellipsoid::Ellipsoid(real a, real f)
     : stol_(real(0.01) * sqrt(numeric_limits<real>::epsilon()))
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _f1(1 - _f)
     , _f12(Math::sq(_f1))
     , _e2(_f * (2 - _f))
diff --git a/src/Geocentric.cpp b/src/Geocentric.cpp
index c9b76c5..bb3e8ae 100644
--- a/src/Geocentric.cpp
+++ b/src/Geocentric.cpp
@@ -15,9 +15,9 @@ namespace GeographicLib {
 
   Geocentric::Geocentric(real a, real f)
     : _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _e2(_f * (2 - _f))
-    , _e2m(Math::sq(1 - _f))          // 1 - _e2
+    , _e2m(Math::sq(1 - _f))    // 1 - _e2
     , _e2a(abs(_e2))
     , _e4a(Math::sq(_e2))
     , _maxrad(2 * _a / numeric_limits<real>::epsilon())
diff --git a/src/Geodesic.cpp b/src/Geodesic.cpp
index 65ef554..71435a3 100644
--- a/src/Geodesic.cpp
+++ b/src/Geodesic.cpp
@@ -51,18 +51,18 @@ namespace GeographicLib {
       // which otherwise failed for Visual Studio 10 (Release and Debug)
     , tol1_(200 * tol0_)
     , tol2_(sqrt(tol0_))
-      // Check on bisection interval
-    , tolb_(tol0_ * tol2_)
+    , tolb_(tol0_ * tol2_)      // Check on bisection interval
     , xthresh_(1000 * tol2_)
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _f1(1 - _f)
     , _e2(_f * (2 - _f))
-    , _ep2(_e2 / Math::sq(_f1))       // e2 / (1 - e2)
+    , _ep2(_e2 / Math::sq(_f1)) // e2 / (1 - e2)
     , _n(_f / ( 2 - _f))
     , _b(_a * _f1)
     , _c2((Math::sq(_a) + Math::sq(_b) *
-           Math::eatanhe(real(1), (_f < 0 ? -1 : 1) * sqrt(abs(_e2))) / _e2)
+           (_e2 == 0 ? 1 :
+            Math::eatanhe(real(1), (_f < 0 ? -1 : 1) * sqrt(abs(_e2))) / _e2))
           / 2) // authalic radius squared
       // The sig12 threshold for "really short".  Using the auxiliary sphere
       // solution with dnm computed at (bet1 + bet2) / 2, the relative error in
@@ -151,7 +151,8 @@ namespace GeographicLib {
     lat1 = Math::AngRound(Math::LatFix(lat1));
     lat2 = Math::AngRound(Math::LatFix(lat2));
     // Swap points so that point with higher (abs) latitude is point 1
-    int swapp = abs(lat1) >= abs(lat2) ? 1 : -1;
+    // If one latitude is a nan, then it becomes lat1.
+    int swapp = abs(lat1) < abs(lat2) ? -1 : 1;
     if (swapp < 0) {
       lonsign *= -1;
       swap(lat1, lat2);
@@ -228,7 +229,7 @@ namespace GeographicLib {
         ssig2 = sbet2, csig2 = calp2 * cbet2;
 
       // sig12 = sig2 - sig1
-      sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, real(0)),
+      sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2),
                     csig1 * csig2 + ssig1 * ssig2);
       {
         real dummy;
@@ -794,11 +795,11 @@ namespace GeographicLib {
     // Math::norm(somg2, comg2); -- don't need to normalize!
 
     // sig12 = sig2 - sig1, limit to [0, pi]
-    sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, real(0)),
+    sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2),
                   csig1 * csig2 + ssig1 * ssig2);
 
     // omg12 = omg2 - omg1, limit to [0, pi]
-    omg12 = atan2(max(comg1 * somg2 - somg1 * comg2, real(0)),
+    omg12 = atan2(max(real(0), comg1 * somg2 - somg1 * comg2),
                   comg1 * comg2 + somg1 * somg2);
     real B312, h0;
     real k2 = Math::sq(calp0) * _ep2;
diff --git a/src/GeodesicExact.cpp b/src/GeodesicExact.cpp
index 888b08b..9d11489 100644
--- a/src/GeodesicExact.cpp
+++ b/src/GeodesicExact.cpp
@@ -51,14 +51,13 @@ namespace GeographicLib {
       // which otherwise failed for Visual Studio 10 (Release and Debug)
     , tol1_(200 * tol0_)
     , tol2_(sqrt(tol0_))
-      // Check on bisection interval
-    , tolb_(tol0_ * tol2_)
+    , tolb_(tol0_ * tol2_)      // Check on bisection interval
     , xthresh_(1000 * tol2_)
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _f1(1 - _f)
     , _e2(_f * (2 - _f))
-    , _ep2(_e2 / Math::sq(_f1))       // e2 / (1 - e2)
+    , _ep2(_e2 / Math::sq(_f1)) // e2 / (1 - e2)
     , _n(_f / ( 2 - _f))
     , _b(_a * _f1)
       // The Geodesic class substitutes atanh(sqrt(e2)) for asinh(sqrt(ep2)) in
@@ -156,7 +155,8 @@ namespace GeographicLib {
     lat1 = Math::AngRound(Math::LatFix(lat1));
     lat2 = Math::AngRound(Math::LatFix(lat2));
     // Swap points so that point with higher (abs) latitude is point 1
-    int swapp = abs(lat1) >= abs(lat2) ? 1 : -1;
+    // If one latitude is a nan, then it becomes lat1.
+    int swapp = abs(lat1) < abs(lat2) ? -1 : 1;
     if (swapp < 0) {
       lonsign *= -1;
       swap(lat1, lat2);
@@ -236,7 +236,7 @@ namespace GeographicLib {
         ssig2 = sbet2, csig2 = calp2 * cbet2;
 
       // sig12 = sig2 - sig1
-      sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, real(0)),
+      sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2),
                     csig1 * csig2 + ssig1 * ssig2);
       {
         real dummy;
@@ -803,15 +803,15 @@ namespace GeographicLib {
     // Math::norm(schi2, cchi2); -- don't need to normalize!
 
     // sig12 = sig2 - sig1, limit to [0, pi]
-    sig12 = atan2(max(csig1 * ssig2 - ssig1 * csig2, real(0)),
+    sig12 = atan2(max(real(0), csig1 * ssig2 - ssig1 * csig2),
                   csig1 * csig2 + ssig1 * ssig2);
 
     // omg12 = omg2 - omg1, limit to [0, pi]
-    omg12 = atan2(max(comg1 * somg2 - somg1 * comg2, real(0)),
+    omg12 = atan2(max(real(0), comg1 * somg2 - somg1 * comg2),
                   comg1 * comg2 + somg1 * somg2);
     real k2 = Math::sq(calp0) * _ep2;
     E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2);
-    real chi12 = atan2(max(cchi1 * somg2 - somg1 * cchi2, real(0)),
+    real chi12 = atan2(max(real(0), cchi1 * somg2 - somg1 * cchi2),
                        cchi1 * cchi2 + somg1 * somg2);
     lam12 = chi12 -
       _e2/_f1 * salp0 * E.H() / (Math::pi() / 2) *
diff --git a/src/GeographicLib.pro b/src/GeographicLib.pro
index b9d4a3c..db5f3a0 100644
--- a/src/GeographicLib.pro
+++ b/src/GeographicLib.pro
@@ -1,4 +1,4 @@
-VERSION = 14.2.0
+VERSION = 14.2.1
 
 TEMPLATE = lib
 
diff --git a/src/Gnomonic.cpp b/src/Gnomonic.cpp
index e93ef6c..7d59b49 100644
--- a/src/Gnomonic.cpp
+++ b/src/Gnomonic.cpp
@@ -10,8 +10,9 @@
 #include <GeographicLib/Gnomonic.hpp>
 
 #if defined(_MSC_VER)
-// Squelch warnings about potentially uninitialized local variables
-#  pragma warning (disable: 4701)
+// Squelch warnings about potentially uninitialized local variables and
+// constant conditional expressions
+#  pragma warning (disable: 4701 4127)
 #endif
 
 namespace GeographicLib {
@@ -61,7 +62,7 @@ namespace GeographicLib {
                                   Geodesic::GEODESICSCALE));
     int count = numit_, trip = 0;
     real lat1, lon1, azi1, M;
-    while (count--) {
+    while (count-- || GEOGRAPHICLIB_PANIC) {
       real m, t;
       line.Position(s, lat1, lon1, azi1, m, M, t);
       if (trip)
diff --git a/src/LambertConformalConic.cpp b/src/LambertConformalConic.cpp
index ce44811..3283507 100644
--- a/src/LambertConformalConic.cpp
+++ b/src/LambertConformalConic.cpp
@@ -19,7 +19,7 @@ namespace GeographicLib {
     , epsx_(Math::sq(eps_))
     , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
@@ -44,7 +44,7 @@ namespace GeographicLib {
     , epsx_(Math::sq(eps_))
     , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
@@ -73,7 +73,7 @@ namespace GeographicLib {
     , epsx_(Math::sq(eps_))
     , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _fm(1 - _f)
     , _e2(_f * (2 - _f))
     , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
diff --git a/src/PolarStereographic.cpp b/src/PolarStereographic.cpp
index 0f29322..99e290e 100644
--- a/src/PolarStereographic.cpp
+++ b/src/PolarStereographic.cpp
@@ -15,7 +15,7 @@ namespace GeographicLib {
 
   PolarStereographic::PolarStereographic(real a, real f, real k0)
     : _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _e2(_f * (2 - _f))
     , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
     , _e2m(1 - _e2)
diff --git a/src/TransverseMercator.cpp b/src/TransverseMercator.cpp
index b8adb8b..e3a8d01 100644
--- a/src/TransverseMercator.cpp
+++ b/src/TransverseMercator.cpp
@@ -47,7 +47,7 @@ namespace GeographicLib {
 
   TransverseMercator::TransverseMercator(real a, real f, real k0)
     : _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _k0(k0)
     , _e2(_f * (2 - _f))
     , _es((f < 0 ? -1 : 1) * sqrt(abs(_e2)))
diff --git a/src/TransverseMercatorExact.cpp b/src/TransverseMercatorExact.cpp
index 5092e8b..c334f47 100644
--- a/src/TransverseMercatorExact.cpp
+++ b/src/TransverseMercatorExact.cpp
@@ -57,7 +57,7 @@ namespace GeographicLib {
     , tol2_(real(0.1) * tol_)
     , taytol_(pow(tol_, real(0.6)))
     , _a(a)
-    , _f(f <= 1 ? f : 1/f)
+    , _f(f <= 1 ? f : 1/f)      // f > 1 behavior is DEPRECATED
     , _k0(k0)
     , _mu(_f * (2 - _f))        // e^2
     , _mv(1 - _mu)              // 1 - e^2
diff --git a/tools/tests.cmake b/tools/tests.cmake
index 80c42c4..5a59068 100644
--- a/tools/tests.cmake
+++ b/tools/tests.cmake
@@ -206,6 +206,114 @@ add_test (NAME GeodSolve25 COMMAND GeodSolve
 set_tests_properties (GeodSolve25 PROPERTIES PASS_REGULAR_EXPRESSION
   "89\\.9[0-9]* 0\\.00000000000 0\\.00000000000")
 
+# Check 0/0 problem with area calculation on sphere 2015-09-08
+add_test (NAME GeodSolve26 COMMAND GeodSolve
+  -i -f -e 6.4e6 0 --input-string "1 2 3 4")
+add_test (NAME GeodSolve27 COMMAND GeodSolve
+  -i -f -e 6.4e6 0 --input-string "1 2 3 4" -E)
+set_tests_properties (GeodSolve26 GeodSolve27
+  PROPERTIES PASS_REGULAR_EXPRESSION " 49911046115")
+
+# Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
+# Java implementation fixed on 2015-05-19).
+add_test (NAME GeodSolve28 COMMAND GeodSolve
+  -f -e 6.4e6 0.1 -p 3 --input-string "1 2 10 5e6")
+set_tests_properties (GeodSolve28 PROPERTIES PASS_REGULAR_EXPRESSION
+  " 48.55570690 ")
+
+# Check longitude unrolling with inverse calculation 2015-09-16
+add_test (NAME GeodSolve29 COMMAND GeodSolve
+  -i -f -p 0 --input-string "0 539 0 181")
+add_test (NAME GeodSolve30 COMMAND GeodSolve
+  -i -f -p 0 --input-string "0 539 0 181" -E)
+set_tests_properties (GeodSolve29 GeodSolve30 PROPERTIES PASS_REGULAR_EXPRESSION
+  "0\\..* 179\\..* 90\\..* 0\\..* -179\\..* 90\\..* 222639 ")
+add_test (NAME GeodSolve31 COMMAND GeodSolve
+  -i -f -p 0 --input-string "0 539 0 181" -u)
+add_test (NAME GeodSolve32 COMMAND GeodSolve
+  -i -f -p 0 --input-string "0 539 0 181" -u -E)
+set_tests_properties (GeodSolve31 GeodSolve32 PROPERTIES PASS_REGULAR_EXPRESSION
+  "0\\..* 539\\..* 90\\..* 0\\..* 541\\..* 90\\..* 222639 ")
+
+# Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave --
+# sind(-0.0) = +0.0 -- and in some version of Visual Studio --
+# fmod(-0.0, 360.0) = +0.0.
+add_test (NAME GeodSolve33 COMMAND GeodSolve
+  -i -p 0 --input-string "0 0 0 179")
+add_test (NAME GeodSolve34 COMMAND GeodSolve
+  -i -p 0 --input-string "0 0 0 179" -E)
+add_test (NAME GeodSolve35 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179.5")
+add_test (NAME GeodSolve36 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179.5" -E)
+add_test (NAME GeodSolve37 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180")
+add_test (NAME GeodSolve38 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180" -E)
+add_test (NAME GeodSolve39 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180")
+add_test (NAME GeodSolve40 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180" -E)
+add_test (NAME GeodSolve41 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179" -e 6.4e6 0)
+add_test (NAME GeodSolve42 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179" -e 6.4e6 0 -E)
+add_test (NAME GeodSolve43 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180" -e 6.4e6 0)
+add_test (NAME GeodSolve44 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180" -e 6.4e6 0 -E)
+add_test (NAME GeodSolve45 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180" -e 6.4e6 0)
+add_test (NAME GeodSolve46 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180" -e 6.4e6 0 -E)
+add_test (NAME GeodSolve47 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179" -e 6.4e6 -1/300)
+add_test (NAME GeodSolve48 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 179" -e 6.4e6 -1/300 -E)
+add_test (NAME GeodSolve49 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180" -e 6.4e6 -1/300)
+add_test (NAME GeodSolve50 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0 180" -e 6.4e6 -1/300 -E)
+add_test (NAME GeodSolve51 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0.5 180" -e 6.4e6 -1/300)
+add_test (NAME GeodSolve52 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 0.5 180" -e 6.4e6 -1/300 -E)
+add_test (NAME GeodSolve53 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180" -e 6.4e6 -1/300)
+add_test (NAME GeodSolve54 COMMAND GeodSolve
+ -i -p 0 --input-string "0 0 1 180" -e 6.4e6 -1/300 -E)
+set_tests_properties (GeodSolve33 GeodSolve34
+  PROPERTIES PASS_REGULAR_EXPRESSION "90\\.00000 90\\.00000 19926189")
+set_tests_properties (GeodSolve35 GeodSolve36
+  PROPERTIES PASS_REGULAR_EXPRESSION "55\\.96650 124\\.03350 19980862")
+set_tests_properties (GeodSolve37 GeodSolve38
+  PROPERTIES PASS_REGULAR_EXPRESSION "0\\.00000 -180\\.00000 20003931")
+set_tests_properties (GeodSolve39 GeodSolve40
+  PROPERTIES PASS_REGULAR_EXPRESSION "0\\.00000 -180\\.00000 19893357")
+set_tests_properties (GeodSolve41 GeodSolve42
+  PROPERTIES PASS_REGULAR_EXPRESSION "90\\.00000 90\\.00000 19994492")
+set_tests_properties (GeodSolve43 GeodSolve44
+  PROPERTIES PASS_REGULAR_EXPRESSION "0\\.00000 -180\\.00000 20106193")
+set_tests_properties (GeodSolve45 GeodSolve46
+  PROPERTIES PASS_REGULAR_EXPRESSION "0\\.00000 -180\\.00000 19994492")
+set_tests_properties (GeodSolve47 GeodSolve48
+  PROPERTIES PASS_REGULAR_EXPRESSION "90\\.00000 90\\.00000 19994492")
+set_tests_properties (GeodSolve49 GeodSolve50
+  PROPERTIES PASS_REGULAR_EXPRESSION "90\\.00000 90\\.00000 20106193")
+set_tests_properties (GeodSolve51 GeodSolve52
+  PROPERTIES PASS_REGULAR_EXPRESSION "33\\.02493 146\\.97364 20082617")
+set_tests_properties (GeodSolve53 GeodSolve54
+  PROPERTIES PASS_REGULAR_EXPRESSION "0\\.00000 -180\\.00000 20027270")
+
+# Check fix for nan + point on equator or pole not returning all nans in
+# Geodesic::Inverse, found 2015-09-23.
+add_test (NAME GeodSolve55 COMMAND GeodSolve -i --input-string "nan 0 0 90")
+add_test (NAME GeodSolve56 COMMAND GeodSolve -i --input-string "nan 0 0 90" -E)
+add_test (NAME GeodSolve57 COMMAND GeodSolve -i --input-string "nan 0 90 9")
+add_test (NAME GeodSolve58 COMMAND GeodSolve -i --input-string "nan 0 90 9" -E)
+set_tests_properties (GeodSolve55 GeodSolve56 GeodSolve57 GeodSolve58
+  PROPERTIES PASS_REGULAR_EXPRESSION "nan nan nan")
+
 # Check fix for pole-encircling bug found 2011-03-16
 add_test (NAME Planimeter0 COMMAND Planimeter
   --input-string "89 0;89 90;89 180;89 270")
@@ -216,9 +324,7 @@ add_test (NAME Planimeter2 COMMAND Planimeter
 add_test (NAME Planimeter3 COMMAND Planimeter --input-string "90 0; 0 0; 0 90")
 add_test (NAME Planimeter4 COMMAND Planimeter
   -l --input-string "90 0; 0 0; 0 90")
-set_tests_properties (Planimeter0 PROPERTIES PASS_REGULAR_EXPRESSION
-  "4 631819\\.8745[0-9]+ 2495230567[78]\\.[0-9]+")
-set_tests_properties (Planimeter1 PROPERTIES PASS_REGULAR_EXPRESSION
+set_tests_properties (Planimeter0 Planimeter1 PROPERTIES PASS_REGULAR_EXPRESSION
   "4 631819\\.8745[0-9]+ 2495230567[78]\\.[0-9]+")
 set_tests_properties (Planimeter2 PROPERTIES PASS_REGULAR_EXPRESSION
   "4 627598\\.2731[0-9]+ 24619419146.[0-9]+")

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/geographiclib.git



More information about the Pkg-grass-devel mailing list