1# -*- coding: utf-8 -*-

2

3 u'''Single-instance C{float} and C{int} constants across C{pygeodesy}

4 modules and related functions L{pygeodesy.float_}, L{pygeodesy.isclose},

5 L{pygeodesy.isfinite}, L{pygeodesy.isinf}, L{pygeodesy.isint0},

6 L{pygeodesy.isnan}, L{pygeodesy.isnear0}, L{pygeodesy.isnear1},

7 L{pygeodesy.isnear90}, L{pygeodesy.isneg0}, L{pygeodesy.isninf},

8 L{pygeodesy.isnon0} and L{pygeodesy.remainder}.

9'''

10# make sure int/int division yields float quotient, see .basics

11 from __future__ import division as _; del _ # PYCHOK semicolon

12

13 from pygeodesy.basics import _0_0, _copysign, isbool, iscomplex, isint

14 from pygeodesy.errors import _xError, _xError2, _xkwds_get, _ALL_LAZY

15 from pygeodesy.interns import _INF_, _NAN_, _UNDER_

16# from pygeodesy.lazily import _ALL_LAZY # from .errors

17# from pygeodesy.streprs import Fmt # from .unitsBase

18 from pygeodesy.unitsBase import Float, Int, Radius, Fmt

19

20 from math import fabs, isinf, isnan, pi as _PI, sqrt

21 try:

22 from math import inf as _inf, nan as _nan # PYCHOK Python 3+

23 except ImportError: # Python 2-

24 _inf, _nan = float(_INF_), float(_NAN_)

25 try:

26 from math import log2 as _log2

27 except ImportError: # Python 3.3-

28 from math import log as _log

29

30 def _log2(x): # in .rhumbaux, .auxilats.auxLat

31 return _log(x, 2)

32

33__all__ = _ALL_LAZY.constants

34__version__ = '23.08.05'

35

36

37 def _copysign_0_0(y):

38 '''(INTERNAL) copysign(0.0, y), only C{float}.

39 '''

40 return _N_0_0 if y < 0 else _0_0

41

42

43 def _copysign_1_0(y):

44 '''(INTERNAL) copysign(1.0, y), only C{float}.

45 '''

46 return _N_1_0 if y < 0 else _1_0

47

48

49 def _copysignINF(y):

50 '''(INTERNAL) copysign(INF, y), only C{float}.

51 '''

52 return NINF if y < 0 else INF

53

54

55 def _Float(**name_arg):

56 '''(INTERNAL) New named, cached C{Float}.

57 '''

58 n, arg = name_arg.popitem()

59 return Float(_float(arg), name=n)

60

61

62 def _Radius(**name_arg):

63 '''(INTERNAL) New named, cached C{Radius}.

64 '''

65 n, arg = name_arg.popitem()

66 return Radius(_float(arg), name=n)

67

68

69 def float_(*fs, **sets): # sets=False

70 '''Get scalars as C{float} or I{intern}'ed C{float}.

71

72 @arg fs: One more values (C{scalar}), all positional.

73 @kwarg sets: Use C{B{sets}=True} to C{intern} each

74 B{C{fs}}, otherwise don't C{intern}.

75

76 @return: A single C{float} if only one B{C{fs}} is

77 given, otherwise a tuple of C{float}s.

78

79 @raise TypeError: Some B{C{fs}} is not C{scalar}.

80 '''

81 fl = []

82 _a = fl.append

83 _f = _floats.setdefault if _xkwds_get(sets, sets=False) else \

84 _floats.get

85 try:

86 for i, f in enumerate(fs):

87 f = float(f)

88 _a(_f(f, f))

89 except Exception as x:

90 E, t = _xError2(x)

91 fs_i = Fmt.SQUARE(fs=i)

92 raise E(fs_i, f, txt=t)

93 return fl[0] if len(fl) == 1 else tuple(fl)

94

95

96 def _float(f): # in .datums, .ellipsoids, ...

97 '''(INTERNAL) Cache initial C{float}s.

98 '''

99 f = float(f)

100 return _floats.setdefault(f, f) # PYCHOK del _floats

101

102

103 def float0_(*xs):

104 '''Yield C{B{x}s} as a non-NEG0 C{float}.

105 '''

106 for x in xs:

