# Small Python math library

I have been writing a math library with more options than the standard builtin one, partially to make my life easier in the future, and partially just for practice. Here is what I have so far in the main file (master/xtrmth/xm.py):


import typing as tp
import decimal as dc

_meganum = tp.Union[int, float, dc.Decimal, tp.SupportsInt, tp.SupportsFloat]
_num = tp.Union[int, float, dc.Decimal]
_micronum = tp.Union[int, float]
_decnum = tp.Union[float, dc.Decimal]

def _total_round(value: _num, precision: int = 10, decimal: bool = False) -> _num:
"""Rounds 'value' to the nearest 'precision' digits."""
if isinstance(value, int):
return value
elif precision < 0:
raise ValueError('Cannot cast a negative integer onto \'xm._total_round(precision)\'')
elif decimal is True:
if isinstance(value, dc.Decimal):
return round(value, precision)
elif not isinstance(value, dc.Decimal) and decimal is True:
raise TypeError('Cannot cannot cast \'float\' onto \'xm._total_round(value)\' \
with opperand \'decimal\' as \'True\'.')
elif decimal is False:
if isinstance(value, float):
return round(value, precision)
elif not isinstance(value, float):
raise TypeError('Cannot cast \'decimal\' onto \'xm._total_round(value)\' \
with opperand \'decimal\' as \'False\'.')

def summation(count: int, bottom_var: str, expression: str, precision: int = 10, \
decimal: bool = False) -> _num:
'''Summation function. Example: 'summation(4, 'z=1', 'z+1')' would return 14.'''

if precision < 0:
raise ValueError('Cannot cast a negative integer onto \'xm.summation(precision)\'')
var, value = bottom_var.split('=')
var = var.strip()

if decimal is True:
value = dc.Decimal(eval(value))
else:
value = int(eval(value))

res = 0
for i in range(value, count+1):
res += eval(expression.replace(var, str(i)))

if decimal is True:
return _total_round(value=res, precision=precision, decimal=True)
return _total_round(res, precision=precision, decimal=False)

def sq(value: _num, precision: int = 10, decimal: bool = False, _print_unround: bool = False) -> _micronum:
'''Returns 'value' raised to the 2nd power, with 'precision' decimal points.'''
if isinstance(value, float) and decimal is True:
raise TypeError('Cannot cannot cast \'float\' onto \'xm.cb\' \
with opperand \'decimal\' as \'True\'.')
elif isinstance(value, dc.Decimal) and decimal is False:
raise TypeError('Cannot cannot cast \'decimal\' onto \'xm.cb\' \
with opperand \'decimal\' as \'False\'.')
elif isinstance(value, dc.Decimal) and decimal is True:
if _print_unround is True:
print(value*value)
return _total_round(value*value, precision, decimal=True)
if _print_unround is True:
print(value*value)
return _total_round(value*value, precision, decimal=False)

def sqrt(value: _meganum, precision: int = 10, decimal: bool = False, _print_unround: bool = False) -> _num:
if decimal is True:
x = dc.Decimal(value)
y = dc.Decimal(1)
e = dc.Decimal(0.000000000000000000000000000000000000000000000000000000000000000001)
else:
x = value
y = 1
e = 0.0000000000000000000000001

while x - y > e:
x = (x + y)/2
y = value / x

if _print_unround is True:
print(x)

return(_total_round(x, precision, decimal=decimal))

def cb(value: _meganum, precision: int = 10, decimal: bool = False, _print_unround: bool = False) -> _num:
'''Returns 'value' raised to the 2nd power, with '''
if isinstance(value, float) and decimal is True:
raise TypeError('Cannot cannot cast \'float\' onto \'xm.cb\' \
with opperand \'decimal\' as \'True\'.')
elif isinstance(value, dc.Decimal) and decimal is False:
raise TypeError('Cannot cannot cast \'decimal\' onto \'xm.cb\' \
with opperand \'decimal\' as \'False\'.')
elif isinstance(value, dc.Decimal) and decimal is True:
if _print_unround is True:
print(value*value*value)
return _total_round(value*value*value, precision, decimal=True)
if _print_unround is True:
print(value*value*value)
return _total_round(value*value*value, precision, decimal=False)

