{"id":15669825,"url":"https://github.com/alexgustafsson/practical-factorization-comparison","last_synced_at":"2026-03-19T17:52:51.842Z","repository":{"id":83002413,"uuid":"125258552","full_name":"AlexGustafsson/practical-factorization-comparison","owner":"AlexGustafsson","description":"A Practical Study and Comparison of Integer Factorization Methods","archived":false,"fork":false,"pushed_at":"2018-03-27T13:54:06.000Z","size":239,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"cocalc","last_synced_at":"2025-01-19T07:23:37.516Z","etag":null,"topics":["bth","cryptography","elliptic-curve-factorization","factorization","fermat-factorization","integer-factorization","mathematics","paper","pollard-rho-algorithm","quadratic-sieve","study","trial-division"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlexGustafsson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-14T18:44:54.000Z","updated_at":"2023-08-16T05:26:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"31398af9-d174-48e5-84a7-80cfe4e84640","html_url":"https://github.com/AlexGustafsson/practical-factorization-comparison","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexGustafsson%2Fpractical-factorization-comparison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexGustafsson%2Fpractical-factorization-comparison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexGustafsson%2Fpractical-factorization-comparison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexGustafsson%2Fpractical-factorization-comparison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlexGustafsson","download_url":"https://codeload.github.com/AlexGustafsson/practical-factorization-comparison/tar.gz/refs/heads/cocalc","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243230101,"owners_count":20257644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bth","cryptography","elliptic-curve-factorization","factorization","fermat-factorization","integer-factorization","mathematics","paper","pollard-rho-algorithm","quadratic-sieve","study","trial-division"],"created_at":"2024-10-03T14:41:25.116Z","updated_at":"2025-12-26T16:53:01.759Z","avatar_url":"https://github.com/AlexGustafsson.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"A Practical Study and Comparison of Integer Factorization Methods\n======\nA project by [Alex Gustafsson](https://github.com/AlexGustafsson) and [Marcus Lenander](https://github.com/MarcusLenander) in the course MA1453 (Cryptography 2) at BTH, Sweden.\n\nThis project evaluates the relative performance of various algorithms for integer factorization, namely Trial Division, Fermat's Factorization, Pollard's Rho Algorithm, Lenstra's Elliptic Curve Factorization Algorithm and briefly the Quadratic Sieve. The algorithms are presented and described with regards to use and implementation details. No effort was made to optimize the implemented algorithms further than what is considered logical. No algorithm leverages parallel computing techniques, dynamic programming techniques or other techniques in order to gain performance. No consideration of performance was made when the software and programming stack were chosen.\n\nThe project was hosted on a private instance of [CoCalc - Collaborative Calculation in the Cloud](https://cocalc.com). CoCalc makes it easy to collaboratively develop math-oriented code using technologies such as [SageMath](http://www.sagemath.org), [R](https://cran.r-project.org/doc/manuals/r-release/R-intro.html), [Julia](https://docs.julialang.org/en/stable/manual/introduction/) and [LaTeX](https://en.wikibooks.org/wiki/LaTeX) whilst still being completely free and open source.\n\nThe paper is available in the branch [paper](https://github.com/AlexGustafsson/practical-factorization-comparison/tree/paper).\n\nSee also: [Course Notes](https://github.com/CourseNotesBTH) for BTH courses and code and examples for [Programming Courses at BTH](https://github.com/ProgrammingCoursesBTH).\n\n# Quickstart\n\u003ca name=\"quickstart\"\u003e\u003c/a\u003e\n\n1. Download this repository and launch it in a public or private CoCalc project\n2. Navigate / test the code yourself or read below\n\n# Table of contents\n\n[Quickstart](#quickstart)\u003cbr/\u003e\n[Algorithms](#algorithms)\u003cbr /\u003e\n[Utilities](#utilities)\u003cbr /\u003e\n[Disclaimer](#disclaimer)\n\n# Implemented algorithms\n\u003ca name=\"algorithms\"\u003e\u003c/a\u003e\n\nPsuedocode and comments for the implemented factorization algorithms. The goal of each implemented algorithm was to be able to retrieve an unknown amount of factors. Therefore there are some changes made to some of the algorithms to allow for that goal to be fulfilled. Beyond that, we've tried to stay true to each algorithm's core meaning that we've taken no liberty to add dynamic or parallell programming techniques to speed up the factorization. In a realistic scenario, such techniques are crucial to speed factorization up - especially for numbers consisting of many (possibly equal) factors.\n\n## Trial Division\n\nFound in `trial_division.py`, the trial division algorithms implemented consists of the following pseudocode:\n\n##### `TrialDivision`\n\n```\ndef TrialDivision(n)\n    f = 1\n    while n \u003e 1\n        if n is divisible by f\n            f is a factor. Divide n by f\n        else\n            increment f by one\n```\n\n##### `TrialDivision` (modified)\n\nA modification of the above Trial Division that halves the needed time.\n\n```\ndef TrialDivision(n)\n    f = 3\n    while n is divisible by 2\n        2 is a factor. Divide n by 2\n    while n \u003e 1\n        if n is divisible by f\n            f is a factor. Divide n by f\n        else\n            increment f by 2\n```\n\n## Fermat's Factorization Method\n\nFound in `fermats_factorization.py`, the Fermat's algorithm implemented consists of the following pseudocode:\n\n```\ndef FermatsFactorization(n)\n    if n % 2 == 0:\n        return None\n        \n    a = ceil(sqrt(n))\n    b2 = a*a - n\n    while b2 is not square:\n        a += 1\n        b2 = a*a - n\n    b = sqrt(b2)\n    return p = (a + b), q = (a - b)\n```\n\n## Pollard's Rho Algorithm\n\nFound in `pollards_rho_algorithm.py`, the Pollard's algorithm implemented consists of the following pseduocode:\n\n```\ndef PollardsRhoAlgorithm(n)\n    if n is 1\n        1 is a factor. Stop\n    add n to the stack 'stack' to try with the first g(x)\n    while the stack is not empty\n        pop an element a from the stack\n        if a is prime\n            a is a factor\n        else\n            f = 1, x = 2, y = 2\n            while f is 1\n                x = g(x), y = g(g(x))\n                f = gcd(x - y, a)\n            if f is n\n                add a to stack to try with the next g(x)\n            else\n                add f and a / f to the stack\n```\n\n_Note: the function `g(x)` is tested in the following order: `g(x)=x^2+1`, `g(x)=x^3+1`, `g(x)=x+1`. If you're interested in why those functions are tested in that order, please refer to the paper._\n\n## Lenstra's Elliptic Curve Factorization\n\nFound in `lenstras_elliptic_curve_factorization.py`, the Lenstra's algorithm implemented consists of the following pseudocode:\n\n```\ndef LenstrasEllipticCurveFactorizationAlgorithm(n)\n    x, y, A = random element in Zn\n    Q = (x, y)\n    if a modular inverse exists:\n        P = Q + Q\n        while P + Q can be calculated \n            if iterations is less then maximum iterations\n                if modular inverse exists in kP\n                    kP = (k-1)P + Q\n                else\n                    add gcd(modular inverse, n) to the stack of factors\n            else\n                no factor found, try with new random elements\n    else\n        add gcd(modular inverse, n) to the stack of factors\n```\n\n## Quadratic Sieve\n\nFound in `quadratic_sieve.py`, the Quadratic Sieve algorithm implemented is too complex to easily describe using pseudocode. Please view the file `quadratic_sieve.py` and read the source code instead.\n\n# Utilities\n\u003ca name=\"utilities\"\u003e\u003c/a\u003e\n\nUtilities produced to make testing, benchmarking and debugging easier.\n\n## Factorization Function\n\nFound in `factorization_function.py`, the `FactorizationFunction` class is a parent class to all implemented factorization algorithms. The class enables the same interface and testability for all algorithms. Noteworthy details are mentioned below.\n\n##### Implementing a Factorization Function\n\nClass implementation:\n```\nfrom factorization_function import *\n\nclass MyFactorizationFunction(FactorizationFunction)\n    @classmethod\n    def getCharacteristics(self):\n        c = FactorizationFunctionCharacteristics()\n        c.canFactorizePrimeComposites = True\n        c.canFactorizeEvenComposites = True\n        return c\n\n    @classmethod\n    def factorize(self, n, returnBenchmark=False):\n        # Setup\n        factors = []\n        ...\n        # Start of algorithm\n        ...\n        # End of algorithm\n        return (factors, benchmark) if returnBenchmark else factors\n```\n\nClass usage:\n```\nMyFactorizationFunction.factorize(3*5*6)\nMyFactorizationFunction.test()\nMyFactorizationFunction.benchmark(numberSuite)\n```\n\n##### Determining capabilities\n\nEach Factorization Function supports a common interface for determining capabilities.\n\nTo retrieve a Factorization Function's capabilities, call the `getCharacteristics` method.\n\nThe result is expected to be a `FactorizationFunctionCharacteristics` object consisting of two bools:\n\n`canFactorizePrimes` indicates whether or not the algorithm can factorize numbers consisting of prime factors. `canFactorizeNonPrimes` indicates whether or not the algorithm can factorize numbers consisting of even factors.\n\nTo add specific characteristics to your function, simply define the function like this:\n\n```\n...\nclass MyFactorizationFunction(FactorizationFunction)\n    ...\n    def getCharacteristics():\n        c = FactorizationFunctionCharacteristics()\n        c.canFactorizePrimes = True\n        c.canFactorizeNonPrimes = False\n        return c\n```\n\n##### Testing\n\nEach Factorization Function supports a common interface for testing.\n\nTo test a Factorization Function, call the `test` method.\n\n```\nMyFactorizationFunction.test()\n```\n\nEach Factorization Function is first tested to see whether or not the `factorization` function will run at all. If not, the test will fail.\n\nIf the algorithm has been noted to support factorization to prime factors, the function is then tested using the following numbers: ```2, 3, 5, 7, 11, 15, 21, 25, 45, 77, 121, 169, 21853```. These numbers have been found by us to test the overall factorization of an algorithm, including edge cases, multiple factors and other quirks.\n\nIf the algorithm has been noted to support factorization to even factors, the function is then tested using the following numbers: ```2, 4, 12, 20, 30```. No further effort has been made to cover edge cases using even numbers. This is due to the fact that not all algorithms support even numbers without alteration. Furthermore, factorizing even numbers is often not interesting in a true to life scenario.\n\nWhen common tests have been run, any function of the class starting with `test` will be run. To add specific tests to your function, simply define a function like this:\n\n```\n...\nclass MyFactorizationFunction(FactorizationFunction)\n    ...\n    def testMySpecifics():\n        ...\n```\n\n##### Benchmarking\n\nEach Factorization Function supports a common interface for benchmarking. See also the implementation details on benchmarking.\n\nTo test a Factorization Function, call the `benchmark` function with your number suite and an optional number of iterations for each benchmark (defaults to `10`).\n\n```\nMyFactorizationFunction.benchmark(numberSuite, iterations=10)\n```\n\nThe number suite is expected to follow the following format:\n\n```\n# A tuple\n(\n    # String description of the suite\n    description,\n    # A list of tuples\n    [\n        # A tuple\n        (\n            # Integer indicating amount of factors in this benchmark\n            numberOfFactors,\n            # A list of tuples\n            [\n                # A tuple\n                (\n                    # The number to factorize\n                    number,\n                    # A list of factors\n                    [\n                        factor1,\n                        factor2,\n                        ...\n                    ]\n                )\n            ]\n        )\n    ]\n)\n```\n\nFor a more comprohensive view, refer to the following example as well as the file `number_suite.sagews`.\n\nAn example number suite testing two numbers consisting of two and three factors. The number tested for two factors is `118=1*2*59`. The number tested for three factors is `159953=1*17*97*97`.\n```\n(\"Example\", [(2, [(118, [1, 2, 59])]), (3, [(159953, [1, 97, 17, 97])])])`\n```\n\nTo add benchmarks to your function, define benchmarks like this:\n\n```\nfrom benchmark import *\n...\nclass MyFactorizationFunction(FactorizationFunction)\n    ...\n    def factorize():\n        # Setup\n        ...\n        benchmark = Benchmark()\n        # Start of algorithm\n        benchmark.start()\n        for i in range(10):\n            benchmark.iterate()\n            benchmark.start(\"another timer\")\n            ...\n            benchmark.stop(\"another timer\")\n        # End of algorithm\n        benchmark.stop()\n        ...\n```\n\nThe complete list of parameters supported by `.benchmark()`:\n(self, numberSuite, iterations=10, optionalArgs={}, start=2, skipFactors=None):\n\n| Parameter | Default | Description | Required |\n| --------- | ------- | ----------- | -------- |\n| `numberSuite` | - | See above | Yes |\n| `iterations` | `10` | Number of times each number is tested | No |\n| `optionalArgs` | `{}`| Map of optional parameter (key) and corresponding value (value) sent to the function that is being benchmarked | No |\n| `start` | `2` | The least number of factors to test, no matter the number suite | No |\n| `skipFactors` | `None` | A function that accepts a number, `numberOfFactors` and returns a boolean whether or not the benchmark should occur. Allows all numbers if `None` | No |\n\n## Benchmark\n\nFound in `benchmark.py`, the `Benchmark` class is a utility class to keep track of time and iterations.\n\n##### Basic usage\n\n```\n# Import the class\nfrom benchmark import *\n\n# Create an instance to store values\nbenchmark = Benchmark()\nbenchmark.verbose = True\n\n# Start a benchmark (the default one)\nbenchmark.start()\n\nprint \"This is some heavy work\"\n\n# Stop the default benchmark\nbenchmark.stop()\n\nbenchmark.start(\"main loop\")\nfor i in range(2):\n    # Add one to the default iteration counter\n    benchmark.iterate()\n    benchmark.start(\"inner loop\")\n    for j in range(2):\n        benchmark.iterate()\n    benchmark.stop(\"inner loop\")\nbenchmark.stop(\"main loop\")\n\n# Benchmark uses named timers and iterations\n# If none is given, \"default\" is used - as seen above\nbenchmark.start(\"another timer\")\nbenchmark.stop(\"another timer\")\nbenchmark.iterate(\"another counter\")\n\n# When printing a benchmark, it is automatically formatted to be somewhat easy to read\nprint benchmark\n```\n\n```\nThis is some heavy work\nBenchmark \u003cTotal time: {\"default\": 0.000479, \"inner loop\": 0.000051, \"main loop\": 0.000562, \"another timer\": 0.000119}, iterations: {\"default\": 6, \"another counter\": 1}\u003e\n```\n\n##### Methods and attributes\n\nA benchmark instance has the following noteworthy attributes:\n\n* `totalTime`: a map where keys are labels and values are total time recorded for label\n* `iterations`: a map where keys are labels and values are iterations recorded for label\n\nA benchmark instance has the following noteworthy methods:\n\n* `start(label)`: starts counting time for `label`. `label` defaults to \"default\"\n* `stop(label)`: stops counting time for `label`. `label` defaults to \"default\"\n* `iterate(label)`: add one to `label` iteration counter. `label` defaults to \"default\"\n\n* `asTables()`: return a latex string describing the benchmark as time and iterations tables - commonly used with Sage's `show()`\n* `printEvents()`: an overview of the algorithm. See \"Other\" below.\n\nA benchmark instance has the following operations:\n\n* `+`: add to benchmarks together - useful when measuring different functions and aggregating the results\n\nBenchmark has the following static functions:\n\n* `min(benchmarks)`: returns the minimum value for each measurement in the benchmarks array as a new benchmark\n* `max(benchmarks)`: returns the maximum value for each measurement in the benchmarks array as a new benchmark\n* `average(benchmarks)`: returns the average value for each measurement in the benchmarks array as a new benchmark\n\n##### Other\n\nBenchmarking can be used to describe the algorithm rather cleanly. You can try to set `verbose=True` on your `Benchmark` instance to record logs. An overview of the algorithm can the be printed using `.printEvents()`. The output below is from the example above (\"Basic usage\") with an added call to `benchmark.printEvents()`:\n\n* ▶️: start of a timer\n* ⏹: stop of a timer\n* 🔂: iteration\n\n```\nThis is some heavy work\nBenchmark \u003cTotal time: {\"default\": 0.000493, \"inner loop\": 0.000046, \"main loop\": 0.000550, \"another timer\": 0.000118}, iterations: {\"default\": 6, \"another counter\": 1}\u003e\n▶️ default\n⏹ default (0.000493 s)\n▶️ main loop\n\t🔂 default\n\t▶️ inner loop\n\t\t🔂 default x 2\n\t⏹ inner loop (0.000025 s)\n\t🔂 default\n\t▶️ inner loop\n\t\t🔂 default x 2\n\t⏹ inner loop (0.000021 s)\n⏹ main loop (0.000550 s)\n▶️ another timer\n⏹ another timer (0.000118)\n🔂 another counter\n```\n\n__IMPORTANT NOTE__:\n\nEvery time either `.start()`, `.stop()` or `.iterate()` is called, somewhere around 40 bytes is added to RAM usage due to the logging of events. This might not seem as much, but when, for example, Lenstra's Elliptic Curve Factorization Method is run on larger numbers it may run several hundred thousand iterations, resulting in multiple gigabytes of RAM usage.\n\n# Disclaimer\n\u003ca name=\"disclaimer\"\u003e\u003c/a\u003e\n\n_This repository holds code created by students. Although the code is most likely correct, it may not promote best practices, be factual or grammatically correct. The code is meant as a future reference for students reading the same or a similar course. The code is also made publically available alongside the report written on the subject._\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexgustafsson%2Fpractical-factorization-comparison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexgustafsson%2Fpractical-factorization-comparison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexgustafsson%2Fpractical-factorization-comparison/lists"}