107 yield float(x) if x else _0_0

108

109

110 def _float0(f): # in .resections, .vector3dBase, ...

111 '''(INTERNAL) Return C{float(B{f})} or C{INT0}.

112 '''

113 if f:

114 f = float(f)

115 f = _floats.get(f, f)

116 elif f is not INT0:

117 f = _0_0

118 return f

119

120

121 def _floatuple(*fs):

122 '''(INTERNAL) Cache a tuple of C{float}s.

123 '''

124 return tuple(map(_float, fs))

125

126

127 def _over(p, q):

128 '''(INTERNAL) Return C{B{p} / B{q}} avoiding ZeroDivisionError exceptions.

129 '''

130 r = (-p) if q < 0 else p

131 if q:

132 if isinf(q):

133 r = _copysign_0_0(r) if isfinite(r) else NAN

134 elif p:

135 r = p / q

136 else:

137 r = _copysignINF(r) if isfinite(r) else NAN

138 return r

139

140

141 def _1_over(x):

142 '''(INTERNAL) Return reciprocal C{1 / B{x}} avoiding ZeroDivisionError exceptions.

143 '''

144 return _over(_1_0, float(x))

145

146

147_floats = {} # PYCHOK floats cache, in .__main__

148# _float = float # PYCHOK expected

149# del _floats # XXX zap floats cache never

150

151_0_0 = _float( _0_0) # PYCHOK expected

152_0_0_1T = _0_0, # PYCHOK 1-tuple

153_0_0_9T = _0_0_1T * 9 # PYCHOK 9-tuple

154_0_0001 = _float( 0.0001) # PYCHOK expected

155_0_001 = _float( 0.001) # PYCHOK expected

156_0_01 = _float( 0.01) # PYCHOK expected

157_0_1 = _float( 0.1) # PYCHOK expected

158_0_125 = _float( 0.125) # PYCHOK expected

159_0_25 = _float( 0.25) # PYCHOK expected

160_0_26 = _float( 0.26) # PYCHOK expected

161_0_5 = _float( 0.5) # PYCHOK expected

162_1_0 = _float( 1) # PYCHOK expected

163_1_0_1T = _1_0, # PYCHOK 1-tuple

164_1_5 = _float( 1.5) # PYCHOK expected

165_1_75 = _float( 1.75) # PYCHOK expected

166_2_0 = _float( 2) # PYCHOK expected

167_3_0 = _float( 3) # PYCHOK expected

168_4_0 = _float( 4) # PYCHOK expected

169_5_0 = _float( 5) # PYCHOK expected

170_6_0 = _float( 6) # PYCHOK expected

171_8_0 = _float( 8) # PYCHOK expected

172_9_0 = _float( 9) # PYCHOK expected

173_10_0 = _float( 10) # PYCHOK expected

174_16_0 = _float( 16) # PYCHOK expected

175_32_0 = _float( 32) # PYCHOK expected

176_60_0 = _float( 60) # PYCHOK expected

177_90_0 = _float( 90) # PYCHOK expected

178_100_0 = _float( 100) # PYCHOK expected

179_180_0 = _float( 180) # PYCHOK expected

180_270_0 = _float( 270) # PYCHOK expected

181_360_0 = _float( 360) # PYCHOK expected

182_400_0 = _float( 400) # PYCHOK expected

183_720_0 = _float( 720) # PYCHOK expected

184_1000_0 = _float(1000) # PYCHOK expected

185_3600_0 = _float(3600) # PYCHOK expected

186

187_N_0_0 = float( '-0.0') # PYCHOK NOT _float!

188_N_0_5 = _float( -_0_5) # PYCHOK expected

189_N_1_0 = _float( -_1_0) # PYCHOK expected

190_N_2_0 = _float( -_2_0) # PYCHOK expected

191_N_90_0 = _float( -_90_0) # PYCHOK expected

192_N_180_0 = _float(-_180_0) # PYCHOK expected

193

194_M_KM = _1000_0 # meter per Kilo meter, see .utily

195_M_NM = _float(1852.0) # meter per Nautical Mile

196_M_SM = _float(1609.344) # meter per Statute Mile

197

198 try:

199 from sys import float_info as _f_i

200 # @see: <https://NumPy.org/doc/stable/reference/generated/numpy.finfo.html>