def cbrt(value, _print_unround: bool = False) -> _num:
x = value**(1/3)

if _print_unround is True:
print(x)

if type(x) is float:
if round(x, 10) == int(round(x, 10)): return int(round(x, 10))
return round(x, 10)
return x

def xpn(base: _meganum, exponent: _meganum, decimal: bool = False, precision: int = 10, _print_debug: bool = False) \
-> _num:
'''Raises 'base' to the power of 'exponent'.'''

if not isinstance(base, dc.Decimal) and decimal is True:
raise TypeError(f'Cannot cast \'{type(base).__name__()}\' onto \'xm.xpn(base)\' with opperand \'decimal\' as \'True\'')
elif isinstance(base, dc.Decimal) and decimal is False:
raise TypeError('Cannot cast \'decimal.Decimal\' onto \'xm.xpn(base)\' with opperand \'decimal\' as \'False\'')

out = 1

if isinstance(exponent, int):
if _print_debug is True:
print('exponent is int')
for i in range(exponent):
if _print_debug is True:
print(out)
out *= base
return _total_round(out, precision=precision, decimal=decimal)
else:
# will update with my own algorithim in a later update
return _total_round(base**exponent, precision=precision, decimal=decimal)

def rt(base: _meganum, root: _meganum, precision: int = 10, decimal: bool = False, _print_debug: bool = False) -> _num:
'''Takes the 'root' root of 'base' '''

if isinstance(base, dc.Decimal) and decimal is False:
raise TypeError('Cannot cast \'decimal.Decimal\' onto \'xm.rd(base)\' with opperand \'decimal\' as \'False\'')
elif not isinstance(base, dc.Decimal) and decimal is True:
raise TypeError(f'Cannot cast \'{type(base).__name__}\' onto \'xm.rd(base)\' with opperand \'decimal\' as \'False\'')
elif isinstance(root, dc.Decimal) and decimal is False:
raise TypeError('Cannot cast \'decimal.Decimal\' onto \'xm.rd(root)\' with opperand \'decimal\' as \'False\'')
elif not isinstance(root, dc.Decimal) and decimal is True:
raise TypeError(f'Cannot cast \'{type(root).__name__}\' onto \'xm.rd(root)\' with opperand \'decimal\' as \'True\'')

if decimal is True:
return xpn(base = base, exponent = (dc.Decimal(1) / root), decimal = True, precision = precision, _print_debug = _print_debug)
return xpn(base = base, exponent = (1 / root), decimal = False, precision = precision, _print_debug = _print_debug)


Is there anything I should change?

Github page: Github
PyPI page: PyPI

