Coverage for pygeodesy/elliptic.py : 99%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
Class L{Elliptic} transcoded from I{Charles Karney}'s C++ class U{EllipticFunction <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1EllipticFunction.html>} to pure Python, including symmetric integrals L{Elliptic.fRC}, L{Elliptic.fRD}, L{Elliptic.fRF}, L{Elliptic.fRG} and L{Elliptic.fRJ} as C{static methods}.
Python method names follow the C++ member functions, I{except}:
- member functions I{without arguments} are mapped to Python properties prefixed with C{"c"}, for example C{E()} is property C{cE},
- member functions with 1 or 3 arguments are renamed to Python methods starting with an C{"f"}, example C{E(psi)} to C{fE(psi)} and C{E(sn, cn, dn)} to C{fE(sn, cn, dn)},
- other Python method names conventionally start with a lower-case letter or an underscore if private.
Following is a copy of I{Karney}'s U{EllipticFunction.hpp <https://GeographicLib.SourceForge.io/C++/doc/EllipticFunction_8hpp_source.html>} file C{Header}.
Copyright (C) U{Charles Karney<mailto:Charles@Karney.com>} (2008-2022) and licensed under the MIT/X11 License. For more information, see the U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
B{Elliptic integrals and functions.}
This provides the elliptic functions and integrals needed for C{Ellipsoid}, C{GeodesicExact}, and C{TransverseMercatorExact}. Two categories of function are provided:
- functions to compute U{symmetric elliptic integrals <https://DLMF.NIST.gov/19.16.i>}
- methods to compute U{Legrendre's elliptic integrals <https://DLMF.NIST.gov/19.2.ii>} and U{Jacobi elliptic functions<https://DLMF.NIST.gov/22.2>}.
In the latter case, an object is constructed giving the modulus C{k} (and optionally the parameter C{alpha}). The modulus (and parameter) are always passed as squares which allows C{k} to be pure imaginary. (Confusingly, Abramowitz and Stegun call C{m = k**2} the "parameter" and C{n = alpha**2} the "characteristic".)
In geodesic applications, it is convenient to separate the incomplete integrals into secular and periodic components, e.g.
I{C{E(phi, k) = (2 E(k) / pi) [ phi + delta E(phi, k) ]}}
where I{C{delta E(phi, k)}} is an odd periodic function with period I{C{pi}}.
The computation of the elliptic integrals uses the algorithms given in U{B. C. Carlson, Computation of real or complex elliptic integrals <https://DOI.org/10.1007/BF02198293>} (also available U{here <https://ArXiv.org/pdf/math/9409227.pdf>}), Numerical Algorithms 10, 13--26 (1995) with the additional optimizations given U{here <https://DLMF.NIST.gov/19.36.i>}.
The computation of the Jacobi elliptic functions uses the algorithm given in U{R. Bulirsch, Numerical Calculation of Elliptic Integrals and Elliptic Functions<https://DOI.org/10.1007/BF01397975>}, Numerische Mathematik 7, 78--90 (1965).
The notation follows U{NIST Digital Library of Mathematical Functions <https://DLMF.NIST.gov>} chapters U{19<https://DLMF.NIST.gov/19>} and U{22<https://DLMF.NIST.gov/22>}. ''' # make sure int/int division yields float quotient, see .basics
_0_0, _0_125, _0_25, _0_5, _1_0, _1_64th, _2_0, \ _N_2_0, _3_0, _4_0, _6_0, _8_0, _180_0, _360_0 # from pygeodesy.fsums import Fsum # from .fmath # from pygeodesy.lazily import _ALL_LAZY # from .karney _update_all
'''(INTERAL) Hold complete integrals. '''
'''(INTERNAL) Collector for L{_Deferred_Fsum}. '''
def __add__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
# assert isscalar(x)
def __imul__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
def __isub__(self, x): # PYCHOK no cover # assert isscalar(x) self.append(-float(x)) return self
def __mul__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
def __radd__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
def __rsub__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
def __sub__(self, other): # PYCHOK no cover return _NotImplemented(self, other)
# get a L{_Deferred_Fsum} instance, pre-named
'''(INTERNAL) Deferred L{Fsum}. '''
# ... waste time zapping non-existing Property/_ROs Fsum._update(self, **other)
'''Elliptic integrals and functions.
@see: I{Karney}'s U{Detailed Description<https://GeographicLib.SourceForge.io/ html/classGeographicLib_1_1EllipticFunction.html#details>}. '''
'''Constructor, specifying the C{modulus} and C{parameter}.
@see: Method L{Elliptic.reset} for further details.
@note: If only elliptic integrals of the first and second kinds are needed, use C{B{alpha2}=0}, the default value. In that case, we have C{Π(φ, 0, k) = F(φ, k), G(φ, 0, k) = E(φ, k)} and C{H(φ, 0, k) = F(φ, k) - D(φ, k)}. '''
'''Get α^2, the square of the parameter (C{float}). '''
'''Get α'^2, the square of the complementary parameter (C{float}). '''
'''Get Jahnke's complete integral C{D(k)} (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E6>}. '''
'''Get the complete integral of the second kind C{E(k)} (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E5>}. '''
'''Get Legendre's complete geodesic longitude integral C{G(α^2, k)} (C{float}). '''
'''Get Cayley's complete geodesic longitude difference integral C{H(α^2, k)} (C{float}). '''
'''Get the complete integral of the first kind C{K(k)} (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E4>}. '''
'''Get the difference between the complete integrals of the first and second kinds, C{K(k) − E(k)} (C{float}). '''
'''Get the complete integral of the third kind C{Pi(α^2, k)} (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E7>}. '''
'''The periodic Jahnke's incomplete elliptic integral.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π D(φ, k) / (2 D(k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''The periodic incomplete integral of the second kind.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π E(φ, k) / (2 E(k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''The periodic inverse of the incomplete integral of the second kind.
@arg stau: sin(τ) @arg ctau: cos(τ)
@return: Periodic function E^−1(τ (2 E(k)/π), k) - τ (C{float}).
@raise EllipticError: No convergence. ''' # Function is periodic with period pi
'''The periodic incomplete integral of the first kind.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π F(φ, k) / (2 K(k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''Legendre's periodic geodesic longitude integral.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π G(φ, k) / (2 G(k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''Cayley's periodic geodesic longitude difference integral.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π H(φ, k) / (2 H(k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''The periodic incomplete integral of the third kind.
@arg sn: sin(φ). @arg cn: cos(φ). @arg dn: sqrt(1 − k2 * sin(2φ)).
@return: Periodic function π Π(φ, α2, k) / (2 Π(α2, k)) - φ (C{float}).
@raise EllipticError: Invalid invokation or no convergence. '''
'''(INTERNAL) Helper for C{.deltaD} thru C{.deltaPi}. ''' raise _invokationError(n, sn, cn, dn)
'''Get epsilon (C{float}). '''
'''Jahnke's incomplete elliptic integral in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: D(φ, k) as though φ ∈ (−π, π] (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E4>}.
@raise EllipticError: Invalid invokation or no convergence. '''
self.deltaD, _fD)
'''The C{Delta} amplitude function.
@arg sn: sin(φ). @arg cn: cos(φ).
@return: sqrt(1 − k2 * sin(2φ)) (C{float}). ''' + ((k2 * cn**2) if k2 > 0 else _0_0))
'''The incomplete integral of the second kind in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: E(φ, k) as though φ ∈ (−π, π] (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E5>}.
@raise EllipticError: Invalid invokation or no convergence. ''' '''(INTERNAL) Core of C{.fE}. ''' _RF3(self, cn2, dn2, _1_0)) * kp2 else: # <https://DLMF.NIST.gov/19.25.E11> ei = dn / abs(cn) ei -= _RD(self, dn2, _1_0, cn2, _3over(kp2, sn2)) else: # PYCHOK no cover ei = _0_0
self.deltaE, _fE)
'''The incomplete integral of the second kind with the argument given in degrees.
@arg deg: Angle (C{degrees}).
@return: E(π B{C{deg}}/180, k) (C{float}).
@raise EllipticError: No convergence. ''' else: # PYCHOK no cover e = ceil(deg / _360_0 - _0_5) deg -= e * _360_0 e *= self.cE * _4_0
'''The inverse of the incomplete integral of the second kind.
@arg x: Argument (C{float}).
@return: φ = 1 / E(B{C{x}}, k), such that E(φ, k) = B{C{x}} (C{float}).
@raise EllipticError: No convergence. ''' # linear approximation # first order correction # For kp2 close to zero use asin(x/.cE) or J. P. Boyd, # Applied Math. and Computation 218, 7005-7013 (2012) # <https://DOI.org/10.1016/j.amc.2011.12.021> else: # PYCHOK no cover raise _no_convergenceError(e, _TolJAC, self.fEinv, x)
'''The incomplete integral of the first kind in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: F(φ, k) as though φ ∈ (−π, π] (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E4>}.
@raise EllipticError: Invalid invokation or no convergence. '''
self.deltaF, _fF)
'''Legendre's geodesic longitude integral in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: G(φ, k) as though φ ∈ (−π, π] (C{float}).
@raise EllipticError: Invalid invokation or no convergence.
@note: Legendre expresses the longitude of a point on the geodesic in terms of this combination of elliptic integrals in U{Exercices de Calcul Intégral, Vol 1 (1811), p 181<https://Books.Google.com/books?id= riIOAAAAQAAJ&pg=PA181>}.
@see: U{Geodesics in terms of elliptic integrals<https:// GeographicLib.SourceForge.io/html/geodesic.html#geodellip>} for the expression for the longitude in terms of this function. ''' self.cG, self.deltaG)
'''Cayley's geodesic longitude difference integral in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: H(φ, k) as though φ ∈ (−π, π] (C{float}).
@raise EllipticError: Invalid invokation or no convergence.
@note: Cayley expresses the longitude difference of a point on the geodesic in terms of this combination of elliptic integrals in U{Phil. Mag. B{40} (1870), p 333 <https://Books.Google.com/books?id=Zk0wAAAAIAAJ&pg=PA333>}.
@see: U{Geodesics in terms of elliptic integrals<https:// GeographicLib.SourceForge.io/html/geodesic.html#geodellip>} for the expression for the longitude in terms of this function. ''' self.cH, self.deltaH)
'''The incomplete integral of the third kind in terms of Jacobi elliptic functions.
@arg phi_or_sn: φ or sin(φ). @kwarg cn: C{None} or cos(φ). @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
@return: Π(φ, α2, k) as though φ ∈ (−π, π] (C{float}).
@raise EllipticError: Invalid invokation or no convergence. ''' self.cPi, self.deltaPi)
'''(INTERNAL) Helper for C{.fG}, C{.fH} and C{.fPi}. ''' else: # PYCHOK no cover r = _0_0
'''(INTERNAL) Helper for C{f.D}, C{.fE}, C{.fF} and C{._fXa}. ''' if abs(phi) >= PI: # PYCHOK no cover return (deltaX(sn, cn, dn) + phi) * cX / PI_2 # fall through raise _invokationError(n, sn, cn, dn)
else:
'''Get k^2, the square of the modulus (C{float}). '''
'''Get k'^2, the square of the complementary modulus (C{float}). '''
'''Reset the modulus, parameter and the complementaries.
@kwarg k2: Modulus squared (C{float}, NINF <= k^2 <= 1). @kwarg alpha2: Parameter squared (C{float}, NINF <= α^2 <= 1). @kwarg kp2: Complementary modulus squared (C{float}, k'^2 >= 0). @kwarg alphap2: Complementary parameter squared (C{float}, α'^2 >= 0).
@raise EllipticError: Invalid B{C{k2}}, B{C{alpha2}}, B{C{kp2}} or B{C{alphap2}}.
@note: The arguments must satisfy C{B{k2} + B{kp2} = 1} and C{B{alpha2} + B{alphap2} = 1}. No checking is done that these conditions are met to enable accuracy to be maintained, e.g., when C{k} is very close to unity. '''
Error=EllipticError)
# Values of complete elliptic integrals for k = 0,1 and alpha = 0,1 # K E D # k = 0: pi/2 pi/2 pi/4 # k = 1: inf 1 inf # Pi G H # k = 0, alpha = 0: pi/2 pi/2 pi/4 # k = 1, alpha = 0: inf 1 1 # k = 0, alpha = 1: inf inf pi/2 # k = 1, alpha = 1: inf inf inf # # G(0, k) = Pi(0, k) = H(1, k) = E(k) # H(0, k) = K(k) - D(k) # Pi(alpha2, 0) = G(alpha2, 0) = pi / (2 * sqrt(1 - alpha2)) # H( alpha2, 0) = pi / (2 * (sqrt(1 - alpha2) + 1)) # Pi(alpha2, 1) = inf # G( alpha2, 1) = H(alpha2, 1) = RC(1, alphap2)
'''(INTERNAL) Get the complete integrals D, E, K and KE plus C{eps}. ''' # D(k) = (K(k) - E(k))/k2, Carlson eq.4.3 # <https://DLMF.NIST.gov/19.25.E1> # Complete elliptic integral E(k), Carlson eq. 4.2 # <https://DLMF.NIST.gov/19.25.E1> # Complete elliptic integral K(k), Carlson eq. 4.1 # <https://DLMF.NIST.gov/19.25.E1> else: # PYCHOK no cover cD = cK = cKE = INF cE = _1_0 eps = k2 else: # PYCHOK no cover cD = PI_4 cE = cK = PI_2 cKE = _0_0 # k2 * cD eps = EPS
'''(INTERNAL) Get the complete integrals G, H and Pi. ''' else: # PYCHOK no cover cG = cH = _RC(self, _1_0, alphap2) cPi = INF # XXX or NAN? else: # PYCHOK no cover cG = cH = cPi = INF # XXX or NAN? else: # H = K - D but this involves large cancellations if k2 is near 1. # So write (for alpha2 = 0) # H = int(cos(phi)**2/sqrt(1-k2*sin(phi)**2),phi,0,pi/2) # = 1/sqrt(1-k2) * int(sin(phi)**2/sqrt(1-k2/kp2*sin(phi)**2,...) # = 1/kp * D(i*k/kp) # and use D(k) = RD(0, kp2, 1) / 3 # so H = 1/kp * RD(0, 1/kp2, 1) / 3 # = kp2 * RD(0, 1, kp2) / 3 # using <https://DLMF.NIST.gov/19.20.E18>. Equivalently # RF(x, 1) - RD(0, x, 1)/3 = x * RD(0, 1, x)/3 for x > 0 # For k2 = 1 and alpha2 = 0, we have # H = int(cos(phi),...) = 1
'''The Jacobi elliptic function.
@arg x: The argument (C{float}).
@return: An L{Elliptic3Tuple}C{(sn, cn, dn)} with C{*n(B{x}, k)}.
@raise EllipticError: No convergence. ''' # Bulirsch's sncndn routine, p 89. if d and _signBit(self.kp2): # PYCHOK no cover cn, dn = dn, cn sn = sn / d # /= chokes PyChecker else:
'''(INTERNAL) Get Bulirsch' 4-tuple C{(c, d, cd, mn_)}. ''' # Bulirsch's sncndn routine, p 89. if _signBit(mc): # PYCHOK no cover d = _1_0 - mc mc = neg(mc / d) d = sqrt(d)
# This converges quadratically, max 6 trips else: # PYCHOK no cover raise _no_convergenceError(a - mc, t, None, kp=self.kp, kp2=self.kp2)
'''(INTERNAL) Helper for C{.fEinv} and C{._fXf}. '''
'''Degenerate symmetric integral of the first kind C{RC(x, y)}.
@return: C{RC(x, y)}, equivalent to C{RF(x, y, y)}.
@see: U{C{RC} definition<https://DLMF.NIST.gov/19.2.E17>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. '''
'''Degenerate symmetric integral of the third kind C{RD(x, y, z)}.
@return: C{RD(x, y, z)}, equivalent to C{RJ(x, y, z, z)}.
@see: U{C{RD} definition<https://DLMF.NIST.gov/19.16.E5>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. '''
'''Symmetric or complete symmetric integral of the first kind C{RF(x, y, z)} respectively C{RF(x, y)}.
@return: C{RF(x, y, z)} or C{RF(x, y)} for missing or zero B{C{z}}.
@see: U{C{RF} definition<https://DLMF.NIST.gov/19.16.E1>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. '''
'''Symmetric or complete symmetric integral of the second kind C{RG(x, y, z)} respectively C{RG(x, y)}.
@return: C{RG(x, y, z)} or C{RG(x, y)} for missing or zero B{C{z}}.
@see: U{C{RG} definition<https://DLMF.NIST.gov/19.16.E3>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. ''' _RG2(None, x, y) * _0_5)
'''Symmetric integral of the third kind C{RJ(x, y, z, p)}.
@return: C{RJ(x, y, z, p)}.
@see: U{C{RJ} definition<https://DLMF.NIST.gov/19.16.E2>} and U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}. '''
'''Elliptic integral, function, convergence or other L{Elliptic} issue. '''
'''3-Tuple C{(sn, cn, dn)} all C{scalar}. '''
'''(INTERNAL) Helper for C{_RD}, C{_RF3} and C{_RJ}. '''
'''Compute the initial C{a}. '''
'''Compute next C{a}, C{sqrt(xyz_)} and C{fdot(sqrt(xyz))}. ''' # assert a is L._a # assert L is self
'''Rescale C{x}, C{y}, ... '''
'''Return the convergence threshold. '''
'''(INTERNAL) Horner form for C{_RD} and C{_RJ} below. ''' # Polynomial is <https://DLMF.NIST.gov/19.36.E2> # (1 - 3*E2/14 + E3/6 + 9*E2**2/88 - 3*E4/22 - 9*E2*E3/52 # + 3*E5/26 - E2**3/16 + 3*E3**2/40 + 3*E2*E4/20 # + 45*E2**2*E3/272 - 9*(E3*E4+E2*E5)/68) # converted to Horner-like form ...
def _invokationError(name, *args): # PYCHOK no cover '''(INTERNAL) Return an L{EllipticError}. ''' n = _DOT_(Elliptic.__name__, name) n = _SPACE_(_invokation_, n) return EllipticError(NN(n, repr(args))) # unstr
'''(INTERNAL) Aggregate iterations B{C{i}}. '''
def _no_convergenceError(d, tol, where, *args, **kwds_thresh): # PYCHOK no cover '''(INTERNAL) Return an L{EllipticError}. ''' n = Elliptic.__name__ if where: n = _DOT_(n, where.__name__) if kwds_thresh: q = _xkwds_pop(kwds_thresh, thresh=False) t = unstr(n, *args, **kwds_thresh) else: q = False t = unstr(n, *args) return EllipticError(Fmt.no_convergence(d, tol, thresh=q), txt=t)
'''(INTERNAL) Return C{3 / (a * b)}. '''
'''(INTERNAL) Defined only for y != 0 and x >= 0. ''' # <https://DLMF.NIST.gov/19.2.E18> else: raise _invokationError(Elliptic.fRC.__name__, x, y)
'''(INTERNAL) Carlson, eqs 2.28 - 2.34. ''' else: # PYCHOK no cover raise _no_convergenceError(am, q, Elliptic.fRD, x, y, z, *over, thresh=True) xy - _6_0 * z2, (xy * _3_0 - _8_0 * z2) * z, (xy - z2) * _3_0 * z2, xy * z2 * z, *over)
'''(INTERNAL) Carlson, eqs 2.36 - 2.38. ''' else: # PYCHOK no cover raise _no_convergenceError(a - b, t, Elliptic.fRF, x, y)
'''(INTERNAL) Carlson, eqs 2.2 - 2.7. ''' else: # PYCHOK no cover raise _no_convergenceError(am, q, Elliptic.fRF, x, y, z, thresh=True) # Polynomial is <https://DLMF.NIST.gov/19.36.E1> # (1 - E2/10 + E3/14 + E2**2/24 - 3*E2*E3/44 # - 5*E2**3/208 + 3*E3**2/104 + E2**2*E3/16) # converted to Horner-like form ...
'''(INTERNAL) Carlson, eqs 2.36 - 2.39. ''' else: # PYCHOK no cover raise _no_convergenceError(ab, t, Elliptic.fRG, x, y)
'''(INTERNAL) Never called with zero B{C{z}}, see C{.fRG}. ''' # if not z: # y, z = z, y
'''(INTERNAL) Carlson, eqs 2.17 - 2.25. '''
else: else: # PYCHOK no cover raise _no_convergenceError(am, q, Elliptic.fRJ, x, y, z, p, thresh=True) Fsum(p3 * _4_0, xyz, E2p * _2_0), Fsum(p3 * _3_0, E2p, xyz * _2_0).fmul(p), xyz * p2, *over)
# **) MIT License # # Copyright (C) 2016-2022 -- mrJean1 at Gmail -- All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. |