Route4Me Route Optimization Python SDK¶
Documentation
Build status
PyPI info
GitHub info
Code metrics/quality



The official Python SDK for Route4Me API
Install¶
$ pip install route4me-sdk
Example¶
from route4me.sdk import ApiClient
r4m = ApiClient(api_key='11111111111111111111111111111111')
opt = r4m.optimizations.get('07372F2CF3814EC6DFFAFE92E22771AA')
print(opt)
FAQ¶
What does the Route4Me SDK permit me to do?¶
This SDK makes it easier for you use the Route4Me API, which creates optimally sequenced driving routes for many drivers.
Who can use the Route4Me SDK (and API)?¶
The service is typically used by organizations who must route many drivers to many destinations. In addition to route optimization for new (future) routes, the API can also be used to analyze historical routes and to distribute routes to field personnel.
Who is prohibited from using the Route4Me SDK (and API)?¶
The Route4Me SDK and API cannot be resold or used in a product or system that competes directly with Route4Me. This means that developers cannot resell route optimization services to other businesses or developers. However, developers can integrate our route optimization SDK/API into their software applications. Developers and startups are also permitted to use our software for internal purposes (i.e. the same day delivery startup).
How does the API/SDK Integration Work?¶
A Route4Me customer, integrator, or partner incorporates the Route4Me SDK or API into their code base. Route4Me permits any paying subscriber to interact with every part of its system using its API. The API is RESTful, which means that it’s web based and can be accessed by other programs and machines The API/SDK should be used to automate the route planning process or to generate many routes with minimal manual intervention
Do optimized routes automatically appear inside my Route4Me account?¶
Every Route4Me SDK instance needs a unique API key. The API key can be retrieved inside your Route4Me.com account, inside the Settings tab called API. When a route is planned, it appears inside the corresponding Route4Me account. Because Route4Me web and mobile accounts are synchronized, the routes will appear in both environments at the same time.
Can I test the SDK with other addresses without a valid API Key?¶
No. The sample API key only permits you to optimize routes with the sample address coordinates that are part of this SDK.
Does the SDK have rate limits?¶
The number of requests you can make per second is limited by your current subscription plan. Typically, there are different rate limits for these core features: Address Geocoding & Address Reverse Geocoding Route Optimization & Management Viewing a Route
What is the recommended architecture for the Route4Me SDK?¶
There are two typical integration strategies that we recommend. Using this SDK, you can make optimization requests and then the SDK polls the Route4Me API to detect state changes as the optimization progresses. Alternatively, you can provide a webhook/callback URL, and the API will notify that callback URL every time there is a state change.
I don’t need route management or mobile capabilities. Is there a lower level Route4Me API just for the optimization engine?¶
Yes. Please contact support@route4me.com to learn about the low-level RESTful API.
How fast is the route Route4Me Optimization Web Service?¶
Most routes having less than 200 destinations are optimized in 1 second or less.
Can I disable optimization when planning routes?¶
Yes. You can send routes with optimization disabled if you want to conveniently see them on a map, or distribute them to your drivers in the order you prefer.
Can the API be used for aerial vehicles such as drones or self-driving cars?¶
Yes. The API can accept latitude/longitude and an unlimited amount of per-address metadata. The metadata will be preserved as passthrough data by our API so that the receiving device will have access to critical data when our API invokes a webhook callback to the device.
Are all my optimized routes stored permanently stored in the Route4Me database?¶
Yes. All routes are permanently stored in the database and are no longer accessible to you after your subscription is terminated.
Can I incorporate your API into my mobile application?¶
Route4Me’s route planning and optimization technology can only be added into applications that do not directly compete with Route4Me. This means the application’s primary capabilities must be unrelated to route optimization, route planning, or navigation.
Can I pay you to develop a custom algorithm?¶
Yes
Can I use your API and resell it to my customers?¶
White-labeling and private-labeling Route4Me is possible but the deal’s licensing terms vary considerably based on customer count, route count, and the level of support that Route4Me should provide to your customers.
Does the API/SDK have TMS or EDI, or EDI translator capabilities?¶
Route4Me is currently working on these features but they are not currently available for sale.
Can the API/SDK send notifications back to our system using callbacks, notifications, pushes, or webhooks?¶
Because Route4Me processes all routes asynchronously, Route4Me will conveniently notify the endpoint you specify as the route optimization job progresses through each state of the optimization. Every stage of the route optimization process has a unique stage id.
Does the Route4Me API and SDK work in my country?¶
Route4Me.com, as well as all of Route4Me’s mobile applications, use the Route4Me SDK’s and API. Since Route4Me works globally, this means that all of Route4Me’s capabilities are available using the SDK’s in every country
Will the Route4Me API/SDK work in my program on the Mac, PC, or Linux?¶
Customers are encouraged to select their preferred operating system environment. The Route4Me API/SDK will function on any operating system that supports the preferred programming language of the customer. At this point in time, almost every supported SDK can run on any operating system.
Does the Route4Me API/SDK require me to buy my own servers?¶
Route4Me has its own computing infrastructure that you can access using the API and SDKs. Customers typically have to run the SDK code on their own computers and/or servers to access this infrastructure.
Does Route4Me have an on-premise solution?¶
Route4Me does not currently lease or sell servers and does not have on-premise appliance solution. This would only be possible in exceptionally unique scenarios.
Does the Route4Me API/SDK require me to have my own programmers?¶
The time required to integrate the SDK can be as little as 1 hour or may take several weeks, depending on the number of features being incorporated into the customer’s application and how much integration testing will be done by the client. A programmer’s involvement is almost always required to use Route4Me’s technology when accessing it through the API.
Module Index¶
Quick Start¶
Quick start guide
Install¶
Probably, the best and simplest way — use PIP:
$ pip install route4me-sdk
See also
The full installation guide: Installation guide
Use¶
import route4m.sdk
r4m = route4me.sdk.ApiClient(api_key='11111111111111111111111111111111')
print(r4m.version)
opt = r4m.optimizations.get('07372F2CF3814EC6DFFAFE92E22771AA')
print(opt)
Installation guide¶
There are several ways to install Route4Me Python SDK.
PyPI¶
To install Route4Me Python SDK run this command in your terminal:
$ pip install route4me-sdk
If you don’t have pip
installed you can use this
guide to install pip.
Source Code¶
The source code of Route4Me Python SDK is open, public and live on GitHub: https://github.com/route4me/route4me-python-sdk.
You can either clone the entire repository:
$ git clone git://github.com/route4me/route4me-python-sdk.git
Or, download the zip/tarball:
# tarball
$ curl -OL https://github.com/route4me/route4me-python-sdk/tarball/master
# zip version:
$ curl -OL https://github.com/route4me/route4me-python-sdk/archive/master.zip
Now you can explore/modify code or embed it in your own project.
Date, Time and Timezones¶
Route4Me works worldwide, this means that we know what the time zone is.
Handling time zones is a challenge when we want to handle date and time
correctly for any time zone for many python
implementations. For example,
python 2 can’t process the following code
(an online proof), and python >3.3 — can:
import datetime
print(datetime.datetime.now().timestamp())
On the other hand, python3
will return float
, but many Unix users
could expect for an int, because /bin/date
returns int:
date +%s
# >> 1504267959
Explicit / TZ-Aware datetime¶
To avoid most problems, this SDK always use tz-aware datetime
variables. In most cases, datetime
variables are bound to
UTC time zone.
Under the hood we use two additional libraries:
Display local time¶
If you want to print local time (not UTC), or time in any other time zone (it is the same moment of time, but convenient for people in the other location), you could use arrow package to convert date/time presentation:
from route4me.sdk import ApiClient
import arrow
client = ApiClient(YOUR_API_KEY)
opt = client.optimizations.get(OPTIMIZATION_ID)
dt = opt.route_datetime # Now we have DT in UTC
print(dt.isoformat())
# >>> '2016-01-01T12:25:42.465805+00:00'
# move to local:
loc = arrow.get(dt).to('local').datetime
print(loc.isoformat())
# >>> '2016-01-01T11:25:42.465805-01:00'
# PROFIT!
API¶
ApiClient¶
Official Python SDK for Route4Me
Consists of classes:
- access to all Route4Me API’s endpoints;
- convenient models for Route4Me’s entities;
- constants and enums;
Look out the appropriate section for details.
-
class
route4me.sdk.
ApiClient
(api_key=None)[source]¶ Bases:
object
Route4Me API client
Provides an access to API’s endpoints through a convenient interface. In other words, all the power of Route4Me API is accessible through this class.
-
geocodings
¶ Geocodings endpoint functions
Returns: Geocoding namespace Return type: Geocodings
-
optimizations
¶ Optimization endpoint functions
Returns: Optimization namespace Return type: Optimizations
-
Endpoints¶
Geocoding is the process of converting addresses (like “1600 Amphitheatre Parkway, Mountain View, CA”) into geographic coordinates (like “latitude 37.423021 and longitude -122.083739”), which you can use to place markers on a map or position the map.
See also
An Optimization Problem refers to a collection of addresses that need to be visited.
The optimization problem takes into consideration all of the addresses that need to be visited and all the constraints associated with each address and depot.
It is preferable to create an optimization problem with as many orders in it as possible, so that the optimization engine is able to consider the entire problem set.
This is different from a Route
, which
is a sequence of addresses that need to be visited by a single vehicle and
driver in a fixed time period. Solving an Optimization Problem results in
a number of routes. (Possibly recurring in the future)
-
class
route4me.sdk.endpoints.optimizations.
Optimizations
(api_key=None, _network_client=None)[source]¶ Bases:
object
Optimizations endpoint
-
create
(optimization_data, optimized_callback_url=None)[source]¶ Create a new optimization through the Route4Me API
You could pass any valid URL as :paramref:`optimized_callback_url` parameter.
The callback URL is a URL that gets called when the optimization is solved, or if there is an error. The callback is called with a POST request. The example of the POST data sent:
{ "timestamp": 1500111222, // seconds "state": 4, // ID of the optimization state // ID of Optimization Problem "optimization_problem_id": "1EDB78F63556D99336E06A13A34CF139" }
The
state
is a value from the enumerationOptimizationStateEnum
See also
Route4Me API: https://route4me.io/docs/#create-an-optimization
Parameters: - optimization_data (Optimization or dict) – Optimization data
- optimized_callback_url (str or None, optional) – Optimization done callback URL, defaults to None
Returns: New optimization
Return type:
-
get
(ID)[source]¶ GET a single optimization by ID.
See also
Route4Me API: https://route4me.io/docs/#get-an-optimization
Parameters: ID (str) – Optimization Problem ID Returns: Optimization data Return type: Optimization Raises: Route4MeEntityNotFoundError – if optimization was not found
-
list
(states=None, limit=None, offset=None)[source]¶ GET all optimizations belonging to a user.
Optionally filtered
See also
Route4Me API: https://route4me.io/docs/#get-optimizations
Parameters: - states (str or list(str) or list(OptimizationStateEnum), optional) – Comma separated list of states, you can pass one CSV string or any enumerable of state IDS (string and enums are supported), defaults to None
- limit (int, optional) – Search limitation, defaults to None
- offset (int, optional) – Search starting position, defaults to None
-
remove
(ID)[source]¶ Remove an existing optimization belonging to an user.
See also
Route4Me API: https://route4me.io/docs/#remove-an-optimization
Parameters: ID (str) – Optimization Problem ID
Returns: Always
True
Return type: Raises: - Route4MeApiError – if Route4Me API returned not expected response
- Route4MeEntityNotFoundError – if optimization was not found
-
update
(ID, optimization_data=None, reoptimize=False)[source]¶ Update existing optimization problem, by changing some parameters or addresses. Optionally you can re-run the optimization engine for the optimization problem
Notice, that you can re-run optimization without modifications.
See also
Route4Me API: https://route4me.io/docs/#re-optimize-an-optimization
Parameters: - ID (str) – Optimization problem ID
- optimization_data (Optimization or dict, optional) – Optimization data, defaults to None
- reoptimize (bool, optional) – Whether to re-run optimization, defaults to
False
Returns: Updated optimization
Return type:
-
Data Structures¶
-
class
route4me.sdk.models.
Address
(raw=None)[source]¶ Bases:
route4me.sdk.models.BaseModel
Single Address, also known as Route Destination
See also
-
ID
¶ Route Destination ID
Internal unique address identifier
Getter: Gets value Setter: Sets value Return type: str
-
__init__
(raw=None)[source]¶ Create instance LOCALLY.
Use
add_address()
oradd_address()
to create new Address in the Route4Me APIParameters: raw (dict, optional) – Raw values for new address, example: add address to optimization, defaults to None
-
address
¶ The route’s Address Line
Note
In Route4Me API this field is known as
address
Getter: Get Setter: Set Return type: str
-
address_stop_type
¶ Address stop type
Getter: Get Setter: Set Return type: AddressStopTypeEnum
-
failed_geocoding
¶ True
means there was a geocoding attempt which failed.False
means success or no geocodingGetter: Get Setter: Set Return type: bool
-
geocoded
¶ True
means theaddress_string
field was successfully geocodedGetter: Get Setter: Set Return type: bool
-
latitude
¶ Latitude
Shoud be -90.0 ≤ lat ≤ 90
Note
In Route4Me API this field is known as
lat
Getter: Get Setter: Set Return type: float
-
longitude
¶ Longitude
Shoud be -180.0 ≤ lng ≤ 180
Note
In Route4Me API this field is known as
lng
Getter: Get Setter: Set Return type: float
-
-
class
route4me.sdk.models.
Optimization
(raw=None)[source]¶ Bases:
route4me.sdk.models.BaseModel
Optimization problem (or simple Optimization)
See also
-
__init__
(raw=None)[source]¶ Create instance LOCALLY.
Use
create()
to create new Optimization Problem in the Route4Me APIParameters: raw (dict, optional) – Raw values for new optimization, example: create optimization, defaults to None
-
addresses
¶ Addresses included to this Optimization Problem
<AUTO>
-
algorithm_type
¶ The algorithm type to be used
Getter: Get Setter: Set Return type: AlgorithmTypeEnum
-
device_id
¶ Device ID
32 Character MD5 String ID of the device that was used to plan this route
Getter: Get Setter: Set Return type: str
-
device_type
¶ Device type
The type of the device that is creating this Optimization Problem
Getter: Get Setter: Set Return type: DeviceTypeEnum
-
disable_optimization
¶ By disabling optimization, the route optimization engine will not resequence stops in your optimization problem
Getter: Get Setter: Set Return type: bool
-
links
¶ Links to the GET operations for the optimization problem
Getter: Get Setter: Set Deleter: Del Return type: dict or None
-
member_id
¶ Member ID
User ID who is assigned to this Optimization Problem
Getter: Get Setter: Set Return type: int
-
metric
¶ Metric
Getter: Get Setter: Set Return type: RouteMetricEnum
-
name
¶ The name of this optimization problem. This name will be accessible in the search API, and also will be displayed on the mobile device of a user
Getter: Get Setter: Set Return type: str
-
optimization_factor
¶ The driving directions can be generated biased for this selection. This has no impact on route sequencing.
Note
In Route4Me API this enum also known as
optimize
Getter: Get Setter: Set Return type: OptimizationFactorEnum
-
parts
¶ Legacy feature which permits a user to request an example number of optimized routes
Getter: Get Setter: Set Return type: int
-
quality
¶ Optimization quality
There are 3 types of optimization qualities that are optimizations goals, see
OptimizationQualityEnum
Getter: Get Setter: Set Return type: OptimizationQualityEnum
-
round_trip
¶ Round Trip
The tour type of this route. The optimization engine changes its behavior for round trip routes
Note
In Route4Me API this parameter is also known as
parameters.rt
Getter: Get Setter: Set Return type: bool
-
route_datetime
¶ Route DateTime
Note
In Route4Me API this parameter is broken into 2 parts: fields
parameters.route_date
andparameters.route_time
Getter: returns a datetime
combining values from two fieldsSetter: sets parameters.route_date
andparameters.route_time
in the raw dataReturn type: datetime
-
route_max_duration_sec
¶ Route maximum duration
How many seconds a route can last at most. Default is 86400 seconds = 24 hours
Getter: Get Setter: Set Return type: int
-
state
¶ The current state of the optimization
Getter: Get Setter: Set Return type: OptimizationStateEnum
-
travel_mode
¶ Travel mode
The mode of travel that the directions should be optimized for
Getter: Get Setter: Set Return type: TravelModeEnum
-
-
class
route4me.sdk.enums.
AddressStopTypeEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Address stop type
-
BREAK
= 'BREAK'¶ Break
-
DELIVERY
= 'DELIVERY'¶ Delivery
-
MEETUP
= 'MEETUP'¶ Meetup
-
PICKUP
= 'PICKUP'¶ Pickup
-
-
class
route4me.sdk.enums.
AlgorithmTypeEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
The algorithm type to be used.
-
ALG_LEGACY_DISTRIBUTED
= 101¶ ALG_LEGACY_DISTRIBUTED
-
ALG_NONE
= 100¶ ALG_NONE
-
BBCVRP
= 7¶ BBCVRP
-
CVRP_TW_MD
= 4¶ CVRP_TW_MD
-
CVRP_TW_SD
= 3¶ CVRP_TW_SD
-
TSP
= 1¶ TSP
-
TSP_TW
= 5¶ TSP_TW
-
TSP_TW_CR
= 6¶ TSP_TW_CR
-
VRP
= 2¶ VRP
-
-
class
route4me.sdk.enums.
DeviceTypeEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Device Type
The type of the device that is creating this route
-
ANDROID_PHONE
= 'android_phone'¶ Android phone
-
ANDROID_TABLET
= 'android_tablet'¶ Android tablet
-
IPAD
= 'ipad'¶ IPad
-
IPHONE
= 'iphone'¶ IPhone
-
WEB
= 'web'¶ Web
-
-
class
route4me.sdk.enums.
DistanceUnitEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Optimization
problem can be at one state at any given time-
KILOMETER
= 'km'¶ Kilometers
-
MILE
= 'mi'¶ Miles
-
-
class
route4me.sdk.enums.
OptimizationFactorEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
The driving directions can be generated biased for this selection. This has no impact on route sequencing.
Note
In Route4Me API this enum also known as
optimize
-
DISTANCE
= 'Distance'¶ Optimize by distance
-
TIME
= 'Time'¶ Optimize by time
-
TIME_TRAFFIC
= 'timeWithTraffic'¶ Optimize by time and traffic
-
-
class
route4me.sdk.enums.
OptimizationQualityEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Optimization Quality
-
BEST
= 3¶ Generate The Shortest And Quickest Possible Routes
-
FAST
= 1¶ Generate Optimized Routes As Quickly as Possible
-
MEDIUM
= 2¶ Generate Routes That Look Better On A Map
-
-
class
route4me.sdk.enums.
OptimizationStateEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
The distance measurement unit
-
COMPUTING_DIRECTIONS
= 6¶ Computing Directions
-
ERROR
= 5¶ Error
-
INITIAL
= 1¶ Initial
-
MATRIX_PROCESSING
= 2¶ Matrix Processing
-
OPTIMIZED
= 4¶ Optimized
-
OPTIMIZING
= 3¶ Optimizing
-
-
class
route4me.sdk.enums.
PEnum
[source]¶ Bases:
enum.Enum
An enumeration.
-
classmethod
parse_many
(value)[source]¶ Parses enum items from string/list
Parameters: value (str or list or OptimizationStateEnum) – string or enumerable with enum items Returns: list of parsed items Return type: str or list(str) or list(OptimizationStateEnum)
-
classmethod
-
class
route4me.sdk.enums.
RouteMetricEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Metric
-
EUCLIDEAN
= 1¶ Euclidean
-
EXACT2D
= 5¶ Exact 2d
-
GEODESIC
= 3¶ Geodesic
-
MANHATTAN
= 2¶ Manhattan
-
MATRIX
= 4¶ Matrix
-
-
class
route4me.sdk.enums.
TravelModeEnum
[source]¶ Bases:
route4me.sdk.enums.PEnum
Travel Mode
The mode of travel that the directions should be optimized for
-
CYCLING
= 'Cycling'¶ Cycling
-
DRIVING
= 'Driving'¶ Driving
-
TRANSIT
= 'Transit'¶ Transit
-
TRUCKING
= 'Trucking'¶ Trucking
-
WALKING
= 'Walking'¶ Walking
-
Utilities¶
Data structures¶
-
class
route4me.sdk.utils.
PagedList
(total=None, limit=None, offset=None, items=None)[source]¶ Bases:
list
Enumerable structure for array-like responses of the Route4Me API.
Several endpoints of Route4Me API return enumerable responses, which contains meta-information about paging (fields like
total
,limit
,offset
). This class allows enumerating requested data along with mentioned meta-data fields.-
limit
¶ Limit used during endpoint access
Getter: Value of limit
parameter if it was used in queryReturn type: int
-
Typeconv¶
-
route4me.sdk._internals.typeconv.
bool201
(boolvalue)[source]¶ Converts
bool
value to string:u'0'
oru'1'
— a returned value is in unicode.Parameters: boolvalue (bool) – Value to convert Returns: String '0'
or'1'
Return type: str Raises: TypeError – if not a bool argument was passed
-
route4me.sdk._internals.typeconv.
str2bool
(strvalue, default=None)[source]¶ Converts string value to
bool
. Can parse human-readable values, like'yes'
and'off'
.If value is not recognized – the value of the :paramref:`~str2bool.default` argument will be returned.
Note
str2bool()
is not an extension of defaultbool
. For example, it doesn’t recognize integer2
(or any other number) asTrue
value.Parameters: Returns: Parsed boolean value or :paramref:`~str2bool.default` (if defined). Not a bool value will be returned only if value was not recognized.
Return type: bool or *
Errors¶
-
exception
route4me.sdk.errors.
Route4MeApiError
(message, code='route4me.sdk.other', details=None, inner=None, method=None, url=None, status_code=None)[source]¶ Bases:
route4me.sdk.errors.Route4MeError
Error on Route4Me SDK
-
exception
route4me.sdk.errors.
Route4MeEntityNotFoundError
(message, code='route4me.sdk.other', details=None, inner=None)[source]¶ Bases:
route4me.sdk.errors.Route4MeError
Requested entity was not found on Route4Me
-
exception
route4me.sdk.errors.
Route4MeError
(message, code='route4me.sdk.other', details=None, inner=None)[source]¶ Bases:
Exception
Base (abstract) error-class
-
code
= None¶ Unique error code. Helps to distinguish different errors.
Type: str
-
details
= None¶ Some error details
Type: dict
-
inner
= None¶ Internal exception that describes an original error.
Type: Exception
-
-
exception
route4me.sdk.errors.
Route4MeNetworkError
(message, code='route4me.sdk.other', details=None, inner=None)[source]¶ Bases:
route4me.sdk.errors.Route4MeError
Route4Me SDK network/connection errors.
Occurs on:
- invalid SSL
- network timeout
- wrong redirects (Route4Me API doesn’t send redirect responses)
- no connection, no path to route (DNS)
-
exception
route4me.sdk.errors.
Route4MeValidationError
(message, code='route4me.sdk.other', details=None, inner=None)[source]¶ Bases:
route4me.sdk.errors.Route4MeError
Route4Me Validation error.
Variable has invalid format/data
CHANGELOG¶
The history of changes of the route4me/route4me-python-sdk
project.
This file MUST be filled only by maintainers, using messages from pull requests.
Please, follow this guide.
?? // unreleased¶
UPGRAGE TO ALPHA VERSION
- Optimizations Problem endpoints:
- list
- update
- reoptimize
Address
wrapper- Increase default timeout up to 10 seconds
- First working example (create optimization)
- Handle datetimes with TZ
- Parse enums function
PagedList
- structure for array-like responcesTiedList
- wrapper for array-like properties of the main Model
2017-08-25 // 0.1.0-dev.8¶
- Optimizations Problem endpoints:
- create
- get (get one)
- remove
2017-08-24 // 0.1.0-dev.7¶
- network agent
- core error handling (network access, SSL issues)
- scaffold for
Optimizations
- embed meta variables
__version__
,__author__
e.t.c - auto-generated docs
- build for python versions 2.6-3.6 on Travis CI and Appveyor
2017-08-13 // 0.1.0-dev.4¶
The first version was published on PyPI.
2014-07-01 // 0.0.1¶
First commit of the package.
License¶
Copyright (C) 2016-2017 Route4Me
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.