• cb(…) -> _num: '''Returns 'value' raised to the 2nd power, with ''' Again? Jan 25, 2021 at 10:42
• @greybeard That was a mistype in the docstring. Jan 25, 2021 at 13:43
• (I'd call it a C&PE: Copy&Paste Error. In the "original code", probably, rather than in transferring to SE. Keep in mind a) posting on SE puts contents under a Creative Commons licence b) docstrings are available during execution via introspection.) Jan 25, 2021 at 13:57
• @greybeard No, I simply made a mistype. It's not that rare. Jan 25, 2021 at 14:28

# General

import typing as tp => from typing import Union reduces acronyms reader needs to remmber

Backslashes are scary. Adding parentheses or in this case trusting the existing ones will be easier to read.

def summation(count: int, bottom_var: str, expression: str, precision: int = 10, \
decimal: bool = False) -> _num:


Again, backslashes are scary.

raise ValueError('Cannot cast a negative integer onto \'xm.summation(precision)\'')
# could be
raise ValueError("Cannot cast a negative integer onto 'xm.summation(precision)'")


There are a lot of underscores here. Exposing the important functions in an separate file would reduce the need to hide everything.

# Functions

Each of your functions are written in a pretty similar style, so I'll just look at one of them.

def rt(base: _meganum, root: _meganum, precision: int = 10, decimal: bool = False, _print_debug: bool = False) -> _num:
'''Takes the 'root' root of 'base' '''

if isinstance(base, dc.Decimal) and decimal is False:
raise TypeError('Cannot cast \'decimal.Decimal\' onto \'xm.rd(base)\' with opperand \'decimal\' as \'False\'')
elif not isinstance(base, dc.Decimal) and decimal is True:
raise TypeError(f'Cannot cast \'{type(base).__name__}\' onto \'xm.rd(base)\' with opperand \'decimal\' as \'False\'')
elif isinstance(root, dc.Decimal) and decimal is False:
raise TypeError('Cannot cast \'decimal.Decimal\' onto \'xm.rd(root)\' with opperand \'decimal\' as \'False\'')
elif not isinstance(root, dc.Decimal) and decimal is True:
raise TypeError(f'Cannot cast \'{type(root).__name__}\' onto \'xm.rd(root)\' with opperand \'decimal\' as \'True\'')

if decimal is True:
return xpn(base = base, exponent = (dc.Decimal(1) / root), decimal = True, precision = precision, _print_debug = _print_debug)
return xpn(base = base, exponent = (1 / root), decimal = False, precision = precision, _print_debug = _print_debug)

• You can afford more than 2 characters for a function name.
• The Decimal type as an edge case clutters up both your usage and your library code.
• The conditional statements can be removed by taking advantage of features these types already have
def nth_root(base: _meganum, n: _meganum, precision: int = 10) -> _num:
'''Takes the nth root of 'base' '''
# works for Decimals, ints, floats, whatever
exponent = n ** -1
return xpn(base=base, exponent=exponent, precision=precision)


What's going on here?

elif decimal is True:
⋮
elif decimal is False:


Firstly, especially since we annotated that decimal is boolean, we should just be testing its truthiness; secondly, at the elif we already know that decimal isn't true. So, simply write

elif decimal:
⋮
else:


Applying this simple change throughout already cuts a lot of unnecessary verbiage and improves the readability.

The immediately preceding code shows we shouldn't be in elif anyway

if isinstance(value, int):
return …
elif precision < 0:
raise …
elif decimal is True:


Since return and raise both exit the flow of control, we can reduce cognitive load thus:

if isinstance(value, int):
return …
if precision < 0:
raise …
if decimal:


Looking inside the if decimal case, we see:

  elif decimal is True:
if isinstance(value, dc.Decimal):
return …
elif not isinstance(value, dc.Decimal) and decimal is True:
raise …


In last condition, we know that decimal is true and that value is not a dc.Decimal, so that one just becomes a plain else - and not needed because the previous clause returned:

  elif decimal:
if not isinstance(value, dc.Decimal):
raise …
return …


Just simplifying the control flow in that one function makes it a lot simpler:

def _total_round(value: _num, precision: int = 10, decimal: bool = False) -> _num:
"""Rounds 'value' to the nearest 'precision' digits."""
if isinstance(value, int):
return value
if precision < 0:
raise ValueError("Cannot cast a negative integer onto 'xm._total_round(precision)'")
if decimal and not isinstance(value, dc.Decimal):
raise TypeError("Cannot cannot cast 'float' onto 'xm._total_round(value)' \
with operand 'decimal' as 'True'.")
if not decimal and not isinstance(value, float):
raise TypeError("Cannot cast 'decimal' onto 'xm._total_round(value)' \
with operand 'decimal' as 'False'.")
return round(value, precision)


Looking again from further away, I don't see any unit-tests at all. What happened to them?

• To most of this: understood, I will change (note though that _total_round() has already been greatly simplified since I made this post). To the unit tests: I usually just go into the REPL, import the file, and try out the functions. Mar 15, 2021 at 16:18
• A good regression suite is well worth cultivating, and in Python it's easy to include it under if __name__ == '__main__'. Definitely consider writing some tests - and for your next project, they are even more valuable if you use them to write the code! Mar 15, 2021 at 16:50
• I just left the ugly wrapped strings as is, because I was focusing on the control flow. The other answer deals with that more effectively (Python isn't my main language, so I didn't want to over-step my competence!) Mar 22, 2021 at 10:11
• @TobySpeight Sorry didn't get pung. Oh, I thought you were the one to change the strings... I see now I was wrong. Sorry. Mar 31, 2021 at 21:13