201 DIG = Int( DIG =_f_i.dig) # PYCHOK system's float decimal digits

202 EPS = _Float(EPS =_f_i.epsilon) # PYCHOK system's EPSilon

203 MANT_DIG = Int( MANT_DIG=_f_i.mant_dig) # PYCHOK system's float mantissa bits

204 MAX = _Float(MAX =_f_i.max) # PYCHOK system's MAX float 1.7976931348623157e+308

205 MAX_EXP = Int( MAX_EXP =_f_i.max_exp) # PYTHON system's max base 2 exponent

206 MIN = _Float(MIN =_f_i.min) # PYCHOK system's MIN float 2.2250738585072014e-308

207 MIN_EXP = Int( MIN_EXP =_f_i.min_exp) # PYTHON system's min base 2 exponent

208# RADIX = Int( RADIX =_f_i.radix) # PYTHON system's float base

209 del _f_i

210 except ImportError: # PYCHOK no cover

211 DIG = Int( DIG =15) # PYCHOK system's 64-bit float decimal digits

212 EPS = _Float(EPS =2.220446049250313e-16) # PYCHOK EPSilon 2**-52, M{EPS +/- 1 != 1}

213 MANT_DIG = Int( MANT_DIG=53) # PYCHOK float mantissa bits ≈ 53 (C{int})

214 MAX = _Float(MAX =pow(_2_0, 1023) * (_2_0 - EPS)) # PYCHOK ≈ 10**308

215 MAX_EXP = Int( MAX_ESP =_log2(MAX)) # 308 base 10

216 MIN = _Float(MIN =pow(_2_0, -1021)) # PYCHOK ≈ 10**-308

217 MIN_EXP = Int(MIN_EXP =_log2(MIN)) # -307 base 10

218# RADIX = Int(Radix =2) # base

219

220 EPS0 = _Float( EPS0 = EPS**2) # PYCHOK near-/non-zero comparison 4.930381e-32, or EPS or EPS_2

221 EPS02 = _Float( EPS02 = EPS**4) # PYCHOK near-zero-squared comparison 2.430865e-63

222 EPS_2 = _Float( EPS_2 = EPS / _2_0) # PYCHOK ≈ 1.110223024625e-16

223 EPS1 = _Float( EPS1 =_1_0 - EPS) # PYCHOK ≈ 0.9999999999999998

224 EPS2 = _Float( EPS2 = EPS * _2_0) # PYCHOK ≈ 4.440892098501e-16

225 EPS4 = _Float( EPS4 = EPS * _4_0) # PYCHOK ≈ 8.881784197001e-16

226# _1EPS = _Float(_1EPS =_1_0 + EPS) # PYCHOK ≈ 1.0000000000000002

227_1_EPS = _Float(_1_EPS =_1_0 / EPS) # PYCHOK = 4503599627370496.0

228# _2_EPS = _Float(_2_EPS =_2_0 / EPS) # PYCHOK = 9007199254740992.0

229_EPS2e4 = _Float(_EPS2e4 = EPS2 * 1.e4) # PYCHOK ≈ 4.440892098501e-12

230_EPS4e8 = _Float(_EPS4e8 = EPS4 * 1.e8) # PYCHOK ≈ 8.881784197001e-08

231_EPSmin = _Float(_EPSmin = sqrt(MIN)) # PYCHOK = 1.49166814624e-154

232_EPSqrt = _Float(_EPSqrt = sqrt(EPS)) # PYCHOK = 1.49011611938e5-08

233_EPStol = _Float(_EPStol =_EPSqrt * _0_1) # PYCHOK = 1.49011611938e5-09 == sqrt(EPS * _0_01)

234

235_89_999_ = _Float(_89_999_= EPS1 * _90_0) # just below 90.0

236# <https://Numbers.Computation.Free.FR/Constants/Miscellaneous/digits.html>

237_1__90 = _Float(_1__90 =_1_0 / _90_0) # PYCHOK = 0.011_111_111_111_111_111_111_111_111_111_111_111_111_111_111_11111

238_2__PI = _Float(_2__PI =_2_0 / _PI) # PYCHOK = 0.636_619_772_367_581_343_075_535_053_490_057_448_137_838_582_96182

239

