~jasper/type_stack_calc

ref: 6aca66fb1a7cf1e10f7dd3263f04537c8ff37590 type_stack_calc/type_stack_calc/util/fun_info.py -rw-r--r-- 3.3 KiB
6aca66fb — Jasper den Ouden Apparently `to_c` can already read ahead one, hopefully its this simple... 1 year, 1 month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Describe functions info for use in testing, defining stuff etc.

from inspect import signature

import math
pi = math.pi

class FunInfo:
    """Object that generally defines info about function."""
    def __init__(self, call=None, commute=None, **kv):
        self.name = None
        self.call = call  # Does the function.
        if call is not None:  # Fish argument names&count out of the call.
            assert callable(call)
            try:
                self.args = list(signature(call).parameters)
                self.n = len(self.args)
            except:
                self.args, self.n = None, None
        for k, v in kv.items(): setattr(self, k, v)
        self.commute = commute  # Whether the parameters can be switched.

    def permitted_range(self, fr, to):
        assert to >= fr
        if not all(x <= fr or x >= to for x in getattr(self, 'holes', [])):
            return False  # (got a hole in it)
        rising, falling, allow = (list(getattr(self, k, []))
                                  for k in ['rising', 'falling', 'allow'])
        allow += rising + falling
        if len(allow) == 0:  # (unspecified means all is allowed)
            return True
        # In one of the ranges.
        return any((afr is None or afr <=fr) and (ato is None or ato>=to)
                   for afr, ato in allow)

fi = FunInfo

fun_info = dict(
    # `ineq_way` used in ~.sets.range
    gt = fi(lambda a,b: a>b,  bool=True, ineq_way=True, infix='>'),  # Inequalities.
    ge = fi(lambda a,b: a>=b, bool=True, ineq_way=True, infix='>='),
    lt = fi(lambda a,b: a<b,  bool=True, ineq_way=False, infix='<'),
    le = fi(lambda a,b: a<=b, bool=True, ineq_way=False, infix='<='),

    eq= fi(lambda a,b: a==b, bool=True, is_eq=True,  # Equality.
           infix='=', commute=True),

    # Two input basic operations.
    add  = fi(lambda a,b: a+b, infix='+', commute=True),  # Basic operations.
    subtract = fi(lambda a,b: a-b, infix='-'),
    mult = fi(lambda a,b: a*b, infix='*', commute=True),
    divide = fi(lambda a,b: a/b, infix='/', holes=((None,0),)),
    modulo = fi(lambda a,b: a*b, infix='%'),
    pow    = fi(lambda a,b: a**b, infix='**'),

    # TODO bitwise?

    # Bunch of functions (one input)
    negative = fi(lambda x:-x, falling=[(None,None)]),

    abs = fi(abs, extrema=(0,pi), rising=[(0,None)], falling=[(None,0)]),
    cos = fi(math.cos, float=True, period=2*pi,
             extrema=(0, pi)),
    sin = fi(math.sin, float=True, period=2*pi,
             extrema=(math.pi/2, 3*math.pi/2)),
    cosh = fi(math.cosh, float=True, extrema=(0,),
              rising=[(0,None)], falling=[(None,0)]),
    sqrt = fi(math.sqrt, float=True, rising=[(0,None)]))

    # TODO erf, refc, gamma

    # TODO tan, cot, atan, asin

    # TODO atan2

for k in ['log', 'log2', 'log10']:  # Positive input only.
    fun_info[k] = fi(getattr(math, k), float=True, rising=[(0,None)],
                     n=1, holes=[0])

# Rising functions.
for k in ['exp', 'sinh', 'tanh', 'degrees', 'radians']:
    fun_info[k] = fi(getattr(math, k), float=True, rising=[(None,None)])

for k,v in list(fun_info.items()):  #(list is against shenanigans happening..
    v.name = k
    if infix := getattr(v, 'infix', None):
        if infix != k and infix not in fun_info:
            fun_info[infix] = v

infixed_set = set(v.infix for v in fun_info.values() if getattr(v, 'infix', None))