{"id":23231209,"url":"https://github.com/maxhumber/marc","last_synced_at":"2026-03-06T15:03:41.766Z","repository":{"id":34900786,"uuid":"184164723","full_name":"maxhumber/marc","owner":"maxhumber","description":"Markov chain generator for Python and/or Swift","archived":false,"fork":false,"pushed_at":"2022-05-28T16:04:58.000Z","size":3037,"stargazers_count":65,"open_issues_count":0,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-24T10:31:52.840Z","etag":null,"topics":["markov","markov-chain","markov-chains","python","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxhumber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"maxhumber","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-04-30T00:41:54.000Z","updated_at":"2025-05-12T13:04:22.000Z","dependencies_parsed_at":"2022-08-28T19:41:32.933Z","dependency_job_id":null,"html_url":"https://github.com/maxhumber/marc","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/maxhumber/marc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxhumber%2Fmarc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxhumber%2Fmarc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxhumber%2Fmarc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxhumber%2Fmarc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxhumber","download_url":"https://codeload.github.com/maxhumber/marc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxhumber%2Fmarc/sbom","scorecard":{"id":629166,"data":{"date":"2025-08-11","repo":{"name":"github.com/maxhumber/marc","commit":"71cc93e9ae4132c3b407f09d4b361d952a66ff02"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/python-publish.yml:6","Warn: no topLevel permission defined: .github/workflows/swift.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":1,"reason":"dependency not pinned by hash detected -- score normalized to 1","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/maxhumber/marc/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/maxhumber/marc/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/swift.yml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/maxhumber/marc/swift.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:18","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:19","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:22","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T07:21:02.761Z","repository_id":34900786,"created_at":"2025-08-21T07:21:02.761Z","updated_at":"2025-08-21T07:21:02.761Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30182686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T14:42:24.748Z","status":"ssl_error","status_checked_at":"2026-03-06T14:42:14.925Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["markov","markov-chain","markov-chains","python","swift"],"created_at":"2024-12-19T02:13:36.562Z","updated_at":"2026-03-06T15:03:41.717Z","avatar_url":"https://github.com/maxhumber.png","language":"Swift","funding_links":["https://github.com/sponsors/maxhumber"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg alt=\"marc\" src=\"images/logo.png\" height=\"125px\"\u003e\n\u003c/div\u003e\n\n\n### About\n\nmarc is a **Mar**kov **c**hain generator for Python and/or Swift\n\n\n### Python\n\nInstall\u003csup\u003e‡\u003c/sup\u003e\n\n```sh\npip install marc\n```\n\n\nQuickstart:\n\n```python\nfrom marc import MarkovChain\n\nplayer_throws = \"RRRSRSRRPRPSPPRPSSSPRSPSPRRRPSSPRRPRSRPRPSSSPRPRPSSRPSRPRSSPRP\"\nsequence = [throw for throw in player_throws]\n# ['R', 'R', 'R', 'S', 'R', 'S', 'R', ...]\n\nchain = MarkovChain(sequence)\nchain.update(\"R\", \"S\")\n\nchain[\"R\"]\n# {'P': 0.5, 'R': 0.25, 'S': 0.25}\n\nplayer_last_throw = \"R\"\nplayer_predicted_next_throw = chain.next(player_last_throw)\n# 'P'\n\ncounters = {\"R\": \"P\", \"P\": \"S\", \"S\": \"R\"}\ncounter_throw = counters[player_predicted_next_throw]\n# 'S'\n```\n\nFor more inspiration see the [python/examples/](python/examples/) directory\n\n\n### Swift\n\nSPM:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/maxhumber/marc.git\", .upToNextMajor(from: \"22.5.0\"))\n]\n```\n\n\nQuickstart:\n\n```swift\nimport Marc\n\nlet playerThrows = \"RRRSRSRRPRPSPPRPSSSPRSPSPRRRPSSPRRPRSRPRPSSSPRPRPSSRPSRPRSSPRP\"\nlet sequence = playerThrows.map { String($0) }\n\nlet chain = MarkovChain(sequence)\nchain.update(\"R\", \"S\")\n\nprint(chain[\"R\"])\n// [(\"P\", 0.5), (\"R\", 0.25), (\"S\", 0.25)]\n\nlet playerLastThrow = \"R\"\nlet playerPredictedNextThrow = chain.next(playerLastThrow)!\n\nlet counters = [\"R\": \"P\", \"P\": \"S\", \"S\": \"R\"]\nlet counterThrow = counters[playerPredictedNextThrow]!\nprint(counterThrow)\n// \"S\"\n```\n\nFor more inspiration see the [swift/Examples/](swift/Examples/) directory\n\n\n### API/Comparison\n\n|                         | Python                                 | Swift                                      |\n| ----------------------- | -------------------------------------- | ------------------------------------------ |\n| Import                  | `from marc import MarkovChain`         | `import Marc`                              |\n| Initialize \u003csub\u003eA\u003c/sub\u003e | `chain = MarkovChain()`                | `chain = MarkovChain\u003cString\u003e()`            |\n| Initialize \u003csub\u003eB\u003c/sub\u003e | `chain = MarkovChain([\"R\", \"P\", \"S\"])` | `let chain = MarkovChain([\"R\", \"P\", \"S\"])` |\n| Update chain            | `chain.update(\"R\", \"P\")`               | `chain.update(\"R\", \"P\")`                   |\n| Lookup transitions      | `chain[\"R\"]`                           | `chain[\"R\"]`                               |\n| Generate next           | `chain.next(\"R\")`                      | `chain.next(\"R\")!`                         |\n\n\n### Why\n\nI built the first versions of *marc* in the Fall of 2019. Back then I created, and used, it as a teaching tool (for how to build and upload a PyPI package). Since March 2020 I've been spending less and less time with Python and more and more time with Swift... and so, just kind of forgot about *marc*.\n\nRecently, I had an iOS project come up that needed some Markov chains. After surveying GitHub and not finding any implementations that I liked (forgetting that I had already rolled my own in Python) I started from scratch on a new implementation in Swift. \n\nJust as I was finishing the Swift package I re-discovered *marc*... I had a good laugh looking back through the [original](https://github.com/maxhumber/marc/tree/5ea21639aba16fcfe15c5de25049d024e0bb3332) Python library. My feelings about the code I wrote and my abilities in 2019 can be summarized in a picture:\n\n\u003cdiv align=\"center\"\u003e\n   \u003cimg alt=\"meme\" src=\"images/meme.png\" width=\"500px\"\u003e\n\u003c/div\u003e\n\n\nUnable to resist a good procrasticode™ project, I cross-ported the finished Swift package to Python and polished up both codebases and documentation into this mono repo. \n\nHonestly, I had a lot of fun trying to mirror the APIs as closely as possible while doing my best to keep the Python code \"Pythonic\" and the Swift code \"Schwifty\". The whole project/exercise was incredibly rewarding, interesting, and insightful. Crudely, here's how I found working on both packages:\n\n**Python**\n\n| Like                                | Dislike                                  |\n| ----------------------------------- | ---------------------------------------- |\n| `defaultdict` !!                    | Clunky `setup.py` packaging              |\n| `random.choice` !                   | Setting up and working with environments |\n| Dictionary comprehensions + sorting | `__init__.py` and directory issues       |\n\n**Swift**\n\n| Like                                              | Dislike                                        |\n| ------------------------------------------------- | ---------------------------------------------- |\n| `Package.swift` and packaging in general          | Dictionary performance sucks... (surprising!!) |\n| Don't have to think about environments            | Need randomness? Too bad. Go roll it yourself  |\n| `XCTest` is nicer/easier than `unittest`/`pytest` | Playgrounds aren't as good as Hydrogen/Jupyter |\n\nSo why? For fun! And procrastination. And, more seriously, because I needed some chains in Swift. And then, because I thought it could be interesting to create a Rosetta Stone for Python and Swift... So if you, Dear Reader, are looking to use Markov chains in your Python or Swift project, or are looking to jump to or from either language, I hope you find this useful.\n\n\n### Warning\n\n\u003csup\u003e‡\u003c/sup\u003e marc 22.5+ is incompatible with marc 2.x\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxhumber%2Fmarc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxhumber%2Fmarc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxhumber%2Fmarc/lists"}