240_1_16th = _Float(_1_16th =_1_0 / _16_0) # PYCHOK in .ellipsoids, .karney

241_1_64th = _Float(_1_64th =_1_0 / 64) # PYCHOK in .elliptic, pow(2.0, -6)

242_1_3rd = _Float(_1_3rd =_1_0 / _3_0) # PYCHOK in .fmath

243_2_3rd = _Float(_2_3rd =_2_0 / _3_0) # PYCHOK in .fmath

244

245_K0_UTM = _Float(_K0_UTM = 0.9996) # PYCHOK in .etm, .ktm, .utm, UTM scale at central meridian

246# sqrt(2) <https://WikiPedia.org/wiki/Square_root_of_2>

247# 1.414213562373095_048_801_688_724_209_698_078_569_671_875_376_948_073_176_679_737_99

248# _1SQRT2= _Float(_1SQRT2 =sqrt(_2_0) + 1)

249_SQRT2_2 = _Float(_SQRT2_2=sqrt(_0_5)) # PYCHOK = 0.707106781186547_6 == sqrt(2) / 2

250

251 INF = Float(INF =_inf) # PYCHOK INFinity, see function L{isinf}, L{isfinite}, NOT _Float!

252 INT0 = Int( INT0= 0) # PYCHOK unique int(0) instance, see .fsums, useZ=False

253 NAN = Float(NAN =_nan) # PYCHOK Not-A-Number, see function L{isnan}, NOT _Float!

254 NEG0 = Float(NEG0=_N_0_0) # PYCHOK NEGative 0.0, see function L{isneg0}, NOT _Float!

255 NINF = Float(NINF=-INF) # PYCHOK Negative INFinity, NOT _Float!

256

257 PI = _Float(PI =_PI)

258 PI2 = _Float(PI2 =_PI * _2_0) # PYCHOK Two PI, M{PI * 2} aka I{Tau}

259 PI_2 = _Float(PI_2 =_PI / _2_0) # PYCHOK Half PI, M{PI / 2}

260 PI3 = _Float(PI3 =_PI * _3_0) # PYCHOK Three PI, M{PI * 3}

261 PI3_2 = _Float(PI3_2=_PI * _1_5) # PYCHOK PI and a half, M{PI * 3 / 2}

262 PI_3 = _Float(PI_3 =_PI / _3_0) # PYCHOK One third PI, M{PI / 3}

263 PI4 = _Float(PI4 =_PI * _4_0) # PYCHOK Four PI, M{PI * 4}

264 PI_4 = _Float(PI_4 =_PI / _4_0) # PYCHOK Quarter PI, M{PI / 4}

265

266 R_MA = _Radius(R_MA=6378137.0) # PYCHOK equatorial earth radius (C{meter}), WGS84, EPSG:3785

267 R_MB = _Radius(R_MB=6356752.3) # PYCHOK polar earth radius (C{meter}), WGS84, EPSG:3785

268 R_M = _Radius(R_M =6371008.771415) # PYCHOK mean, spherical earth radius (C{meter})

269 R_KM = _Radius(R_KM=R_M / _M_KM) # PYCHOK mean, spherical earth radius (C{KM}, Kilo meter)

270 R_NM = _Radius(R_NM=R_M / _M_NM) # PYCHOK mean, spherical earth radius (C{NM}, nautical miles)

271 R_SM = _Radius(R_SM=R_M / _M_SM) # PYCHOK mean, spherical earth radius (C{SM}, statute miles)

272# See <https://www.EdWilliams.org/avform.htm>, <https://www.DTIC.mil/dtic/tr/fulltext/u2/a216843.pdf>

273# and <https://GitHub.com/NASA/MultiDop/blob/master/src/share/man/man3/geog_lib.3> based on the

274# International Standard Nautical Mile of 1,852 meter (1' latitude)

275 R_FM = _Radius(R_FM=6371000.0) # PYCHOK former FAI Sphere earth radius (C{meter})

276 R_GM = _Radius(R_GM=6371230.0) # PYCHOK avg. radius, distance to geoid surface (C{meter})

277# <http://Wiki.GIS.com/wiki/index.php/Ellipsoidal_quadratic_mean_radius>

278 R_QM = _Radius(R_QM=6372797.560856) # PYCHOK earth' quadratic mean radius (C{meter})

