├── .coveragerc ├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── factordb ├── __init__.py ├── cli.py └── factordb.py ├── requirements.txt ├── setup.cfg ├── setup.py └── tests └── test_factordb.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | 4 | [report] 5 | exclude_lines = 6 | pragma: no cover 7 | 8 | if __name__ == .__main__.: 9 | ignore_errors = True 10 | 11 | omit = 12 | .venv/* 13 | .eggs/* 14 | *.egg-info/* 15 | tests/* 16 | setup.py 17 | 18 | [html] 19 | directory = coverage_report -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python-version: [3.6, 3.7, 3.8, 3.x, pypy3] 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-python@v2 12 | with: 13 | python-version: ${{ matrix.python-version }} 14 | - name: display python version 15 | run: python -c "import sys; print(sys.version)" 16 | - name: prepare before running tests 17 | run: | 18 | pip install --user -r requirements.txt 19 | pip install --user -e . 20 | - name: run tests 21 | run: python setup.py test 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/160d27e2bebf784c4f4a1e070df057f3868b62bc/Python.gitignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | .venv/ 86 | venv/ 87 | ENV/ 88 | 89 | # Spyder project settings 90 | .spyderproject 91 | 92 | # Rope project settings 93 | .ropeproject 94 | 95 | 96 | coverage_report/ 97 | coverage.xml 98 | README.rst 99 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 ryosan-470 (Ryosuke SATO) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.md 3 | include requirements.txt 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # factordb-python 2 | ![.github/workflows/test.yml](https://github.com/ryosan-470/factordb-python/workflows/.github/workflows/test.yml/badge.svg) 3 | [![PyPI](https://img.shields.io/pypi/l/factordb-python.svg?style=flat-square)](./LICENSE.md) 4 | [![PyPI](https://img.shields.io/pypi/pyversions/factordb-python.svg?style=flat-square)](https://pypi.python.org/pypi/factordb-python) 5 | [![PyPI](https://img.shields.io/pypi/status/factordb-python.svg?style=flat-square)](https://pypi.python.org/pypi/factordb-python) 6 | [![PyPI](https://img.shields.io/pypi/v/factordb-python.svg?style=flat-square)](https://pypi.python.org/pypi/factordb-python) 7 | 8 | The [FactorDB](https://factordb.com) is the database to store known factorizations for any number. 9 | This tool can use on your command line. 10 | And also you can use this tool with python 2 & 3 scripts. 11 | 12 | ## Installation 13 | The easiest way to install factordb-python is to use [pip](http://www.pip-installer.org/en/latest/): 14 | 15 | ```bash 16 | $ pip install factordb-python 17 | ``` 18 | 19 | or, if you are not installing in a `virtualenv`: 20 | 21 | ```bash 22 | $ sudo pip install factordb-python 23 | ``` 24 | 25 | If you have the factordb-python installed and want to upgrade to the latest version you can run: 26 | 27 | ```bash 28 | $ pip install --upgrade factordb-python 29 | ``` 30 | 31 | ## Getting Started 32 | 33 | ### CLI 34 | If you want to know the result of factorization of 16, you should type like this: 35 | 36 | ```bash 37 | $ factordb 16 38 | ``` 39 | 40 | Then, you can get the answer from factordb.com. 41 | 42 | ```bash 43 | $ factordb 16 44 | 2 2 2 2 45 | ``` 46 | 47 | If you want to know more detail of result, you can get an answer of JSON format. 48 | 49 | ```bash 50 | $ factordb --json 16 51 | {"id": "http://factordb.com/?id=2", "status": "FF", "factors": [2, 2, 2, 2]} 52 | ``` 53 | 54 | ### Library usage 55 | If you want to use this script with Python, you should type `import` statement on your code like this: 56 | 57 | ``` 58 | from factordb.factordb import FactorDB 59 | ``` 60 | 61 | Then, you can get the answer with Python lists. 62 | 63 | ``` 64 | In [1]: from factordb.factordb import FactorDB 65 | 66 | In [2]: f = FactorDB(16) 67 | 68 | In [3]: f.get_factor_list() 69 | Out[3]: [] 70 | 71 | In [4]: f.connect() 72 | Out[4]: 73 | 74 | In [5]: f.get_factor_list() 75 | Out[5]: [2, 2, 2, 2] 76 | 77 | In [6]: f.get_factor_from_api() 78 | Out[6]: [['2', 4]] 79 | 80 | In [7]: f.get_status() 81 | Out[7]: 'FF' 82 | ``` 83 | 84 | # License 85 | MIT 86 | -------------------------------------------------------------------------------- /factordb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryosan-470/factordb-python/f2f1d797490d4a7197c41926568d3065f5e58864/factordb/__init__.py -------------------------------------------------------------------------------- /factordb/cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function, unicode_literals 3 | import argparse 4 | import json 5 | import sys 6 | 7 | from .factordb import FactorDB, ENDPOINT 8 | 9 | 10 | def create_parser(): 11 | parser = argparse.ArgumentParser( 12 | description="The CLI for factordb.com" 13 | ) 14 | 15 | parser.add_argument( 16 | "number", 17 | type=int, 18 | help="The number what you want to factor" 19 | ) 20 | 21 | parser.add_argument( 22 | "--json", 23 | help="Print all data formated by JSON", 24 | action='store_true' 25 | ) 26 | 27 | return parser 28 | 29 | 30 | def main(): 31 | parser = create_parser() 32 | args = parser.parse_args() 33 | 34 | factordb = FactorDB(args.number) 35 | factordb.connect() 36 | 37 | if args.json: 38 | out = { 39 | "id": "{}/?id={}".format(ENDPOINT, factordb.get_id()), 40 | "status": factordb.get_status(), 41 | "factors": factordb.get_factor_list(), 42 | } 43 | ret = json.dumps(out) 44 | else: 45 | ret = " ".join(map(str, factordb.get_factor_list())) 46 | 47 | sys.stdout.write(ret) 48 | sys.stdout.write("\n") 49 | 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /factordb/factordb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function, unicode_literals 3 | 4 | import requests 5 | 6 | 7 | ENDPOINT = "http://factordb.com/api" 8 | 9 | 10 | class FactorDB(): 11 | def __init__(self, n): 12 | self.n = n 13 | self.result = None 14 | 15 | def connect(self, reconnect=False): 16 | if self.result and not reconnect: 17 | return self.result 18 | self.result = requests.get(ENDPOINT, params={"query": str(self.n)}) 19 | return self.result 20 | 21 | def get_id(self): 22 | if self.result: 23 | return self.result.json().get("id") 24 | return None 25 | 26 | def get_status(self): 27 | if self.result: 28 | return self.result.json().get("status") 29 | return None 30 | 31 | def get_factor_from_api(self): 32 | if self.result: 33 | return self.result.json().get("factors") 34 | return None 35 | 36 | def is_prime(self, include_probably_prime=False): 37 | if self.result: 38 | status = self.result.json().get("status") 39 | return status == 'P' or (status == 'PRP' and include_probably_prime) 40 | return None 41 | 42 | def get_factor_list(self): 43 | """ 44 | get_factors: [['2', 3], ['3', 2]] 45 | Returns: [2, 2, 2, 3, 3] 46 | """ 47 | factors = self.get_factor_from_api() 48 | if not factors: 49 | return [] 50 | ml = [[int(x)] * y for x, y in factors] 51 | return [y for x in ml for y in x] 52 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [tool:pytest] 5 | addopts = --verbose 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup, find_packages 3 | 4 | 5 | def _read_from_requirements(): 6 | with open('requirements.txt') as f: 7 | return f.read().splitlines() 8 | 9 | 10 | def _load_description(): 11 | with open("README.md", "r") as fh: 12 | return fh.read() 13 | 14 | 15 | setup_options = dict( 16 | name='factordb-python', 17 | version='1.3.0', 18 | description='The FactorDB client library with Python', 19 | long_description=_load_description(), 20 | long_description_content_type='text/markdown', 21 | author='Ryosuke SATO (@ryosan-470)', 22 | author_email='rskjtwp@gmail.com', 23 | url='https://github.com/ryosan-470/factordb-python', 24 | py_modules=['factordb'], 25 | packages=find_packages(), 26 | entry_points={ 27 | 'console_scripts': 'factordb = factordb.cli:main' 28 | }, 29 | install_requires=_read_from_requirements(), 30 | setup_requires=['pytest-runner'], 31 | tests_require=['pytest'], 32 | license="MIT License", 33 | classifiers=( 34 | "Development Status :: 4 - Beta", 35 | "Environment :: Console", 36 | "Programming Language :: Python :: 2", 37 | "Programming Language :: Python :: 2.7", 38 | "Programming Language :: Python :: 3", 39 | "Programming Language :: Python :: 3.4", 40 | "Programming Language :: Python :: 3.5", 41 | "Programming Language :: Python :: 3.6", 42 | "Programming Language :: Python :: 3.7", 43 | "Programming Language :: Python :: 3.8", 44 | "Topic :: Utilities", 45 | ), 46 | ) 47 | 48 | setup(**setup_options) 49 | -------------------------------------------------------------------------------- /tests/test_factordb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function, unicode_literals 3 | import unittest 4 | 5 | from factordb.factordb import FactorDB 6 | 7 | 8 | class FactorDBTestCase(unittest.TestCase): 9 | def test_factordb_api_1(self): 10 | factordb = FactorDB(1) 11 | factordb.connect() 12 | 13 | self.__check_testcase(factordb, { 14 | 'id': -1, 15 | 'status': "Unit", 16 | 'factors': [] 17 | }) 18 | 19 | self.assertFalse(factordb.is_prime()) 20 | 21 | def test_factordb_api_16(self): 22 | factordb = FactorDB(16) 23 | factordb.connect() 24 | 25 | self.__check_testcase(factordb, { 26 | 'id': '2', 27 | 'status': 'FF', 28 | 'factors': [2, 2, 2, 2] 29 | }) 30 | 31 | self.assertFalse(factordb.is_prime()) 32 | 33 | def test_factordb_api_large_composite_number(self): 34 | """ 35 | Large composite number tests from RSA 768 challenges 36 | """ 37 | p = int("3347807169895689878604416984821269081770479498371376856891243" 38 | "1388982883793878002287614711652531743087737814467999489") 39 | q = int("3674604366679959042824463379962795263227915816434308764267603" 40 | "2283815739666511279233373417143396810270092798736308917") 41 | 42 | factordb = FactorDB(p * q) 43 | factordb.connect() 44 | 45 | self.__check_testcase(factordb, { 46 | 'id': '1100000000193442616', 47 | 'status': 'FF', 48 | 'factors': [p, q] 49 | } 50 | ) 51 | 52 | self.assertFalse(factordb.is_prime()) 53 | 54 | def test_factordb_api_prime(self): 55 | factordb = FactorDB(7) 56 | factordb.connect() 57 | 58 | self.__check_testcase(factordb, { 59 | 'id': '7', 60 | 'status': 'P', 61 | 'factors': [7] 62 | }) 63 | 64 | self.assertTrue(factordb.is_prime()) 65 | 66 | def __check_testcase(self, factordb, expected): 67 | self.assertEqual(factordb.get_id(), expected['id']) 68 | self.assertEqual(factordb.get_status(), expected['status']) 69 | self.assertListEqual(factordb.get_factor_list(), expected['factors']) 70 | --------------------------------------------------------------------------------