├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── conf.py ├── eulerlib.rst ├── index.rst ├── make.bat └── modules.rst ├── eulerlib ├── __init__.py ├── _exceptions.py ├── _tests │ ├── __init__.py │ └── test_eulerlib.py ├── etc.py ├── fibonacci.py ├── numtheory.py ├── prime_numbers.py └── pythagoras.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # Emacs/gedit backups 6 | *~ 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 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE.txt -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | EulerLib 2 | ******** 3 | 4 | `eulerlib`__ is a library of recreational mathematics and number theory related 5 | functions inspired by `Project Euler`_. Available functions include: 6 | 7 | * Prime number generation 8 | * Divisor functions (sigma functions) 9 | * Euler's totient function 10 | * Greatest Common Divisor (GCD) using Euclid's algorithm 11 | * Least Common Multiple (LCM) 12 | * Integer square root 13 | * Fibonacci numbers 14 | * Pandigital numbers 15 | * Palindrome numbers 16 | * Pythagorean triples 17 | 18 | Functions from this library can be used to solve recreational mathematics and 19 | programming problems such as problems in Project Euler. 20 | 21 | Installation 22 | ------------ 23 | `eulerlib`__ is avalaible through Python Package Index (`PyPI`_) using 24 | `pip`_. :: 25 | 26 | >>> pip install --upgrade eulerlib 27 | 28 | To uninstall using `pip`_. :: 29 | 30 | >>> pip uninstall eulerlib 31 | 32 | Usage 33 | ----- 34 | In Python console you can import functions/classes from eulerlib as needed. :: 35 | 36 | >>> from eulerlib import primes 37 | >>> p10 = primes(10) 38 | >>> print(p10) 39 | [2, 3, 5, 7] 40 | 41 | The *Divisors* class implements functions related to prime factorization, 42 | sigma functions etc. :: 43 | 44 | >>> from eulerlib import Divisors 45 | >>> mydiv = Divisors(10000) 46 | >>> div84 = mydiv.divisors(84) #divisors of 84 47 | >>> print(div84) 48 | [1, 2, 3, 4, 6, 7, 12, 14, 21, 28, 42, 84] 49 | >>> pf840 = mydiv.prime_factors(840) # prime factors of 840 50 | >>> print(pf840) 51 | [(2, 3), (3, 1), (5, 1), (7, 1)] 52 | 53 | **Example**: Solved `Project Euler`_ `problem 3`_. :: 54 | 55 | from eulerlib import is_square, primes 56 | #get approximate square root of number since 57 | #largest prime factor < sq. root 58 | (is_sq, sqroot) = is_square(600851475143L) 59 | #get a list of primes less than the approx. square root. 60 | test_primes = primes(sqroot + 1L) 61 | #test the primes from the list to find the largest factor 62 | len_p = len(test_primes) 63 | for i in range(1,len_p+1): 64 | j = 0 - i 65 | test_fact = test_primes[j] 66 | if 600851475143L%test_fact == 0: 67 | break 68 | answer = test_fact #Set the answer 69 | 70 | See complete `documentation`_. 71 | 72 | Modules 73 | ------- 74 | +----------------+------------------------------------------------------------+ 75 | |prime_numbers.py| Functions to generate lists of primes. | 76 | +----------------+------------------------------------------------------------+ 77 | |numtheory.py | * Euler's divisor functions (sigma funtions) | 78 | | | * Euler's totient function (phi function) | 79 | | | * Prime factors of a number | 80 | | | * Divisors of a number | 81 | | | * Greatest Common Divisor (GCD) | 82 | | | * Least Common Multiple (LCM) | 83 | | | * Digital root and digital sum of a number | 84 | +----------------+------------------------------------------------------------+ 85 | |fibonacci.py | Functions related to the Fibonacci sequence. | 86 | +----------------+------------------------------------------------------------+ 87 | |pythagoras.py | Functions related to Pythagorean triples. | 88 | +----------------+------------------------------------------------------------+ 89 | |etc.py | Miscellaneous functions: | 90 | | | | 91 | | | * Pandigital numbers | 92 | | | * Conversion from decimal to base n (2-36) | 93 | | | * Number to lists and vice versa | 94 | | | * Palindrome numbers | 95 | +----------------+------------------------------------------------------------+ 96 | 97 | Development 98 | ----------- 99 | Source code repositories (`GitHub`_, `BitBucket`_) are available. 100 | `Bug reports`_ and suggestions are most welcome. 101 | 102 | License 103 | ------- 104 | eulerlib is licensed under `Apache License 2.0`_. 105 | 106 | .. _Project Euler: https://projecteuler.net 107 | .. _PyPI: https://pypi.python.org/pypi 108 | .. _pip: https://pip.pypa.io 109 | .. _Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0.html 110 | .. _problem 3: https://projecteuler.net/problem=3 111 | .. _GitHub: https://github.com/transmogrifier/eulerlib 112 | .. _BitBucket: https://bitbucket.org/transmogrifier/eulerlib 113 | .. _Bug reports: https://github.com/transmogrifier/eulerlib/issues 114 | .. _documentation: http://pythonhosted.org/eulerlib 115 | .. _eulerlib1: https://pypi.python.org/pypi/eulerlib 116 | __ eulerlib1_ 117 | __ eulerlib1_ -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/eulerlib.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/eulerlib.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/eulerlib" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/eulerlib" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # eulerlib documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Oct 29 17:10:41 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('.\..\eulerlib')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.viewcode', 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix of source filenames. 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = u'eulerlib' 50 | copyright = u'2015, Sameer Marathe' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = '0.2' 58 | # The full version, including alpha/beta/rc tags. 59 | release = 'v0.2-beta' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | #language = None 64 | 65 | # There are two options for replacing |today|: either, you set today to some 66 | # non-false value, then it is used: 67 | #today = '' 68 | # Else, today_fmt is used as the format for a strftime call. 69 | #today_fmt = '%B %d, %Y' 70 | 71 | # List of patterns, relative to source directory, that match files and 72 | # directories to ignore when looking for source files. 73 | exclude_patterns = ['_build'] 74 | 75 | # The reST default role (used for this markup: `text`) to use for all 76 | # documents. 77 | #default_role = None 78 | 79 | # If true, '()' will be appended to :func: etc. cross-reference text. 80 | #add_function_parentheses = True 81 | 82 | # If true, the current module name will be prepended to all description 83 | # unit titles (such as .. function::). 84 | #add_module_names = True 85 | 86 | # If true, sectionauthor and moduleauthor directives will be shown in the 87 | # output. They are ignored by default. 88 | #show_authors = False 89 | 90 | # The name of the Pygments (syntax highlighting) style to use. 91 | pygments_style = 'sphinx' 92 | 93 | # A list of ignored prefixes for module index sorting. 94 | #modindex_common_prefix = [] 95 | 96 | # If true, keep warnings as "system message" paragraphs in the built documents. 97 | #keep_warnings = False 98 | 99 | 100 | # -- Options for HTML output ---------------------------------------------- 101 | 102 | # The theme to use for HTML and HTML Help pages. See the documentation for 103 | # a list of builtin themes. 104 | html_theme = 'default' 105 | 106 | # Theme options are theme-specific and customize the look and feel of a theme 107 | # further. For a list of options available for each theme, see the 108 | # documentation. 109 | #html_theme_options = {} 110 | 111 | # Add any paths that contain custom themes here, relative to this directory. 112 | #html_theme_path = [] 113 | 114 | # The name for this set of Sphinx documents. If None, it defaults to 115 | # " v documentation". 116 | #html_title = None 117 | 118 | # A shorter title for the navigation bar. Default is the same as html_title. 119 | #html_short_title = None 120 | 121 | # The name of an image file (relative to this directory) to place at the top 122 | # of the sidebar. 123 | #html_logo = None 124 | 125 | # The name of an image file (within the static path) to use as favicon of the 126 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 127 | # pixels large. 128 | #html_favicon = None 129 | 130 | # Add any paths that contain custom static files (such as style sheets) here, 131 | # relative to this directory. They are copied after the builtin static files, 132 | # so a file named "default.css" will overwrite the builtin "default.css". 133 | html_static_path = ['_static'] 134 | 135 | # Add any extra paths that contain custom files (such as robots.txt or 136 | # .htaccess) here, relative to this directory. These files are copied 137 | # directly to the root of the documentation. 138 | #html_extra_path = [] 139 | 140 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 141 | # using the given strftime format. 142 | #html_last_updated_fmt = '%b %d, %Y' 143 | 144 | # If true, SmartyPants will be used to convert quotes and dashes to 145 | # typographically correct entities. 146 | #html_use_smartypants = True 147 | 148 | # Custom sidebar templates, maps document names to template names. 149 | #html_sidebars = {} 150 | 151 | # Additional templates that should be rendered to pages, maps page names to 152 | # template names. 153 | #html_additional_pages = {} 154 | 155 | # If false, no module index is generated. 156 | #html_domain_indices = True 157 | 158 | # If false, no index is generated. 159 | #html_use_index = True 160 | 161 | # If true, the index is split into individual pages for each letter. 162 | #html_split_index = False 163 | 164 | # If true, links to the reST sources are added to the pages. 165 | #html_show_sourcelink = True 166 | 167 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 168 | #html_show_sphinx = True 169 | 170 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 171 | #html_show_copyright = True 172 | 173 | # If true, an OpenSearch description file will be output, and all pages will 174 | # contain a tag referring to it. The value of this option must be the 175 | # base URL from which the finished HTML is served. 176 | #html_use_opensearch = '' 177 | 178 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 179 | #html_file_suffix = None 180 | 181 | # Output file base name for HTML help builder. 182 | htmlhelp_basename = 'eulerlibdoc' 183 | 184 | 185 | # -- Options for LaTeX output --------------------------------------------- 186 | 187 | latex_elements = { 188 | # The paper size ('letterpaper' or 'a4paper'). 189 | #'papersize': 'letterpaper', 190 | 191 | # The font size ('10pt', '11pt' or '12pt'). 192 | #'pointsize': '10pt', 193 | 194 | # Additional stuff for the LaTeX preamble. 195 | #'preamble': '', 196 | } 197 | 198 | # Grouping the document tree into LaTeX files. List of tuples 199 | # (source start file, target name, title, 200 | # author, documentclass [howto, manual, or own class]). 201 | latex_documents = [ 202 | ('index', 'eulerlib.tex', u'eulerlib Documentation', 203 | u'Sameer Marathe', 'manual'), 204 | ] 205 | 206 | # The name of an image file (relative to this directory) to place at the top of 207 | # the title page. 208 | #latex_logo = None 209 | 210 | # For "manual" documents, if this is true, then toplevel headings are parts, 211 | # not chapters. 212 | #latex_use_parts = False 213 | 214 | # If true, show page references after internal links. 215 | #latex_show_pagerefs = False 216 | 217 | # If true, show URL addresses after external links. 218 | #latex_show_urls = False 219 | 220 | # Documents to append as an appendix to all manuals. 221 | #latex_appendices = [] 222 | 223 | # If false, no module index is generated. 224 | #latex_domain_indices = True 225 | 226 | 227 | # -- Options for manual page output --------------------------------------- 228 | 229 | # One entry per manual page. List of tuples 230 | # (source start file, name, description, authors, manual section). 231 | man_pages = [ 232 | ('index', 'eulerlib', u'eulerlib Documentation', 233 | [u'Sameer Marathe'], 1) 234 | ] 235 | 236 | # If true, show URL addresses after external links. 237 | #man_show_urls = False 238 | 239 | 240 | # -- Options for Texinfo output ------------------------------------------- 241 | 242 | # Grouping the document tree into Texinfo files. List of tuples 243 | # (source start file, target name, title, author, 244 | # dir menu entry, description, category) 245 | texinfo_documents = [ 246 | ('index', 'eulerlib', u'eulerlib Documentation', 247 | u'Sameer Marathe', 'eulerlib', 'One line description of project.', 248 | 'Miscellaneous'), 249 | ] 250 | 251 | # Documents to append as an appendix to all manuals. 252 | #texinfo_appendices = [] 253 | 254 | # If false, no module index is generated. 255 | #texinfo_domain_indices = True 256 | 257 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 258 | #texinfo_show_urls = 'footnote' 259 | 260 | # If true, do not generate a @detailmenu in the "Top" node's menu. 261 | #texinfo_no_detailmenu = False 262 | 263 | 264 | # -- Options for Epub output ---------------------------------------------- 265 | 266 | # Bibliographic Dublin Core info. 267 | epub_title = u'eulerlib' 268 | epub_author = u'Sameer Marathe' 269 | epub_publisher = u'Sameer Marathe' 270 | epub_copyright = u'2015, Sameer Marathe' 271 | 272 | # The basename for the epub file. It defaults to the project name. 273 | #epub_basename = u'eulerlib' 274 | 275 | # The HTML theme for the epub output. Since the default themes are not optimized 276 | # for small screen space, using the same theme for HTML and epub output is 277 | # usually not wise. This defaults to 'epub', a theme designed to save visual 278 | # space. 279 | #epub_theme = 'epub' 280 | 281 | # The language of the text. It defaults to the language option 282 | # or en if the language is not set. 283 | #epub_language = '' 284 | 285 | # The scheme of the identifier. Typical schemes are ISBN or URL. 286 | #epub_scheme = '' 287 | 288 | # The unique identifier of the text. This can be a ISBN number 289 | # or the project homepage. 290 | #epub_identifier = '' 291 | 292 | # A unique identification for the text. 293 | #epub_uid = '' 294 | 295 | # A tuple containing the cover image and cover page html template filenames. 296 | #epub_cover = () 297 | 298 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 299 | #epub_guide = () 300 | 301 | # HTML files that should be inserted before the pages created by sphinx. 302 | # The format is a list of tuples containing the path and title. 303 | #epub_pre_files = [] 304 | 305 | # HTML files shat should be inserted after the pages created by sphinx. 306 | # The format is a list of tuples containing the path and title. 307 | #epub_post_files = [] 308 | 309 | # A list of files that should not be packed into the epub file. 310 | epub_exclude_files = ['search.html'] 311 | 312 | # The depth of the table of contents in toc.ncx. 313 | #epub_tocdepth = 3 314 | 315 | # Allow duplicate toc entries. 316 | #epub_tocdup = True 317 | 318 | # Choose between 'default' and 'includehidden'. 319 | #epub_tocscope = 'default' 320 | 321 | # Fix unsupported image types using the PIL. 322 | #epub_fix_images = False 323 | 324 | # Scale large images. 325 | #epub_max_image_width = 0 326 | 327 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 328 | #epub_show_urls = 'inline' 329 | 330 | # If false, no index is generated. 331 | #epub_use_index = True 332 | 333 | -------------------------------------------------------------------------------- /docs/eulerlib.rst: -------------------------------------------------------------------------------- 1 | eulerlib package 2 | ================ 3 | 4 | .. automodule:: eulerlib 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | Submodules 10 | ---------- 11 | 12 | eulerlib.prime_numbers module 13 | ----------------------------- 14 | 15 | .. automodule:: eulerlib.prime_numbers 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | eulerlib.numtheory module 21 | ------------------------- 22 | 23 | .. automodule:: eulerlib.numtheory 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | eulerlib.fibonacci module 29 | ------------------------- 30 | 31 | .. automodule:: eulerlib.fibonacci 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | eulerlib.pythagoras module 37 | -------------------------- 38 | 39 | .. automodule:: eulerlib.pythagoras 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | eulerlib.etc module 45 | ------------------- 46 | 47 | .. automodule:: eulerlib.etc 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | 52 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. eulerlib documentation master file, created by 2 | sphinx-quickstart on Wed Oct 29 17:10:41 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to eulerlib's documentation! 7 | ==================================== 8 | 9 | .. include:: ../README.rst 10 | 11 | .. _reference: 12 | 13 | Reference 14 | ********* 15 | 16 | .. toctree:: 17 | :maxdepth: 4 18 | 19 | eulerlib 20 | 21 | Indices and tables 22 | ------------------ 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\eulerlib.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\eulerlib.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | eulerlib 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | eulerlib 8 | -------------------------------------------------------------------------------- /eulerlib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Library of number theory related functions inspired by Project Euler. 17 | 18 | .. moduleauthor:: Sameer Marathe 19 | 20 | """ 21 | 22 | __all__ = ["numtheory", "prime_numbers", "fibonacci", "pythagoras", "etc"] 23 | from .numtheory import * 24 | from .prime_numbers import * 25 | from .fibonacci import * 26 | from .pythagoras import * 27 | from .etc import * -------------------------------------------------------------------------------- /eulerlib/_exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | class EulerlibInputError(Exception): 17 | 18 | def __init__(self,module,method,message): 19 | self.module = module 20 | self.method = method 21 | self.message = message 22 | 23 | def __str__(self): 24 | msg = "In Eulerlib.{0}.{1}: {2}.".format(self.module, 25 | self.method, 26 | self.message) 27 | return msg -------------------------------------------------------------------------------- /eulerlib/_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """TestCases.""" 17 | 18 | 19 | -------------------------------------------------------------------------------- /eulerlib/_tests/test_eulerlib.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib._tests.test_eulerlib 18 | :synopsis: Unittests for eulerlib. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | from unittest import TestCase 25 | import eulerlib.numtheory as NT 26 | import eulerlib.etc as ETC 27 | import eulerlib.fibonacci as FIBO 28 | import eulerlib.prime_numbers as P 29 | 30 | class TestPrimes(TestCase): 31 | 32 | def test_primes1e1(self): 33 | test_primes = P.primes(10) 34 | self.assertEqual(len(test_primes),4) #Number of prime 35 | self.assertEqual(test_primes[-1],7) #Last prime in the list 36 | 37 | def test_primes1e2(self): 38 | test_primes = P.primes(100) 39 | self.assertEqual(len(test_primes),25) 40 | self.assertEqual(test_primes[-1],97) 41 | 42 | def test_primes1e3(self): 43 | test_primes = P.primes(1000) 44 | self.assertEqual(len(test_primes),168) 45 | self.assertEqual(test_primes[-1],997) 46 | 47 | def test_primes1e4(self): 48 | test_primes = P.primes(10000) 49 | self.assertEqual(len(test_primes),1229) 50 | self.assertEqual(test_primes[-1],9973) 51 | 52 | def test_primes1e5(self): 53 | test_primes = P.primes(100000) 54 | self.assertEqual(len(test_primes),9592) 55 | self.assertEqual(test_primes[-1],99991) 56 | 57 | def test_primes1e6(self): 58 | test_primes = P.primes(1000000) 59 | self.assertEqual(len(test_primes),78498) 60 | 61 | def test_primes_wgen(self): 62 | test_wgen = P.primes_wheel_fact(10**7) 63 | self.assertEqual(len(test_wgen),664579) 64 | self.assertEqual(test_wgen[600000-1],8960453) 65 | 66 | class TestNumtheory(TestCase): 67 | 68 | def test_is_square(self): 69 | (is_sq,sqroot) = NT.is_square(99460729) 70 | self.assertTrue(is_sq) 71 | self.assertEqual(sqroot,9973) 72 | 73 | def test_gcd(self): 74 | gcd_test = NT.gcd(231675,86492) 75 | self.assertEqual(gcd_test,3089) 76 | 77 | def test_lcm(self): 78 | lcm_test = NT.lcm(8345,23579) 79 | self.assertEqual(lcm_test,196766755) 80 | 81 | def test_lcm_n(self): 82 | lcmn_test = NT.lcm_n([8345, 23579, 174]) 83 | self.assertEqual(lcmn_test,34237415370) 84 | 85 | def test_nCr(self): 86 | nCr_test = NT.nCr(45,7) 87 | self.assertEqual(nCr_test,45379620) 88 | 89 | def test_nPr(self): 90 | nPr_test = NT.nPr(45,7) 91 | self.assertEqual(nPr_test,228713284800) 92 | 93 | def test_digital_sum(self): 94 | test_dsum = NT.digital_sum(228713284800) 95 | self.assertEqual(test_dsum,45) 96 | 97 | def test_digital_root(self): 98 | test_droot = NT.digital_root(228713284800) 99 | self.assertEqual(test_droot,9) 100 | 101 | def test_divisors_class(self): 102 | myDiv = NT.Divisors(1000000) 103 | pf840 = myDiv.prime_factors(840) 104 | self.assertEqual(len(pf840),4) 105 | self.assertEqual(pf840[0][1],3) 106 | pf14688 = myDiv.prime_factors_only(14688) 107 | self.assertEqual(pf14688[-1],17) 108 | self.assertEqual(len(pf14688),3) 109 | sf14688 = myDiv.sigma_function(14688) 110 | self.assertEqual(sf14688[0],48) 111 | self.assertEqual(sf14688[1],45360) 112 | self.assertEqual(sf14688[2],30672) 113 | tot60 = myDiv.phi(60) 114 | self.assertEqual(tot60,16) 115 | div14688 = myDiv.divisors(14688) 116 | self.assertEqual(len(div14688),sf14688[0]) 117 | self.assertEqual(sum(div14688),sf14688[1]) 118 | 119 | class TestEtc(TestCase): 120 | def test_dec_to_base(self): 121 | hex23456 = ETC.decimal_to_base(23456,16) 122 | self.assertEqual(hex23456,'5ba0') 123 | bin23456 = ETC.decimal_to_base(23456,2) 124 | self.assertEqual(bin23456,'101101110100000') 125 | dec1000 = ETC.decimal_to_base(1000,10) 126 | self.assertEqual(dec1000,'1000') 127 | 128 | def test_is_pandigital(self): 129 | isPD521346 = ETC.is_pandigital(521346,1,6) 130 | self.assertTrue(isPD521346) 131 | 132 | def test_is_palindrome(self): 133 | self.assertTrue(ETC.is_palindrome(3452543)) 134 | self.assertTrue(ETC.is_palindrome(23466432)) 135 | self.assertFalse(ETC.is_palindrome(345443)) 136 | 137 | def test_word_num_val(self): 138 | val_sameer = ETC.word_numerical_val("Sameer") 139 | self.assertEqual(val_sameer,61) 140 | 141 | def test_num_to_list(self): 142 | list230456 = ETC.num_to_list(230456) 143 | num230456 = ETC.list_to_num(list230456) 144 | self.assertEqual(num230456,230456) 145 | self.assertEqual(len(list230456),6) 146 | 147 | class TestFibonacci(TestCase): 148 | def test_first_n_fibo(self): 149 | fibo20_0 = FIBO.first_n_fibo(20,0) 150 | self.assertEqual(fibo20_0[-1],4181) 151 | fibo38_1 = FIBO.first_n_fibo(38) 152 | self.assertEqual(fibo38_1[-1],39088169) 153 | 154 | def test_fibo_less_than(self): 155 | fibo1000 = FIBO.fibo_less_than(1000) 156 | self.assertEqual(fibo1000[-1],987) 157 | self.assertEqual(len(fibo1000),16) 158 | fibo1e6 = FIBO.fibo_less_than(1000000,0) 159 | self.assertEqual(fibo1e6[-1],832040) 160 | self.assertEqual(len(fibo1e6),31) 161 | 162 | def test_fibo_num_digits(self): 163 | fibo5 = FIBO.fibo_num_digits(5) 164 | self.assertEqual(fibo5[-1],10946) 165 | self.assertEqual(len(fibo5),21) 166 | fibo6 = FIBO.fibo_num_digits(6,0) 167 | self.assertEqual(fibo6[-1],121393) 168 | self.assertEqual(len(fibo6),27) 169 | 170 | -------------------------------------------------------------------------------- /eulerlib/etc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib.numtheory 18 | :synopsis: Miscellaneous functions. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | from ._exceptions import EulerlibInputError 25 | 26 | __all__ = ["decimal_to_base", "is_palindrome", "is_pandigital", "num_to_list", 27 | "list_to_num", "word_numerical_val", "write_number", 28 | "collapse_lists"] 29 | 30 | def decimal_to_base(num,b): 31 | """Converts *num* from decimal to base *b* 32 | 33 | :param num: A decimal integer 34 | :param b: A decimal integer value for the base. 1 < b <= 36 35 | :returns: String that represents decimal *num* in base *b* 36 | 37 | For example:: 38 | 39 | >>> decimal_to_base(8,2) 40 | '1000' 41 | """ 42 | symbols = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', 43 | 'q','r','s','t','u','v','w','x','y','z'] 44 | numstring = "" 45 | if(b > 36 or b < 2): 46 | return numstring 47 | else: 48 | done = False 49 | while(not done): 50 | cur_digit = num%b 51 | if(cur_digit > 9): 52 | cur_str = symbols[cur_digit - 10] 53 | else: 54 | cur_str = str(cur_digit) 55 | numstring = cur_str+numstring 56 | num = num//b 57 | if(num < b): 58 | if(num != 0): 59 | if(num > 9): 60 | cur_str = symbols[num - 10] 61 | else: 62 | cur_str = str(num) 63 | numstring = cur_str + numstring 64 | done = True 65 | return numstring 66 | 67 | 68 | def is_palindrome(num,base=10): 69 | """Returns *True* if decimal number *num* is a Palindrome number in *base*. 70 | 71 | :param num: Decimal number to be tested 72 | :param base: Base of the number system in which *num* is to be tested. 73 | (Default = 10) 74 | :returns: *True* if *num* is palindromic in *base* 75 | 76 | For example:: 77 | 78 | >>> is_palidrome(3875783) #Test in default base = 10 79 | >>> True 80 | >>> is_palidrome(9,2) # 9 in base 2 is 1001 81 | >>> True 82 | """ 83 | str_num = str(num) 84 | if base != 10: 85 | str_num = decimal_to_base(num,base) 86 | rev_num = str_num[::-1] 87 | if(str_num == rev_num): 88 | return True 89 | else: 90 | return False 91 | 92 | 93 | def is_pandigital(num,start,stop): 94 | """Returns *True* if *num* is Pandigital. 95 | 96 | :param num: An integer to be checked for pandigitalness. 97 | :param start: Strating digit. 0 <= start <= 9 98 | :param stop: Ending digit start < stop <= 9 99 | 100 | For example:: 101 | 102 | >>> is_pandigital(2134, 1, 4) 103 | True 104 | """ 105 | if(start < 0 or stop < 0 or start >9 or stop >9): 106 | raise EulerlibInputError('etc','is_pandigital', 107 | 'start or stop parameter value is incorrect') 108 | elif(stop < start): 109 | raise EulerlibInputError('etc','is_pandigital', 110 | 'start should be smaller than stop') 111 | else: 112 | test = list(range(start,stop+1)) 113 | nl = num_to_list(num) 114 | if(len(nl) != (stop-start)+1): 115 | return False 116 | else: 117 | nl.sort() 118 | if(nl == test): 119 | return True 120 | else: 121 | return False 122 | 123 | 124 | def num_to_list(num): 125 | """Returns a list of digits in num (most significant digit first).""" 126 | result = [] 127 | if (num < 0): 128 | return result 129 | else: 130 | str_num = str(num) 131 | for c in str_num: 132 | result.append(int(c)) 133 | return result 134 | 135 | 136 | def list_to_num(list_of_digits): 137 | """Returns an integer from a *list_of_digits*.""" 138 | num = 0 139 | if(list_of_digits == []): 140 | return num 141 | test = range(10) 142 | for digit in list_of_digits: 143 | if(not (digit in test)): 144 | raise EulerlibInputError('etc','list_to_num', 145 | 'Invalid member in list_of_digits') 146 | num = 0 147 | break 148 | num = num*10 + digit 149 | return num 150 | 151 | 152 | def word_numerical_val(word): 153 | """Returns the sum of numerical value of each letter in the 154 | word based on its position in the alphabet. 155 | """ 156 | import string 157 | num_val = 0 158 | lcase = string.ascii_lowercase 159 | test_word = word.lower() 160 | for char in test_word: 161 | if not char in lcase: 162 | raise EulerlibInputError('etc','word_numerical_val', 163 | 'Illegal character in the word') 164 | num_val = 0 165 | break 166 | else: 167 | loc = lcase.find(char) 168 | if (loc != -1): 169 | num_val = num_val + loc + 1 170 | else: 171 | raise EulerlibInputError('etc','word_numerical_val', 172 | 'Illegal character in the word') 173 | num_val = 0 174 | break 175 | return num_val 176 | 177 | 178 | def write_number(num): 179 | """Return the written English language string representation of *num* 180 | 181 | :param num: An integer number 182 | 183 | For example:: 184 | 185 | >>> write_num(132) 186 | >>> 'one hundred and thirty-two' 187 | """ 188 | ones = ["zero","one","two","three","four","five","six","seven","eight", 189 | "nine"] 190 | teens = ["eleven","twelve","thirteen","fourteen","fifteen","sixteen", 191 | "seventeen","eighteen","nineteen"] 192 | tens = ["ten","twenty","thirty","forty","fifty","sixty","seventy","eighty", 193 | "ninety"] 194 | hundred = " hundred" 195 | hundred_and = " hundred and " 196 | thousand = " thousand" 197 | thousand_and = " thousand " 198 | minus = "minus " 199 | num1 = abs(num) 200 | number = "" 201 | if num1 < 1000000: 202 | if num1 < 10: 203 | number = ones[num1] 204 | elif num1 >= 10 and num1 < 100: 205 | if num1%10 == 0: 206 | #divisible by 10 207 | number = tens[(num1//10)-1] 208 | elif num1 > 10 and num1 < 20: 209 | #teens 210 | number = teens[num1-11] 211 | else: 212 | #create number by recursion 213 | tens_val = num1//10 214 | ones_val = num1%10 215 | number = tens[tens_val-1] + "-" + ones[ones_val] 216 | elif num1 >= 100 and num1 < 1000: 217 | if num1%100 == 0: 218 | hundreds_val = num1//100 219 | number = write_number(hundreds_val) + hundred 220 | else: 221 | hundreds_val = num1//100 222 | rest_of_num = num1%100 223 | number = ones[hundreds_val] + hundred_and + \ 224 | write_number(rest_of_num) 225 | else: 226 | thousands_val = num1//1000 227 | rest_of_num = num1%1000 228 | if rest_of_num == 0: 229 | number = write_number(thousands_val) + thousand 230 | else: 231 | number = write_number(thousands_val) + thousand_and + \ 232 | write_number(rest_of_num) 233 | if num < 0: 234 | number = minus + number 235 | else: 236 | number = "out of range" 237 | return number 238 | 239 | 240 | def collapse_lists(list1,list2,compf,pathf): 241 | """Function to collapse two lists into a single list based on comparison 242 | and path functions. 243 | 244 | :param list1: First list 245 | :param list2: Second list 246 | :param compf: Comparator function to compare values returned by 247 | path function. 248 | :param pathf: Function that takes a list of elements and returns a 249 | value of the same type. 250 | :returns: A list with length = len(list2) 251 | 252 | .. note:: 253 | * Both lists must have values of the same type 'T' 254 | * Comparison function should take a list of values of type 'T' and 255 | return a value of type 'T'. 256 | * Path function should take a list of values of type 'T' and return a 257 | value of type 'T'. 258 | * The function calculates path totals based on paths from list1 to 259 | list2. 260 | * The difference between lengths of list1 and list2 should be 1 261 | 262 | For example: To calculate maximum sum of path values - 263 | path function => sum, comparison function => max:: 264 | 265 | >>> list1 = [12,37,53,46] 266 | >>> list2 = [23,34,47] 267 | >>> compf = max 268 | >>> pathf = sum 269 | >>> collapse_lists(list1,list2,compf,pathf) 270 | >>> [60, 87, 100] 271 | """ 272 | result = [] 273 | len1 = len(list1) 274 | len2 = len(list2) 275 | if (abs(len1-len2) != 1): 276 | pass 277 | elif(list1 == []): 278 | result = list(list2) 279 | elif(list2 == []): 280 | result = list(list1) 281 | else: 282 | small2large = True 283 | if(len1 > len2): 284 | small2large = False 285 | for j in range(len2): 286 | if(small2large): 287 | if(j == 0): 288 | val = pathf([list2[j],list1[0]]) 289 | elif(j == len2-1): 290 | val = pathf([list2[j],list1[len1-1]]) 291 | else: 292 | pv1 = pathf([list2[j],list1[j-1]]) 293 | pv2 = pathf([list2[j],list1[j]]) 294 | val = compf([pv1, pv2]) 295 | result.append(val) 296 | else: 297 | pv1 = pathf([list2[j],list1[j+1]]) 298 | pv2 = pathf([list2[j],list1[j]]) 299 | val = compf([pv1, pv2]) 300 | result.append(val) 301 | return result -------------------------------------------------------------------------------- /eulerlib/fibonacci.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib.fibonacci 18 | :synopsis: Functions related to Fibonacci sequence. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | from ._exceptions import EulerlibInputError 25 | 26 | __all__ = ["fibo_gen", "first_n_fibo", "fibo_less_than", "fibo_num_digits"] 27 | 28 | def fibo_gen(start=1): 29 | """A generator function that yields the `Fibonacci sequence 30 | `_. 31 | 32 | :param start: Starting digit of Fibonacci sequence (0 or 1, default=1) 33 | """ 34 | if start!= 0 and start!= 1: 35 | raise EulerlibInputError('fibonacci','fibo_gen', 36 | 'start parameter value is incorrect') 37 | else: 38 | if start == 0: 39 | f1 = 0 40 | f2 = 1 41 | elif start == 1: 42 | f1 = 1 43 | f2 = 1 44 | nextf = 0 45 | count = 0 46 | while True: 47 | if count == 0: 48 | nextf = f1 49 | elif count == 1: 50 | nextf = f2 51 | else: 52 | nextf = f1 + f2 53 | f1 = f2 54 | f2 = nextf 55 | count += 1 56 | yield nextf 57 | 58 | 59 | def first_n_fibo(n,start=1): 60 | """Get first *n* numbers in the `Fibonacci sequence`_. 61 | 62 | :param n: Desired length of Fibonacci sequence 63 | :param start: Starting digit of Fibonacci sequence (0 or 1, default=1) 64 | :returns: A list [f1,f2,...,fn] where f1 = 1 if start = 1 65 | """ 66 | myfibo = fibo_gen(start) 67 | result = [next(myfibo) for i in range(n)] 68 | myfibo.close() 69 | return result 70 | 71 | 72 | def fibo_less_than(n,start=1): 73 | """Get the `Fibonacci sequence`_ [f1,f2,...fi] such that fi < n. 74 | 75 | :param n: Desired maximum value. 76 | :param start: Starting digit of Fibonacci sequence (0 or 1, default=1) 77 | :returns: A list [f1,f2,...,fi] such that fi < n 78 | """ 79 | myfibo = fibo_gen(start) 80 | result = [] 81 | for f in myfibo: 82 | if f < n: 83 | result.append(f) 84 | else: 85 | break 86 | myfibo.close() 87 | return result 88 | 89 | 90 | def fibo_num_digits(num,start=1): 91 | """Get the `Fibonacci sequence`_ [f1,f2,...fi] such that fi is the first 92 | Fibonacci number to have *num* digits. 93 | 94 | :param num: Desired number of digits in the last term of sequence. 95 | :param start: Starting digit of Fibonacci sequence (0 or 1, default=1) 96 | :returns: A list [f1,f2,..fi] such that fi is the first Fibonacci number 97 | to have *num* digits 98 | """ 99 | myfibo = fibo_gen(start) 100 | result = [] 101 | numdigits = 0 102 | for f in myfibo: 103 | numdigits = len(str(f)) 104 | if numdigits < num: 105 | result.append(f) 106 | elif numdigits == num: 107 | result.append(f) 108 | break 109 | myfibo.close() 110 | return result 111 | -------------------------------------------------------------------------------- /eulerlib/numtheory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib.numtheory 18 | :synopsis: Number theory related functions. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | __all__ = ["is_square", "gcd", "lcm", "lcm_n", "nCr", "nPr", "digital_sum", 25 | "digital_root","Divisors"] 26 | 27 | from .prime_numbers import primes 28 | 29 | def is_square(num): 30 | """Determines if a positive integer *num* is the perfect square. 31 | 32 | :param num: Integer to be checked 33 | :returns: A tuple (is_square,root) 34 | 35 | .. note:: 36 | 37 | * *is_square* is *True* if *num* is a perfect square. 38 | * The integer *root* <= (square root of *num*). 39 | 40 | Uses `Digit-by-digit algorithm 41 | `_ in base 10. 43 | """ 44 | if(num == 0): 45 | pn = 0 46 | rn = 1 47 | else: 48 | z = num 49 | test = [] 50 | while(z !=0): 51 | test.append(z%100) 52 | z = z//100 53 | test.reverse() 54 | pn = 0 55 | rn = 0 56 | for i in range(len(test)): 57 | #get an estimate of xn 58 | c = 100*rn + test[i] 59 | xn = 1 60 | while ((20*pn+xn)*xn <= c): 61 | xn += 1 62 | xn -= 1 63 | rn = c - (20*pn+xn)*xn 64 | pn = pn*10 + xn 65 | if(rn == 0): 66 | return (True,pn) 67 | else: 68 | return (False,pn) 69 | 70 | 71 | def gcd(a,b): 72 | """Calculates Greatest Common Divisor of two integers. 73 | 74 | :param a: First integer 75 | :param b: Second integer 76 | :returns: Greatest Common Divisor (GCD) of *a* and *b* 77 | 78 | Uses `Euclid's algorithm 79 | `_. 81 | """ 82 | if(a==b): return a 83 | if(a== 0): return b 84 | if(b== 0): return a 85 | if(a==1 or b==1): return 1 86 | if(a>b): 87 | big, small = a, b 88 | else: 89 | big, small = b, a 90 | r = big%small 91 | while(r != 0): 92 | big = small 93 | small = r 94 | r = big%small 95 | return small 96 | 97 | 98 | def lcm(a,b): 99 | """Calculates the `Least Common Multiple 100 | `_ (LCM) of two 101 | integers. 102 | 103 | :param a: First integer 104 | :param b: Second integer 105 | :returns: Least Common Multiple (LCM) of *a* and *b* 106 | """ 107 | lcm_ab = 0 108 | if a!=0 or b!=0: 109 | lcm_ab = abs(a*b)/gcd(a,b) 110 | return lcm_ab 111 | 112 | 113 | def lcm_n(num_list): 114 | """Calculate the `Least Common Multiple`_ of a list of 115 | integers. 116 | 117 | :param num_list: A list of integers *[i1,i2,i3,...,in]* 118 | :returns: LCM of the integers in *num_list* 119 | 120 | Uses the associative property LCM(a,b,c) = LCM(*LCM(a,b)*,c) 121 | """ 122 | result = 0 123 | lennum = len(num_list) 124 | if not (lennum <=1 or sum(num_list) == 0): 125 | cindex = 1 126 | while cindex < lennum: 127 | if cindex == 1: 128 | curr_lcm = lcm(num_list[cindex-1],num_list[cindex]) 129 | else: 130 | curr_lcm = lcm(curr_lcm,num_list[cindex]) 131 | cindex += 1 132 | result = curr_lcm 133 | return result 134 | 135 | 136 | def nCr(n,r): 137 | """Calculate ways of selecting *r* members out of a collection of *n* 138 | members. 139 | 140 | :param n: Size of the collection 141 | :param r: Number of members to be selected from the collection 142 | :returns: n!/[r!(n-r)!] 143 | 144 | .. note:: 145 | 146 | :sup:`n` C :sub:`r` is typically read as *n combination r*. Order of 147 | members *is not* important in the combination. E.g. There are :sup:`4` 148 | C :sub:`2` = 6 ways of selecting two members out of a collection 149 | (A, B, C, D) => AB, AC, AD, BC, BD, CD. 150 | """ 151 | result = 0 152 | if n >= 0 and r >= 0 and r <= n: 153 | num = 1 154 | denom = 1 155 | for i in range(r): 156 | num *= (n-i) 157 | denom *= (i+1) 158 | result = num/denom 159 | return result 160 | 161 | 162 | def nPr(n,r): 163 | """Calculate number of permutations of length *r* out of a collection of 164 | *n* members (No repeated members). 165 | 166 | :param n: Size of the collection 167 | :param r: Number of members to be permutated. 168 | :returns: n!/[(n-r)!] 169 | 170 | .. note:: 171 | 172 | :sup:`n` P :sub:`r` is typically read as *n permutation r*. Order of 173 | members *is* important in the permutation. E.g. There are :sup:`4` P 174 | :sub:`2` = 12 permutations of length 2 out of a collection 175 | (A, B, C, D) => AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB, DC. 176 | """ 177 | result = 0 178 | if n >= 0 and r >= 0 and r <= n: 179 | num = 1 180 | denom = 1 181 | for i in range(r): 182 | num *= (n-i) 183 | result = num/denom 184 | return result 185 | 186 | 187 | def digital_sum(num): 188 | """Calculate the `digital sum `_ 189 | of a number *num* in base 10 190 | 191 | :param num: Number for which digital sum is to be calculated. *num* should 192 | be in base 10. 193 | :returns: Digital sum of *num* in base 10. 194 | """ 195 | from .etc import num_to_list 196 | testnum = num 197 | if num < 0: 198 | testnum = abs(num) 199 | return sum(num_to_list(testnum)) 200 | 201 | 202 | def digital_root(num): 203 | """Calculate the `digital root 204 | `_ of a number *num* in base 10. 205 | 206 | :param num: Number for which digital root is to be calculated. *num* 207 | should be in base 10. 208 | :returns: Digital root of *num* in base 10. 209 | """ 210 | testnum = num 211 | if num < 0: 212 | testnum = abs(num) 213 | while testnum > 9: 214 | testnum = digital_sum(testnum) 215 | return testnum 216 | 217 | 218 | class Divisors: 219 | """Implements methods related to prime factors and divisors. 220 | 221 | :param maxnum: Upper limit for the list of primes. (default = 1000) 222 | """ 223 | 224 | def __init__(self,maxnum=1000): 225 | """Constructor for *Divisors* class 226 | """ 227 | self.limit = maxnum 228 | self.primes_table = primes(maxnum) 229 | self.sigma_table = {} 230 | self.primefact_table = {} 231 | self.pfactonly_table = {} 232 | self.divisors_table = {} 233 | 234 | def sigma_function(self,num): 235 | """Calculates the `divisor functions 236 | `_ (sigma functions). 237 | 238 | :param num: Integer for which sigma functions are needed. 239 | :returns: A tuple (sigma0,sigma1,s(n)) 240 | 241 | .. note:: 242 | 243 | * sigma0 = number of divisors of *num*. 244 | * sigma1 = sum of *all* divisors of *num* 245 | * s(n) = sum of *proper* divisors of *num* 246 | (includes 1, excludes *num*) 247 | 248 | """ 249 | if num in self.sigma_table: 250 | return self.sigma_table[num] 251 | elif ((num < 1) or (num > self.limit)): 252 | return () 253 | elif num == 1: 254 | return (1,1,0) 255 | elif (num in self.primes_table): 256 | return (2,num+1,1) 257 | else: 258 | sigma0 = 1 259 | sigma1 = 1 260 | #Implement divisor fucntion 261 | pfs = self.prime_factors(num) 262 | for (pi,ai) in pfs: 263 | sigma0 = sigma0*(ai+1) 264 | temp = 0 265 | for i in range(ai+1): 266 | temp = temp + pi**i 267 | sigma1 = sigma1*temp 268 | result = (sigma0,sigma1,sigma1-num) 269 | self.sigma_table[num] = result 270 | return result 271 | 272 | def prime_factors(self,num): 273 | """Returns the `prime factors`_ *pf* :sub:`i` of *num* and the maximum 274 | power *a* :sub:`i` for each prime factor *pf* :sub:`i`. 275 | 276 | :param num: An integer for which prime factors are needed 277 | :returns: A list of tuples [(pf1,a1),...(pfi,ai)] 278 | 279 | .. note:: 280 | 281 | num = (pf1**a1)*(pf2**a2)..*(pfi**ai) 282 | """ 283 | if num in self.primefact_table: 284 | return self.primefact_table[num] 285 | elif ((num < 2) or (num > self.limit)): 286 | return [] 287 | elif num in self.primes_table: 288 | self.primefact_table[num] = [(num,1)] 289 | return [(num,1)] 290 | else: 291 | result = [] 292 | tnum = num 293 | for prime in self.primes_table: 294 | if(tnum%prime==0): 295 | ai = 2 296 | pdiv = prime*prime 297 | while(tnum%pdiv==0): 298 | ai += 1 299 | pdiv *= prime 300 | ai -= 1 301 | pdiv //= prime 302 | result.append((prime,ai)) 303 | tnum //= pdiv 304 | if(tnum in self.primes_table): 305 | result.append((tnum,1)) 306 | break 307 | elif(tnum==1): 308 | break 309 | self.primefact_table[num] = result 310 | return result 311 | 312 | def prime_factors_only(self,num): 313 | """Returns the `prime factors 314 | `_ *pf* :sub:`i` of *num*. 315 | 316 | :param num: An integer for which prime factors are needed 317 | :returns: A list [pf1,pf2,...pfi] of prime factors of *num* 318 | """ 319 | if num in self.pfactonly_table: 320 | return self.pfactonly_table[num] 321 | elif ((num < 2) or (num > self.limit)): 322 | return [] 323 | elif num in self.primes_table: 324 | self.pfactonly_table[num] = [num] 325 | return [num] 326 | else: 327 | result = [] 328 | tnum = num 329 | for prime in self.primes_table: 330 | if(tnum%prime==0): 331 | result.append(prime) 332 | pdiv = prime*prime 333 | while(tnum%pdiv == 0): 334 | pdiv *= prime 335 | pdiv //= prime 336 | tnum //= pdiv 337 | if(tnum in self.primes_table): 338 | result.append(tnum) 339 | break 340 | elif(tnum == 1): 341 | break 342 | self.pfactonly_table[num] = result 343 | return result 344 | 345 | def divisors(self,num): 346 | """Returns a list of ALL divisors of *num* (including 1 and num). 347 | 348 | :param num: An integer for which divisors are needed. 349 | :returns: A list [d1,d2,...dn] of divisors of *num* 350 | """ 351 | result = [] 352 | if (num < 1) or (num > self.limit): 353 | return result 354 | result.append(1) 355 | if (num == 1): 356 | return result 357 | elif (num in self.primes_table): 358 | result.append(num) 359 | self.divisors_table[num] = result 360 | return result 361 | else: 362 | pfs = self.prime_factors(num) 363 | for (pi,ai) in pfs: 364 | newdivs = [] 365 | for i in range(ai): 366 | fact = pi**(i+1) 367 | for div in result: 368 | newdivs.append(div*fact) 369 | result += newdivs 370 | newdivs = [] 371 | result.sort() 372 | self.divisors_table[num] = result 373 | return result 374 | 375 | def phi(self,num): 376 | """Returns the number of `totatives 377 | `_ of *num* 378 | 379 | :param num: Integer for which number of totatives are needed. 380 | :returns: Number of totatives of *num* 381 | 382 | .. note:: 383 | 384 | A totative of an integer *num* is any integer *i* such that, 385 | 0 < i < n and *GCD(i,num) == 1*. 386 | 387 | Uses `Euler's totient function 388 | `_. 389 | """ 390 | if(num < 1): 391 | return 0 392 | if(num == 1): 393 | return 1 394 | if(num in self.primes_table): 395 | return num-1 396 | pfs = self.prime_factors_only(num) 397 | prod = num 398 | for pfi in pfs: 399 | prod = prod*(pfi-1)/pfi 400 | return prod -------------------------------------------------------------------------------- /eulerlib/prime_numbers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib.primes 18 | :synopsis: Prime numbers generation functions. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | __all__ = ["prime_gen", "prime_wheel_fact_gen", "primes", "primes_wheel_fact", 25 | "is_prime"] 26 | 27 | from itertools import islice as it_islice 28 | from itertools import count as it_count 29 | from itertools import compress as it_compress 30 | from itertools import cycle as it_cycle 31 | 32 | def prime_gen(): 33 | """A generator function that yields prime numbers using the `Sieve of 34 | Eratosthenes `_ 35 | algorithm. 36 | 37 | .. note:: 38 | 39 | This function is based on the erat2a function which can be found 40 | `here `_. Variable names were 41 | changed and comments added for clarity. 42 | """ 43 | yield 2 44 | comp_dict = {} 45 | for num in it_islice(it_count(3),0,None,2): 46 | p = comp_dict.pop(num,None) 47 | if p is not None: 48 | # num is a composite. Get the next composite that is not already 49 | # in the dictionary and that has p as prime factor. Add it to 50 | # the dictionary. The composite number is thus "sieved" out. 51 | # By taking a 2*p step, we avoid checking if test is even. 52 | test = num + 2*p 53 | while test in comp_dict: 54 | test = test + 2*p 55 | comp_dict[test] = p 56 | else: 57 | # num is a prime. 58 | # Add the first composite number that has 'num' as the 59 | # only prime factor to the composite numbers dictionary 60 | comp_dict[num*num] = num 61 | # return num 62 | yield num 63 | 64 | 65 | def prime_wheel_fact_gen(): 66 | """A generator function that yields prime numbers using the `wheel 67 | factorized `_ `Sieve of 68 | Eratosthenes`_. 69 | 70 | .. note:: 71 | 72 | This function is based on the erat3 function which can be found 73 | `here `_. Variable names were 74 | changed and comments added for clarity. 75 | """ 76 | yield 2 77 | yield 3 78 | yield 5 79 | # The mask is used with itertools.compress method to generate prime 80 | # candidates after eliminating numbers using the wheel. The mask 81 | # contains a 1 when the corresponding number has 2,3 or 5 as a factor 82 | # only odd numbers are considered so the mask has only 15 values instead 83 | # of 30. 84 | wheel_mask = [0 if x%3 == 0 or x%5 == 0 else 1 for x in range(7,37,2)] 85 | modulos = frozenset([x%30 for x in range(31,61,2) 86 | if x%3 != 0 and x%5 != 0]) 87 | comp_dict = {} 88 | for num in it_compress(it_islice(it_count(7),0,None,2), 89 | it_cycle(wheel_mask)): 90 | p = comp_dict.pop(num,None) 91 | if p is not None: 92 | # num is a composite. Get the next composite that is not already 93 | # in the dictionary, that meets the wheel criteria and that has p 94 | # as prime factor. Add it to the dictionary. The composite number 95 | # is thus "sieved" out. 96 | # By taking a 2*p step, we avoid checking if test is even. 97 | test = num + 2*p 98 | while test in comp_dict or test%30 not in modulos: 99 | test = test + 2*p 100 | comp_dict[test] = p 101 | # delete 'num' from comp_dict to free memory. 102 | else: 103 | # num is a prime. 104 | # Add the first composite number that has 'num' as the 105 | # only prime factor to the composite numbers dictionary 106 | comp_dict[num*num] = num 107 | # return num 108 | yield num 109 | 110 | 111 | def primes(num): 112 | """Returns a list of prime numbers. Uses the 113 | :func:`eulerlib.prime_numbers.prime_gen` generator function. 114 | 115 | :param num: The upper limit for prime numbers list (pn < num) 116 | :returns: List of prime numbers [p1,p2,...pn] such that pn < num. 117 | 118 | Uses the prime_gen function that uses Sieve of Eratosthanes. 119 | """ 120 | mypgen = prime_gen() 121 | result = [] 122 | while True: 123 | p = next(mypgen) 124 | if p > num: 125 | break 126 | else: 127 | result.append(p) 128 | mypgen.close() 129 | return result 130 | 131 | 132 | def primes_wheel_fact(num): 133 | """Returns a list of prime numbers. Uses the 134 | :func:`eulerlib.prime_numbers.prime_wheel_fact_gen` generator function. 135 | 136 | :param num: The upper limit for prime numbers list (pn < num) 137 | :returns: List of prime numbers [p1,p2,...pn] such that pn < num. 138 | 139 | Uses the prime_wheel_fact_gen function that uses Sieve of Eratosthanes 140 | with wheel factorization based on the first 3 primes: 2, 3 and 5. 141 | """ 142 | mypgen = prime_wheel_fact_gen() 143 | result = [] 144 | while True: 145 | p = next(mypgen) 146 | if p > num: 147 | break 148 | else: 149 | result.append(p) 150 | mypgen.close() 151 | return result 152 | 153 | 154 | def is_prime(num): 155 | """Primality checking function: returns *True* if *num* is a prime number. 156 | """ 157 | result = True 158 | if num < 4: 159 | if (num != 2 and num != 3): 160 | result = False 161 | else: 162 | nsqrt = int(num**0.5) + 1 163 | mygen = prime_gen() 164 | for p in mygen: 165 | if p > nsqrt: 166 | break 167 | if num%p == 0: 168 | result = False 169 | break 170 | mygen.close() 171 | return result 172 | 173 | -------------------------------------------------------------------------------- /eulerlib/pythagoras.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | .. module:: eulerlib.pythagoras 18 | :synopsis: Functions related to Pythagorean triples. 19 | 20 | .. moduleauthor:: Sameer Marathe 21 | 22 | """ 23 | 24 | __all__ = ["triplet_gen", "primitive_triples"] 25 | 26 | def _xform(t,n): 27 | """Matrix transform triplet *t* using *n*th matrix (n = 1,2 or 3)""" 28 | a = 0 29 | b = 0 30 | c = 0 31 | if n == 1: 32 | a = 1*t[0] -2*t[1] + 2*t[2] 33 | b = 2*t[0] -1*t[1] + 2*t[2] 34 | c = 2*t[0] -2*t[1] + 3*t[2] 35 | elif n==2: 36 | a = t[0] +2*t[1] + 2*t[2] 37 | b = 2*t[0] +1*t[1] + 2*t[2] 38 | c = 2*t[0] +2*t[1] + 3*t[2] 39 | elif n==3: 40 | a = -1*t[0] +2*t[1] + 2*t[2] 41 | b = -2*t[0] +1*t[1] + 2*t[2] 42 | c = -2*t[0] +2*t[1] + 3*t[2] 43 | if (b > a): 44 | return (a,b,c) 45 | else: 46 | return (b,a,c) 47 | 48 | def triplet_gen(): 49 | """A generator function that yields primitive `Pythagorean triples`_. 50 | """ 51 | 52 | oldstack = [(3,4,5)] 53 | newstack = [] 54 | while True: 55 | t0 = oldstack.pop() 56 | for xcount in range(1,4): 57 | newstack.append(_xform(t0,xcount)) 58 | if oldstack == []: 59 | oldstack = newstack 60 | newstack = [] 61 | yield t0 62 | 63 | def primitive_triples(n): 64 | """Returns *n* primitive `Pythagorean triples 65 | `_. 66 | 67 | :param n: Maximum number of primitive triples desired. 68 | :returns: A list of tuples [(a1,b1,c1),(a2,b2,c2),...,(an,bn,cn)] 69 | """ 70 | tgen = triplet_gen() 71 | result = [next(tgen) for i in range(n)] 72 | tgen.close() 73 | return result 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2015 Sameer Suhas Marathe 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Setup file for EulerLib""" 17 | 18 | from setuptools import setup, find_packages 19 | 20 | def readme(): 21 | with open('README.rst') as f: 22 | return f.read() 23 | 24 | setup(name='eulerlib', 25 | version='0.2', 26 | description=('A library of number theory related ' 27 | 'functions inspired by Project Euler.'), 28 | long_description=readme(), 29 | url='https://bitbucket.org/transmogrifier/eulerlib', 30 | author='Sameer Marathe', 31 | author_email='transmogrifier@gmail.com', 32 | license='Apache License 2.0', 33 | classifiers=[ 34 | 'Development Status :: 4 - Beta', 35 | 'License :: OSI Approved :: Apache Software License', 36 | 'Programming Language :: Python :: 2.7', 37 | 'Programming Language :: Python :: 3.4', 38 | 'Topic :: Scientific/Engineering :: Mathematics', 39 | 'Intended Audience :: Developers', 40 | 'Intended Audience :: Science/Research', 41 | 'Intended Audience :: End Users/Desktop' 42 | ], 43 | keywords='mathematics project_euler number_theory prime_numbers', 44 | packages= find_packages(exclude=['eulerlib._tests']), 45 | test_suite='eulerlib._tests', 46 | zip_safe=False) 47 | 48 | --------------------------------------------------------------------------------