279# Rtri= _Radius(Rtri=6372797.5559594) # PYCHOK Rtriaxial quadratic mean radius (C{meter}), WGS84

280# Rbi = _Radius(Rbi =6367453.6345163) # PYCHOK Rbiaxial quadratic mean radius (C{meter}), WGS84

281 R_VM = _Radius(R_VM=6366707.0194937) # PYCHOK aViation/naVigation earth radius (C{meter})

282# R_AU= Meter( R_AU=149597870700.0) # PYCHOK <https://WikiPedia.org/wiki/Astronomical_unit>

283

284_INF_NAN_NINF = INF, NAN, NINF

285_pos_self = _1_0.__pos__() is _1_0 # PYCHOK in .fsums, .vector3dBase

286

287

288 def _0_0s(n):

289 '''(INTERNAL) Return an C{B{n}-tuple} of C{_0_0} zeros.

290 '''

291 return _0_0_9T[:n] if 0 <= n <= len(_0_0_9T) else (_0_0_1T * n)

292

293

294 try:

295 from math import isclose as _isclose

296 except ImportError: # Python 3.4-

297

298 def _isclose(a, b, rel_tol=1e-9, abs_tol=0):

299 '''Mimick Python 3.5+ C{math.isclose}.

300 '''

301 t, d = abs_tol, fabs(a - b)

302 if d > t:

303 r = max(fabs(a), fabs(b)) * rel_tol

304 t = max(r, t)

305 return d <= t

306

307

308 def isclose(a, b, rel_tol=1e-12, abs_tol=EPS0):

309 '''Like C{math.isclose}, but with defaults such

310 that C{isclose(0, EPS0)} is C{True} by default.

311 '''

312 return _isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)

313

314

315 try:

316 from math import isfinite as _isfinite # in .ellipsoids, .fsums, .karney

317 except ImportError: # Python 3.1-

318

319 def _isfinite(x):

320 '''Mimick Python 3.2+ C{math.isfinite}.

321 '''

322 return not (isinf(x) or isnan(x))

323

324

325 def isfinite(obj):

326 '''Check a finite C{scalar} or C{complex} value.

327

328 @arg obj: Value (C{scalar} or C{complex}).

329

330 @return: C{False} if B{C{obj}} is C{INF}, C{NINF}

331 or C{NAN}, C{True} otherwise.

332

333 @raise TypeError: Non-scalar and non-complex B{C{obj}}.

334 '''

335 try:

336 return (obj not in _INF_NAN_NINF) and _isfinite(obj)

337 except Exception as x:

338 if iscomplex(obj): # _isfinite(complex) thows TypeError

339 return isfinite(obj.real) and isfinite(obj.imag)

340 raise _xError(x, Fmt.PAREN(isfinite.__name__, obj))

341

342

343 def isint0(obj, both=False):

344 '''Check for L{INT0} or C{int(0)} value.

345

346 @arg obj: The object (any C{type}).

347 @kwarg both: If C{true}, also check C{float(0)} (C{bool}).

348

349 @return: C{True} if B{C{obj}} is L{INT0}, C{int(0)} or

350 C{float(0)}, C{False} otherwise.

351 '''

352 return (obj is INT0 or obj is int(0) or bool(both and

353 (not obj) and isint(obj, both=True))) and not isbool(obj)

354

355

356 def isnear0(x, eps0=EPS0):

357 '''Is B{C{x}} near I{zero} within a tolerance?

358

359 @arg x: Value (C{scalar}).

360 @kwarg eps0: Near-I{zero} tolerance (C{EPS0}).

361

362 @return: C{True} if C{abs(B{x}) < B{eps0}},

363 C{False} otherwise.

364

365 @see: Function L{isnon0}.

366 '''

367 return bool(eps0 > x > -eps0)

368

369

370 def isnear1(x, eps1=EPS0):

371 '''Is B{C{x}} near I{one} within a tolerance?

372

373 @arg x: Value (C{scalar}).

374 @kwarg eps1: Near-I{one} tolerance (C{EPS0}).

375

376 @return: C{isnear0(B{x} - 1, eps0=B{eps1})}.

377

378 @see: Function L{isnear0}.

379 '''

