# firanka [](https://travis-ci.org/smok-serwis/firanka) [](https://codeclimate.com/github/smok-serwis/firanka/maintainability) [](https://codeclimate.com/github/smok-serwis/firanka/test_coverage) [](https://badge.fury.io/py/firanka) []() []() []() []() firanka is a Python library to perform calculations on particular kinds of functions. These functions have a domain, which is a single continuous subset of the real number line. These functions can have any values. firanka allows you do define two classes of such functions or series. First are the _DiscreteSeries_. _DiscreteSeries_ further divide the function domain into slices (left-closed, right-open) that have constant values. Manipulating _DiscreteSeries_ and performing calculations on them is cheap. Then you have _FunctionSeries_. These are simply defined by user-supplied Python callable. Best part is, you can join series together (given a joining operator), slice them and so on. # Usage ## Series Can be imported from _sai.series_. A generic abstract superclass for series - `Series` can be imported for checking if given object is a series. Series are immutable, but non-hashable. Read the source code of the [base class](firanka/series/series.py#L11) to get to know more about series operations. ### DiscreteSeries To use a _DiscreteSeries_ you must give it a set of data to work with. These will define intervals with given values, left-closed, right-open. as in: ```python fs = DiscreteSeries([(0,1), (3, 4), (5, 6)]) fs[0.5] == 1 fs[3] == 4 fs[5] == 6 fs.domain == '<0;5>' # fs[6] - NotInDomainError's ``` Datapoints given **must be already sorted**!. By default, the domain will be both sides closed, from minimum to maximum given in data, but you can specify a custom one: ```python fs = DiscreteSeries([(0,1), (3, 4), (5, 6)], '(0; 8>') # fs[0] - NotInDomainError's ! fs[6] == 6 ``` Although you can't specify a domain where it would be impossible to compute the value. (ie. starting at smaller than zero). Doing so will throw a _ValueError_. Note that when using `join_discrete()` sometimes other series might get calls from beyond their domain. This can be seen for example here: ```python logs = FunctionSeries(math.log, '(0;5>') dirs = DiscreteSeries([(0,1)], '<0;5>') # Raises ValueError due to math.log being called with 0 dirs.join_discrete(logs, lambda x, y: x+y) ``` ### FunctionSeries Using _FunctionSeries_ is straightforward. Just give them a callable and a domain: ```python fs = FunctionSeries(lambda x: x**2, '<-2;2>') ``` ### ModuloSeries _ModuloSeries_ allow you to wrap a finite series in repetition. ```python fs = ModuloSeries(someOtherSeries) ``` By definition, _ModuloSeries_ has the domain of all real numbers. Note that someOtherSeries's domain length must be non-zero and finite. Otherwise _ValueError_ will be thrown. ## LinearInterpolationSeries These are discretes, but allow you to define an operator that will take its neighbours into account and let you return a custom value. By default, it will assumes that values can be added, subbed, multed and dived, and will do classical linear interpolation. ## Ranges Can be imported from _sai.ranges_. Range would have been better called an **interval**. It is a continuous subset of the real number line. You can create Ranges as follows: ```python Range(-5, 5, True, False) == Range('<-5;5)') ``` First boolean argument signifies whether the interval is left-closed, and second whether it is right-closed. Range's are immutable and hashable. They can be sliced: ```python Range('<-5;5>')[0:] == Range('<0;5>') ``` Slices work as a both-sides-closed range if both sides are shown! You can check whether a range contains a point ```python 5 not in Range('<-1;5)') ``` Or you can check for strict inclusion ```python Range('<-1;1>') in Range('<-2;2>') ``` ## TimeProviders **EXPERIMENTAL** Can be imported from _sai.timeproviders_.