1# -*- coding: utf-8 -*-
2
3u'''Single-instance C{float}s and C{str}ings, C{intern}'ed across modules.
4'''
5from math import pi as PI, sqrt
6
7
8class _Dash(str):
9    '''(INTERNAL) Extended C{str} for prefix_DASH_.
10    '''
11    def __call__(self, *args):
12        '''Join C{self} plus all B{C{args}} like C{str.join((self,) + B{args})}.
13        '''
14        return _DASH_(self, *args)  # re-callable
15
16
17class _Join(str):
18    '''(INTERNAL) Extended, callable C{str}.
19    '''
20    def join_(self, *args):
21        '''Join all B{C{args}} like C{str.join(B{args})}.
22        '''
23        return _Join(str.join(self, map(str, args)))  # re-callable
24
25    __call__ = join_
26
27
28class _Prefix(_Join):
29    '''(INTERNAL) Extended C{str} for prefix.
30    '''
31    def __call__(self, *args):
32        '''Join C{self} plus all B{C{args}} like C{str.join((self,) + B{args})}.
33        '''
34        return _SPACE_.join_(self, *args)  # re-callable
35
36
37class _Python_(str):  # overwritten below
38    '''(INTERNAL) Extended C{str} for C{Python} and version.
39    '''
40    def __call__(self, sys):
41        '''Return C{"Python <version>"}.
42        '''
43        return _SPACE_(self, sys.version.split()[0])
44
45
46class _Slicer(str):
47    '''(INTERNAL) String slicer C{.fromX} or C{.tillY}.
48    '''
49    def __getattr__(self, name):  # .fromX, .tillY
50        if name.startswith(_till_):
51            i = self.find(name[len(_till_):])
52            if 0 < i < len(self):
53                return _Slicer(self[:i + 1])
54        elif name.startswith(_from_):
55            i = self.find(name[len(_from_):])
56            if 0 < (i + 1) < len(self):
57                return _Slicer(self[i:])
58        else:
59            return getattr(str, name)
60        return self
61
62
63class MISSING(object):
64    '''(INTERNAL) Singleton.
65    '''
66    def toRepr(self, **unused):
67        return self.__class__.__name__
68
69    __repr__ = toRepr
70    __str__  = toRepr
71    toStr    = toRepr
72
73MISSING          = MISSING()  # PYCHOK singleton
74MISSING.__name__ = str(MISSING)
75
76NN = _Join('')  # Nomen Nescio <https://Wiktionary.org/wiki/N.N.>
77
78# __DUNDER__-style names would get mangled in classes
79_0_                   = '0'                  # PYCHOK expected
80_0to9_                = '0123456789'         # PYCHOK expected
81_1_                   = '1'                  # PYCHOK expected
82_2_                   = '2'                  # PYCHOK expected
83_3_                   = '3'                  # PYCHOK expected
84_4_                   = '4'                  # PYCHOK expected
85_a_                   = 'a'                  # PYCHOK expected
86_A_                   = 'A'                  # PYCHOK expected
87_Airy1830_            = 'Airy1830'           # PYCHOK expected
88_AiryModified_        = 'AiryModified'       # PYCHOK expected
89_an_                  = 'an'                 # PYCHOK expected
90_angle_               = 'angle'              # PYCHOK expected
91_areaOf_              = 'areaOf'             # PYCHOK expected
92_ambiguous_           = 'ambiguous'          # PYCHOK expected
93# _AMPERSAND_   = _Join('&')                 # PYCHOK expected
94# _AND_               = _AMPERSAND_          # PYCHOK expected
95_and_                 = 'and'                # PYCHOK expected
96_at_                  = 'at'                 # PYCHOK expected
97_AT_            = _Join('@')                 # PYCHOK expected
98_AtoZnoIO_    = _Slicer('ABCDEFGHJKLMNPQRSTUVWXYZ')  # PYCHOK in C{gars}, C{mgrs} and C{wgrs}
99_attribute_           = 'attribute'          # PYCHOK expected
100_azi2_                = 'azi2'               # PYCHOK expected
101_azimuth_             = 'azimuth'            # PYCHOK expected
102_B_                   = 'B'                  # PYCHOK expected
103_band_                = 'band'               # PYCHOK expected
104_BAR_           = _Join('|')                 # PYCHOK expected
105_bearing_             = 'bearing'            # PYCHOK expected
106_Bessel1841_          = 'Bessel1841'         # PYCHOK expected
107_by_                  = 'by'                 # PYCHOK expected
108_C_                   = 'C'                  # PYCHOK expected
109_Cartesian_           = 'Cartesian'          # PYCHOK expected
110_center_              = 'center'             # PYCHOK expected
111_Clarke1866_          = 'Clarke1866'         # PYCHOK expected
112_Clarke1880IGN_       = 'Clarke1880IGN'      # PYCHOK expected
113_coincident_          = 'coincident'         # PYCHOK expected
114_colinear_            = 'colinear'           # PYCHOK expected
115_COLON_         = _Join(':')                 # PYCHOK expected
116_COLONSPACE_    = _Join(': ')                # PYCHOK expected
117_COMMA_         = _Join(',')                 # PYCHOK expected
118_COMMASPACE_    = _Join(', ')                # PYCHOK expected
119_concentric_          = 'concentric'         # PYCHOK expected
120_convergence_ = _Prefix('convergence')       # PYCHOK expected
121_conversion_          = 'conversion'         # PYCHOK expected
122_convex_              = 'convex'             # PYCHOK expected
123_cubic_               = 'cubic'              # PYCHOK expected
124_DASH_          = _Join('-')                 # PYCHOK == _MINUS_
125_datum_               = 'datum'              # PYCHOK expected
126_decode3_             = 'decode3'            # PYCHOK expected
127_deg_                 = 'deg'                # PYCHOK expected
128_degrees_             = 'degrees'            # PYCHOK expected
129_degrees2_            = 'degrees2'           # PYCHOK SQUARED
130_DEQUALSPACED_  = _Join(' == ')              # PYCHOK expected
131_distance_            = 'distance'           # PYCHOK expected
132_distanceTo_          = 'distanceTo'         # PYCHOK expected
133_distant_     = _Prefix('distant')           # PYCHOK expected
134_doesn_t_exist_       = "doesn't exist"      # PYCHOK expected
135_DOT_           = _Join('.')                 # PYCHOK expected
136_down_                = 'down'               # PYCHOK expected
137_e_                   = 'e'                  # PYCHOK expected
138_E_                   = 'E'                  # PYCHOK expected
139_east_                = 'east'               # PYCHOK expected
140_easting_             = 'easting'            # PYCHOK expected
141_ecef_                = 'ecef'               # PYCHOK expected
142_edge_                = 'edge'               # PYCHOK expected
143_elevation_           = 'elevation'          # PYCHOK expected
144_ELLIPSIS_      = _Join('...')               # PYCHOK expected
145# _ELLIPSISPACED_ = _Join(' ... ')           # PYCHOK <https://www.ThePunctuationGuide.com/ellipses.html>
146_ellipsoid_           = 'ellipsoid'          # PYCHOK expected
147_ellipsoidal_         = 'ellipsoidal'        # PYCHOK expected
148_enabled_             = 'enabled'            # PYCHOK expected
149_encode_              = 'encode'             # PYCHOK expected
150_end_                 = 'end'                # PYCHOK expected
151_epoch_               = 'epoch'              # PYCHOK expected
152_EPS_                 = 'EPS'                # PYCHOK expected
153_EPS0_                = 'EPS0'               # PYCHOK expected
154_EQUAL_         = _Join('=')                 # PYCHOK expected
155_EQUALSPACED_   = _Join(' = ')               # PYCHOK expected
156_exceed_PI_radians_   = 'exceed PI radians'  # PYCHOK expected
157_exceeds_     = _Prefix('exceeds')           # PYCHOK expected
158_exists_              = 'exists'             # PYCHOK expected
159_f_                   = 'f'                  # PYCHOK expected
160_feet_                = 'feet'               # PYCHOK expected
161_few_                 = 'few'                # PYCHOK expected
162_finite_              = 'finite'             # PYCHOK expected
163_fraction_            = 'fraction'           # PYCHOK expected
164_from_                = 'from'               # PYCHOK expected
165_g_                   = 'g'                  # PYCHOK expected
166_gamma_               = 'gamma'              # PYCHOK expected
167_GRS80_               = 'GRS80'              # PYCHOK expected
168_h_                   = 'h'                  # PYCHOK expected
169_H_                   = 'H'                  # PYCHOK expected
170_height_              = 'height'             # PYCHOK expected
171_hemipole_            = 'hemipole'           # PYCHOK expected
172_immutable_           = 'immutable'          # PYCHOK expected
173_i_                   = 'i'                  # PYCHOK expected
174_I_                   = 'I'                  # PYCHOK expected
175_in_                  = 'in'                 # PYCHOK expected
176_INF_                 = 'INF'                # PYCHOK expected
177_initial_             = 'initial'            # PYCHOK expected
178_inside_              = 'inside'             # PYCHOK expected
179_intersection_        = 'intersection'       # PYCHOK expected
180_Intl1924_            = 'Intl1924'           # PYCHOK expected
181_invalid_             = 'invalid'            # PYCHOK expected
182_isclockwise_         = 'isclockwise'        # PYCHOK expected
183_ispolar_             = 'ispolar'            # PYCHOK expected
184_j_                   = 'j'                  # PYCHOK expected
185_k0_                  = 'k0'                 # PYCHOK expected
186_kind_                = 'kind'               # PYCHOK expected
187_knots_               = 'knots'              # PYCHOK expected
188_Krassovski1940_      = 'Krassovski1940'     # PYCHOK expected
189_Krassowsky1940_      = 'Krassowsky1940'     # PYCHOK expected
190#_LANGLE_             = '<'                  # PYCHOK expected
191_lam_                 = 'lam'                # PYCHOK expected
192_lat_                 = 'lat'                # PYCHOK expected
193_lat0_                = 'lat0'               # PYCHOK expected
194_lat1_                = 'lat1'               # PYCHOK expected
195_lat2_                = 'lat2'               # PYCHOK expected
196_latlon_              = 'latlon'             # PYCHOK expected
197_LatLon_              = 'LatLon'             # PYCHOK expected
198#_LCURLY_             = '{'                  # PYCHOK LBRACE
199_len_                 = 'len'                # PYCHOK expected
200_linear_              = 'linear'             # PYCHOK expected
201#_LPAREN_             = '('                  # PYCHOK expected
202_lon_                 = 'lon'                # PYCHOK expected
203_lon0_                = 'lon0'               # PYCHOK expected
204_lon2_                = 'lon2'               # PYCHOK expected
205#_LSQUARE_            = '['                  # PYCHOK LBRACK
206_ltp_                 = 'ltp'                # PYCHOK expected
207_m_                   = 'm'                  # PYCHOK expected
208_M_                   = 'M'                  # PYCHOK expected
209_mean_                = 'mean'               # PYCHOK expected
210_meanOf_              = 'meanOf'             # PYCHOK expected
211_meridional_          = 'meridional'         # PYCHOK expected
212_meter_               = 'meter'              # PYCHOK expected
213_meter2_              = 'meter2'             # PYCHOK SQUARED
214_MGRS_                = 'MGRS'               # PYCHOK expected
215_MINUS_               = _DASH_               # PYCHOK expected
216_module_              = 'module'             # PYCHOK expected
217_n_                   = 'n'                  # PYCHOK expected
218_N_                   = 'N'                  # PYCHOK expected
219_n_a_                 = 'n/a'                # PYCHOK expected
220_N_A_                 = 'N/A'                # PYCHOK expected
221_NAD27_               = 'NAD27'              # PYCHOK expected
222_NAD83_               = 'NAD83'              # PYCHOK expected
223_name_                = 'name'               # PYCHOK expected
224_NAN_                 = 'NAN'                # PYCHOK expected
225_near_          = _Dash('near')              # PYCHOK expected
226_nearestOn2_          = 'nearestOn2'         # PYCHOK expected
227_negative_            = 'negative'           # PYCHOK expected
228_NL_            = _Join('\n')                # PYCHOK expected
229_NL_hash_       = _Join(_NL_ + '# ')         # PYCHOK expected
230_NL_var_        = _Join(_NL_ + '@var ')      # PYCHOK expected
231_no_          = _Prefix('no')                # PYCHOK expected
232_north_               = 'north'              # PYCHOK expected
233_northing_            = 'northing'           # PYCHOK expected
234_NorthPole_           = 'NorthPole'          # PYCHOK expected
235_not_         = _Prefix('not')               # PYCHOK expected
236_NTF_                 = 'NTF'                # PYCHOK expected
237_null_                = 'null'               # PYCHOK expected
238_number_              = 'number'             # PYCHOK expected
239_numpy_               = 'numpy'              # PYCHOK expected
240_Nv00_                = 'Nv00'               # PYCHOK expected
241_O_                   = 'O'                  # PYCHOK expected
242_OKd_                 = '._-'                # PYCHOK expected
243_on_                  = 'on'                 # PYCHOK expected
244_or_                  = 'or'                 # PYCHOK expected
245_other_               = 'other'              # PYCHOK expected
246_outside_             = 'outside'            # PYCHOK expected
247_overlap_             = 'overlap'            # PYCHOK expected
248_PERCENT_             = '%'                  # PYCHOK expected
249_PERCENTDOTSTAR_      = '%.*'                # PYCHOK _DOT_(_PERCENT_, _STAR_)
250_perimeterOf_         = 'perimeterOf'        # PYCHOK expected
251_phi_                 = 'phi'                # PYCHOK expected
252_PLUS_          = _Join('+')                 # PYCHOK expected
253_PLUSMINUS_           = _PLUS_ + _MINUS_     # PYCHOK expected
254_point_               = 'point'              # PYCHOK expected
255_points_              = 'points'             # PYCHOK expected
256_pole_                = 'pole'               # PYCHOK expected
257_precision_           = 'precision'          # PYCHOK expected
258_prime_vertical_      = 'prime_vertical'     # PYCHOK expected
259_pygeodesy_abspath_   = 'pygeodesy_abspath'  # PYCHOK expected
260_Python_     = _Python_('Python')            # PYCHOK singleton
261# _QUOTE1_            = "'"                  # PYCHOK expected
262_QUOTE2_              = '"'                  # PYCHOK expected
263# _QUOTE3_            = "'''"                # PYCHOK expected
264# _QUOTE6_            = '"""'                # PYCHOK expected
265_radians_             = 'radians'            # PYCHOK expected
266_radians2_            = 'radians2'           # PYCHOK SQUARED
267_radius_              = 'radius'             # PYCHOK expected
268_radius1_             = 'radius1'            # PYCHOK expected
269_radius2_             = 'radius2'            # PYCHOK expected
270# _range_        = _Range('range')           # moved down
271#_RANGLE_             = '>'                  # PYCHOK expected
272#_RCURLY_             = '}'                  # PYCHOK RBRACE
273_reciprocal_          = 'reciprocal'         # PYCHOK expected
274_reframe_             = 'reframe'            # PYCHOK expected
275_resolution_          = 'resolution'         # PYCHOK expected
276#_RPAREN_             = ')'                  # PYCHOK expected
277#_RSQUARE_            = ']'                  # PYCHOK RBRACK
278_s_                   = 's'                  # PYCHOK expected
279_S_                   = 'S'                  # PYCHOK expected
280_scalar_              = 'scalar'             # PYCHOK expected
281_scale_               = 'scale'              # PYCHOK expected
282_scipy_               = 'scipy'              # PYCHOK expected
283_semi_circular_       = 'semi-circular'      # PYCHOK expected
284_sep_                 = 'sep'                # PYCHOK expected
285_singular_            = 'singular'           # PYCHOK expected
286_small_               = 'small'              # PYCHOK expected
287_Sphere_              = 'Sphere'             # PYCHOK expected
288_spherical_           = 'spherical'          # PYCHOK expected
289_SouthPole_           = 'SouthPole'          # PYCHOK expected
290_SPACE_         = _Join(' ')                 # PYCHOK expected
291_STAR_          = _Join('*')                 # PYCHOK expected
292_start_               = 'start'              # PYCHOK expected
293_std_                 = 'std'                # PYCHOK expected
294_stdev_               = 'stdev'              # PYCHOK expected
295_supported_           = 'supported'          # PYCHOK expected
296_sx_                  = 'sx'                 # PYCHOK expected
297_sy_                  = 'sy'                 # PYCHOK expected
298_sz_                  = 'sz'                 # PYCHOK expected
299_tbd_                 = 'tbd'                # PYCHOK expected
300_till_                = 'till'               # PYCHOK expected
301_to_                  = 'to'                 # PYCHOK expected
302_too_         = _Prefix('too')               # PYCHOK expected
303_transform_           = 'transform'          # PYCHOK expected
304_tx_                  = 'tx'                 # PYCHOK expected
305_ty_                  = 'ty'                 # PYCHOK expected
306_tz_                  = 'tz'                 # PYCHOK expected
307_UNDER_         = _Join('_')                 # PYCHOK expected
308_units_               = 'units'              # PYCHOK expected
309_up_                  = 'up'                 # PYCHOK expected
310_UPS_                 = 'UPS'                # PYCHOK expected
311_utf_8_               = 'utf-8'              # PYCHOK expected
312_UTM_                 = 'UTM'                # PYCHOK expected
313_V_                   = 'V'                  # PYCHOK expected
314_valid_               = 'valid'              # PYCHOK expected
315_version_             = 'version'            # PYCHOK expected
316_vs_                  = 'vs'                 # PYCHOK expected
317__vs__                = ' vs '               # PYCHOK vs-SPACED
318_W_                   = 'W'                  # PYCHOK expected
319_WGS72_               = 'WGS72'              # PYCHOK expected
320_WGS84_               = 'WGS84'              # PYCHOK expected
321_width_               = 'width'              # PYCHOK expected
322_x_                   = 'x'                  # PYCHOK expected
323_X_                   = 'X'                  # PYCHOK expected
324_xyz_                 = 'xyz'                # PYCHOK expected
325_y_                   = 'y'                  # PYCHOK expected
326_z_                   = 'z'                  # PYCHOK expected
327_zone_                = 'zone'               # PYCHOK expected
328
329_EW_                  = _E_  + _W_           # PYCHOK common cardinals
330_NE_                  = _N_  + _E_           # PYCHOK expected
331_NS_                  = _N_  + _S_           # PYCHOK expected
332_NSEW_                = _NS_ + _EW_          # PYCHOK expected
333_NW_                  = _N_  + _W_           # PYCHOK expected
334_SE_                  = _S_  + _E_           # PYCHOK expected
335_SW_                  = _S_  + _W_           # PYCHOK negative ones
336
337_DDOT_          = _Join(_DOT_ * 2)           # PYCHOK expected
338# _DEQUAL_      = _Join(_EQUAL_ * 2)         # PYCHOK expected
339_DUNDER_        = _Join(_UNDER_ * 2)         # PYCHOK expected
340
341
342class _Range(str):
343    '''(INTERNAL) Extended C{str} for C{range} strings.
344    '''
345    def __call__(self, lo, hi, prec=0, lopen=False, ropen=False,
346                               join=_COMMASPACE_):
347        '''Return the range as C{"(lo, hi)"}, C{"(lo, hi]"},
348           C{"[lo, hi)"} or C{"[lo, hi]"}.
349        '''
350        from pygeodesy.streprs import Fmt  # PYCHOK re-imported
351        r = NN(Fmt.f(lo, prec=prec), join,
352               Fmt.f(hi, prec=prec))
353        if lopen:
354            r = Fmt.PAREN(r) if ropen else Fmt.LOPEN(r)
355        else:
356            r = Fmt.ROPEN(r) if ropen else Fmt.SQUARE(r)
357        return r
358
359_range_ = _Range('range')  # PYCHOK expected
360
361
362def _dunder_name(inst, *dflt):
363    '''(INTERNAL) Get the double_underscore __name__ attr.
364    '''
365    try:
366        return inst.__name__
367    except AttributeError:
368        pass
369    return dflt[0] if dflt else inst.__class__.__name__
370
371
372def _float(f):  # in .datums, .ellipsoids, .trf
373    '''(INTERNAL) cache initial C{float}s.
374    '''
375    f = float(f)
376    return _floats.setdefault(f, f)  # PYCHOK del _floats
377
378
379def _floatuple(*fs):
380    '''(INTERNAL) Cache a tuple of C{float}s.
381    '''
382    return tuple(map(_float, fs))
383
384
385_floats = {}      # PYCHOK floats cache, in .__main__
386# _float = float  # PYCHOK expected
387# del _floats     # XXX zap floats cache never
388
389_0_0    = _float(   0)      # PYCHOK expected
390_0_001  = _float(   0.001)  # PYCHOK expected
391_0_01   = _float(   0.01)   # PYCHOK expected
392_0_1    = _float(   0.1)    # PYCHOK expected
393_0_125  = _float(   0.125)  # PYCHOK expected
394_0_25   = _float(   0.25)   # PYCHOK expected
395_0_26   = _float(   0.26)   # PYCHOK expected
396_0_5    = _float(   0.5)    # PYCHOK expected
397_1_0    = _float(   1)      # PYCHOK expected
398_1_0_T  = _1_0,             # PYCHOK 1-tuple
399_1_5    = _float(   1.5)    # PYCHOK expected
400_2_0    = _float(   2)      # PYCHOK expected
401_3_0    = _float(   3)      # PYCHOK expected
402_4_0    = _float(   4)      # PYCHOK expected
403_5_0    = _float(   5)      # PYCHOK expected
404_6_0    = _float(   6)      # PYCHOK expected
405_8_0    = _float(   8)      # PYCHOK expected
406_9_0    = _float(   9)      # PYCHOK expected
407_10_0   = _float(  10)      # PYCHOK expected
408_16_0   = _float(  16)      # PYCHOK expected
409_24_0   = _float(  24)      # PYCHOK expected
410_32_0   = _float(  32)      # PYCHOK expected
411_60_0   = _float(  60)      # PYCHOK expected
412_90_0   = _float(  90)      # PYCHOK expected
413_120_0  = _float( 120)      # PYCHOK expected
414_180_0  = _float( 180)      # PYCHOK expected
415_270_0  = _float( 270)      # PYCHOK expected
416_360_0  = _float( 360)      # PYCHOK expected
417_400_0  = _float( 400)      # PYCHOK expected
418_720_0  = _float( 720)      # PYCHOK expected
419_1000_0 = _float(1000)      # PYCHOK expected
420_3600_0 = _float(3600)      # PYCHOK expected
421
422try:
423    from sys import float_info as _float_info
424
425    DIG      =        _float_info.dig        # PYCHOK system's float decimal digits
426    EPS      = _float(_float_info.epsilon)   # PYCHOK system's EPSilon
427    MANT_DIG =        _float_info.mant_dig   # PYCHOK system's float mantissa bits
428    MAX      = _float(_float_info.max)       # PYCHOK system's MAX float 1.7976931348623157e+308
429    MIN      = _float(_float_info.min)       # PYCHOK system's MIN float 2.2250738585072014e-308
430except (AttributeError, ImportError):  # PYCHOK no cover
431    DIG      =  15  # PYCHOK system's float decimal digits
432    EPS      = _float(2.220446049250313e-16)  # PYCHOK EPSilon 2**-52, M{EPS +/- 1 != 1}
433    MAN_DIG  =  53  # PYCHOK float mantissa bits ≈ 53 (C{int})
434    MAX      = _float(pow(_2_0,  1023) * (_2_0 - EPS))  # PYCHOK ≈ 10**308
435    MIN      = _float(pow(_2_0, -1022))  # PYCHOK ≈ 10**-308
436
437EPS2     = _float(EPS * _2_0)      # PYCHOK ≈ 4.440892098501e-16
438EPS4     = _float(EPS * _4_0)      # PYCHOK ≈ 8.881784197001e-16
439EPS_2    = _float(EPS / _2_0)      # PYCHOK ≈ 1.110223024625e-16
440EPS1     = _float(_1_0 - EPS)      # PYCHOK ≈ 0.9999999999999998
441EPS1_2   = _float(_1_0 - EPS_2)    # PYCHOK ≈ 0.9999999999999999
442# _1EPS  = _float(_1_0 + EPS)      # PYCHOK ≈ 1.0000000000000002
443_1_EPS   = _float(_1_0 / EPS)      # PYCHOK = 4503599627370496.0
444# _2_EPS = _float(_2_0 / EPS)      # PYCHOK = 9007199254740992.0
445_EPS4e8  = _float(EPS4 * 1e8)      # PYCHOK ≈ 8.881784197001e-08
446_EPSmin  = _float(sqrt(MIN))       # PYCHOK = 1.49166814624e-154
447_EPSqrt  = _float(sqrt(EPS))       # PYCHOK = 1.49011611938e5-08
448_EPStol  = _float(_EPSqrt * _0_1)  # PYCHOK = 1.49011611938e5-09 == sqrt(EPS * _0_01)
449
450EPS0     = _float(EPS**2)   # PYCHOK near-zero comparison 4.930381e-32, or EPS or EPS_2
451EPS02    = _float(EPS**4)   # PYCHOK near-zero-squared comparison 2.430865e-63
452INF      = _float( _INF_)   # PYCHOK INFinity, see function L{isinf}, L{isfinite}
453NAN      = _float( _NAN_)   # PYCHOK Not-A-Number, see function L{isnan}
454NEG0     =  float('-0.0')   # PYCHOK NEGative 0.0, see function L{isneg0}
455
456PI2   = _float(PI * _2_0)  # PYCHOK Two PI, M{PI * 2} aka I{Tau}
457PI3   = _float(PI * _3_0)  # PYCHOK Three PI, M{PI * 3}
458PI3_2 = _float(PI * _1_5)  # PYCHOK PI and a half, M{PI * 3 / 2}
459PI4   = _float(PI * _4_0)  # PYCHOK Four PI, M{PI * 4}
460PI_2  = _float(PI / _2_0)  # PYCHOK Half PI, M{PI / 2}
461PI_4  = _float(PI / _4_0)  # PYCHOK Quarter PI, M{PI / 4}
462
463R_M   = _float(6371008.771415)  # PYCHOK mean, spherical earth radius (C{meter})
464
465MANTIS  = MANT_DIG  # DEPRECATED, use C{MANT_DIG}.
466
467__all__ = ('DIG', _EPS_, 'EPS2', 'EPS4', 'EPS1', 'EPS1_2', 'EPS_2', _EPS0_, 'EPS02',
468           'INF', 'MANT_DIG',
469           'MANTIS',  # DEPRECATED
470           'MAX', 'MIN',  # not 'MISSING'!
471           'NAN', 'NEG0', 'NN',
472           'PI', 'PI2', 'PI3', 'PI3_2', 'PI4', 'PI_2', 'PI_4',
473           'machine')  # imported by .lazily
474__version__ = '21.09.14'
475
476
477def _load_lib(name):  # must startwith('lib')
478    # macOS 11 (aka 10.16) no longer provides direct loading
479    # of system libraries, instead it installs the library
480    # after a low-level dlopen(name) call with the library
481    # base name.  As a result, ctypes.util.find_library
482    # can not find any library not previously dlopen'ed.
483    from ctypes import CDLL
484    from ctypes.util import find_library
485    from sys import platform
486
487    ns = find_library(name), name
488    if platform[:6] == 'darwin':  # and os.name == 'posix'
489        from ctypes import _dlopen
490        from os.path import join
491        ns += (_DOT_(name, 'dylib'),
492               _DOT_(name, 'framework'), join(
493               _DOT_(name, 'framework'), name))
494    else:  # non-macOS
495        def _dlopen(unused):
496            return True
497
498    for n in ns:
499        try:
500            if n and _dlopen(n):  # handle
501                lib = CDLL(n)  # == ctypes.cdll.LoadLibrary(n)
502                if lib._name:  # has a qualified name
503                    return lib
504        except (AttributeError, OSError):
505            pass
506
507    return None  # raise OSError
508
509
510def machine():
511    '''Return the C{platform.machine} string, distinguishing Intel from
512       I{emulating} Intel on Apple Silicon (on macOS).
513
514       @return: Machine C{'arm64'} for Apple Silicon, C{"arm64_x86_64""} for
515                Intel I{emulated}, C{'x86_64'} for Intel, etc. (C{str} with
516                any C{comma}s replaced with C{underscore}s).
517    '''
518    return _platform2()[1]
519
520
521def _platform2(sep=NN):
522    # get platform architecture and machine as C{2-list} or C{str}
523    import platform
524    m = platform.machine().replace(_COMMA_, _UNDER_)  # arm64, x86_64, iPhone13_2, etc.
525    if m == 'x86_64':  # only on Intel or Rosetta2 ...
526        v = platform.mac_ver()  # ... and only macOS
527        if v and _version2(v[0]) > (10, 15):  # macOS 11 Big Sur aka 10.16
528            # <https://Developer.Apple.com/forums/thread/659846>
529            if _sysctl_uint('sysctl.proc_translated') == 1:  # and \
530#              _sysctl_uint('hw.optional.arm64') == 1:  # PYCHOK indent
531                m = _UNDER_('arm64', m)  # Apple Silicon emulating Intel x86-64
532    el = [platform.architecture()[0],  # bits
533          m]  # arm64, arm64_x86_64, x86_64, etc.
534    return sep.join(el) if sep else el  # list
535
536
537def _pythonarchine(sep=NN):  # in test/base.py versions
538    # get PyPy and Python versions and C{_platform2} as C{3-} or C{4-list} or C{str}
539    from sys import version  # XXX shadows?
540    el = [_SPACE_(_Python_, version.split(None, 1)[0])] + _platform2()
541    if '[PyPy ' in version:  # see test/base.py
542        el.insert(0, _SPACE_('PyPy', version.split('[PyPy ')[1].split()[0]))
543    return sep.join(el) if sep else el  # list
544
545
546def _sysctl_uint(name):
547    # get an unsigned int sysctl item by name, use on macOS ONLY!
548    libc = _load_lib('libc')
549    if libc:  # <https://StackOverflow.com/questions/759892/python-ctypes-and-sysctl>
550        from ctypes import byref, c_char_p, c_size_t, c_uint, sizeof  # get_errno
551        n = name if str is bytes else bytes(name, _utf_8_)  # PYCHOK isPython2 = str is bytes
552        u = c_uint(0)
553        z = c_size_t(sizeof(u))
554        r = libc.sysctlbyname(c_char_p(n), byref(u), byref(z), None, c_size_t(0))
555    else:  # could find or load 'libc'
556        r = -2
557    return int(r if r else u.value)  # -1 ENOENT error, -2 no libc
558
559
560def _version2(version, n=2):
561    # split C{B{version} str} into 1-, 2- or 3-tuple of C{int}s
562    t = (tuple(map(int, version.split(_DOT_)[:n])) if version else ()) + (0, 0, 0)
563    return t[:n]
564
565# **) MIT License
566#
567# Copyright (C) 2016-2021 -- mrJean1 at Gmail -- All Rights Reserved.
568#
569# Permission is hereby granted, free of charge, to any person obtaining a
570# copy of this software and associated documentation files (the "Software"),
571# to deal in the Software without restriction, including without limitation
572# the rights to use, copy, modify, merge, publish, distribute, sublicense,
573# and/or sell copies of the Software, and to permit persons to whom the
574# Software is furnished to do so, subject to the following conditions:
575#
576# The above copyright notice and this permission notice shall be included
577# in all copies or substantial portions of the Software.
578#
579# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
580# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
581# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
582# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
583# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
584# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
585# OTHER DEALINGS IN THE SOFTWARE.
586