380 return bool(eps1 > (x - _1_0) > -eps1)

381

382

383 def isnear90(x, eps90=EPS0):

384 '''Is B{C{x}} near I{90} within a tolerance?

385

386 @arg x: Value (C{scalar}).

387 @kwarg eps90: Near-I{90} tolerance (C{EPS0}).

388

389 @return: C{isnear0(B{x} - 90)}.

390

391 @see: Function L{isnear0}.

392 '''

393 return bool(eps90 > (x - _90_0) > -eps90)

394

395

396 def isneg0(x):

397 '''Check for L{NEG0}, negative C{0.0}.

398

399 @arg x: Value (C{scalar}).

400

401 @return: C{True} if B{C{x}} is C{NEG0} or C{-0.0},

402 C{False} otherwise.

403 '''

404 return (not x) and _copysign(1, x) < 0

405# and str(x).startswith(_MINUS_)

406

407

408 def isninf(x):

409 '''Check for L{NINF}, negative C{INF}.

410

411 @arg x: Value (C{scalar}).

412

413 @return: C{True} if B{C{x}} is C{NINF} or C{-inf},

414 C{False} otherwise.

415 '''

416 return x is NINF or (x < 0 and not isfinite(x))

417

418

419 def isnon0(x, eps0=EPS0):

420 '''Is B{C{x}} non-zero with a tolerance?

421

422 @arg x: Value (C{scalar}).

423 @kwarg eps0: Non-zero tolerance (C{EPS0}).

424

425 @return: C{True} if C{abs(B{x}) > B{eps0}},

426 C{False} otherwise.

427

428 @see: Function L{isnear0}.

429 '''

430 return not bool(eps0 > x > -eps0) # not isnear0

431

432

433 def _off90(lat):

434 '''(INTERNAL) Off 90.0 for .gars and .wgrs.

435 '''

436 return max(min(lat, _89_999_), -_89_999_)

437

438

439 try:

440 from math import remainder

441 except ImportError: # Python 3.6-

442 from math import fmod as _fmod

443

444 def remainder(x, y):

445 '''Mimick Python 3.7+ C{math.remainder}.

446 '''

447 if isnan(y):

448 x = NAN

449 elif x and not isnan(x):

450 y = fabs(y)

451 x = _fmod(x, y)

452 h = _0_5 * y

453 if x >= h:

454 x -= y

455 elif x < -h:

456 x += y

457 return x # keep signed 0.0

458

459

460 def _umod_360(deg):

461 '''(INTERNAL) Non-negative C{deg} modulo 360, basic C{.utily.wrap360}.

462 '''

463 return (deg % _360_0) or _0_0

464

465

466 def _umod_PI2(rad):

467 '''(INTERNAL) Non-negative C{rad} modulo PI2, basic C{.utily.wrapPI2}.

468 '''

469 return (rad % PI2) or _0_0

470

471

472 if __name__ == '__main__':

473

474 from pygeodesy.errors import itemsorted

475 from pygeodesy.lazily import printf

476

477 t = n = v = []

478 for n, v in itemsorted(locals()):

479 if isinstance(v, (Float, Int, Radius)):

480 printf('%9s: %r', n, v.toRepr(std=False))

481 if v.name != n:

482 raise AssertionError('%r != %r' % (n, v))

483 if v.name is not n:

484 raise AssertionError('%r is not %r' % (n, v))

485 if not n.startswith(_UNDER_):

486 t.append(n)

487 t.append(float_.__name__)

488 printf('__all__ = %r', tuple(t))

489

490# **) MIT License

491#

492# Copyright (C) 2016-2023 -- mrJean1 at Gmail -- All Rights Reserved.

493#

494# Permission is hereby granted, free of charge, to any person obtaining a

495# copy of this software and associated documentation files (the "Software"),

496# to deal in the Software without restriction, including without limitation

497# the rights to use, copy, modify, merge, publish, distribute, sublicense,

498# and/or sell copies of the Software, and to permit persons to whom the

499# Software is furnished to do so, subject to the following conditions:

500#

501# The above copyright notice and this permission notice shall be included

502# in all copies or substantial portions of the Software.

503#

504# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

505# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

506# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

507# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

508# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

509# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

510# OTHER DEALINGS IN THE SOFTWARE.