├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bashrc ├── binaryheap-fruit ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── btree-language ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── btreeset-fruit ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── caesar-cipher-cli ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── caesar-cipher ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── calc-cli-with-tests ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── cli-customize-fruit-salad ├── Cargo.toml ├── Makefile ├── fruits.csv └── src │ ├── lib.rs │ └── main.rs ├── cli-salad ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── community-detection ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── csv-demo ├── Cargo.toml ├── data │ └── text.txt └── src │ └── main.rs ├── data-eng-rust-tutorial ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ └── chapter_1.md ├── data-race ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── decoder-ring ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── dining-philosopher ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── format.sh ├── graph-centrality-ufc ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── graph-visualize ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── hashmap-count ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── hashmap-language ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── hashset-fruit ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── homophonic-cipher ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── immutable-testing ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── immutable ├── Cargo.toml ├── Makefile ├── immutable-py.py └── src │ └── main.rs ├── linked-list-fruit-salad ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── lint.sh ├── lisbon-shortest-path ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── lowmem-fruit-salad ├── Cargo.toml ├── Makefile ├── fruits.csv └── src │ ├── lib.rs │ └── main.rs ├── mutable-fruit-salad ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── pagerank ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── polars-hello-world-code-whisperer ├── Cargo.toml ├── Makefile ├── data │ └── iris.csv └── src │ └── main.rs ├── print-data-structs ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── sha3-dupe-detector ├── Cargo.toml ├── Makefile └── src │ ├── lib.rs │ └── main.rs ├── test.sh ├── vecdeque-fruit-salad ├── Cargo.toml ├── Makefile └── src │ └── main.rs ├── vector-fruit-salad ├── Cargo.toml ├── Makefile └── src │ └── main.rs └── webcrawl-wikipedia-rayon ├── Cargo.toml ├── Makefile └── src └── main.rs /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/rust:0-1-bullseye 2 | 3 | # Include lld linker to improve build times either by using environment variable 4 | # RUSTFLAGS="-C link-arg=-fuse-ld=lld" or with Cargo's configuration file (i.e see .cargo/config.toml). 5 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 6 | && apt-get -y install clang lld \ 7 | && apt-get autoremove -y && apt-get clean -y 8 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/rust 3 | { 4 | "name": "Rust", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Use the VARIANT arg to pick a Debian OS version: buster, bullseye 9 | // Use bullseye when on local on arm64/Apple Silicon. 10 | "VARIANT": "buster" 11 | } 12 | }, 13 | "runArgs": [ 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt", 16 | "seccomp=unconfined" 17 | ], 18 | 19 | // Configure tool-specific properties. 20 | "customizations": { 21 | // Configure properties specific to VS Code. 22 | "vscode": { 23 | // Set *default* container specific settings.json values on container create. 24 | "settings": { 25 | "lldb.executable": "/usr/bin/lldb", 26 | // VS Code don't watch files under ./target 27 | "files.watcherExclude": { 28 | "**/target/**": true 29 | }, 30 | "rust-analyzer.checkOnSave.command": "clippy" 31 | }, 32 | 33 | // Add the IDs of extensions you want installed when the container is created. 34 | "extensions": [ 35 | "rust-lang.rust-analyzer", 36 | "GitHub.copilot-nightly", 37 | "GitHub.copilot-labs", 38 | "GitHub.copilot-chat" 39 | ] 40 | } 41 | }, 42 | 43 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 44 | // "forwardPorts": [], 45 | 46 | // Use 'postCreateCommand' to run commands after the container is created. 47 | "postCreateCommand": "./setup.sh", 48 | 49 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 50 | "remoteUser": "vscode" 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Clippy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | toolchain: stable 14 | profile: minimal 15 | components: clippy, rustfmt 16 | override: true 17 | - name: Run clippy 18 | run: make lint 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | toolchain: stable 14 | profile: minimal 15 | components: clippy, rustfmt 16 | override: true 17 | - name: Run clippy 18 | run: make test 19 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #ignore onnx file 3 | *.onnx 4 | ort 5 | 6 | #ignore models 7 | model/ 8 | 9 | # Generated by Cargo 10 | # will have compiled files and executables 11 | target 12 | debug 13 | *.swp 14 | 15 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 16 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 17 | Cargo.lock 18 | 19 | # These are backup files generated by rustfmt 20 | **/*.rs.bk 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial 4.0 International Public License 2 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 3 | 4 | Section 1 – Definitions. 5 | 6 | Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 7 | Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. 8 | Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 9 | Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 10 | Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 11 | Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 12 | Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 13 | Licensor means the individual(s) or entity(ies) granting rights under this Public License. 14 | NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. 15 | Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 16 | Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 17 | You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. 18 | Section 2 – Scope. 19 | 20 | License grant. 21 | Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 22 | reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and 23 | produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 24 | Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 25 | Term. The term of this Public License is specified in Section 6(a). 26 | Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 27 | Downstream recipients. 28 | Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 29 | No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 30 | No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 31 | Other rights. 32 | 33 | Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 34 | Patent and trademark rights are not licensed under this Public License. 35 | To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. 36 | Section 3 – License Conditions. 37 | 38 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 39 | 40 | Attribution. 41 | 42 | If You Share the Licensed Material (including in modified form), You must: 43 | 44 | retain the following if it is supplied by the Licensor with the Licensed Material: 45 | identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 46 | a copyright notice; 47 | a notice that refers to this Public License; 48 | a notice that refers to the disclaimer of warranties; 49 | a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 50 | indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 51 | indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 52 | You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 53 | If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 54 | If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. 55 | Section 4 – Sui Generis Database Rights. 56 | 57 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 58 | 59 | for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; 60 | if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and 61 | You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 62 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 63 | Section 5 – Disclaimer of Warranties and Limitation of Liability. 64 | 65 | Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. 66 | To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. 67 | The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 68 | Section 6 – Term and Termination. 69 | 70 | This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 71 | Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 72 | 73 | automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 74 | upon express reinstatement by the Licensor. 75 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 76 | For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 77 | Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 78 | Section 7 – Other Terms and Conditions. 79 | 80 | The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 81 | Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 82 | Section 8 – Interpretation. 83 | 84 | For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 85 | To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 86 | No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 87 | Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 88 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. 89 | 90 | Creative Commons may be contacted at creativecommons.org. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | cargo install mdbook 3 | #install node 4 | #curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - &&\ 5 | #sudo apt-get install -y nodejs 6 | #npm install -g @githubnext/github-copilot-cli 7 | 8 | echo 'eval "$(github-copilot-cli alias -- "$0")"' >> ~/.bashrc 9 | 10 | build: 11 | mdbook build data-eng-rust-tutorial 12 | 13 | serve: 14 | mdbook serve -p 8000 -n 127.0.0.1 data-eng-rust-tutorial 15 | 16 | format: 17 | @echo "Formatting all projects with cargo" 18 | ./format.sh 19 | 20 | lint: 21 | @echo "Linting all projects with cargo" 22 | @rustup component add clippy 2> /dev/null 23 | ./lint.sh 24 | 25 | test: 26 | @echo "Testing all projects with cargo" 27 | ./test.sh 28 | 29 | check-gpu-linux: 30 | sudo lshw -C display 31 | 32 | linkcheck: 33 | mdbook test -L data-eng-rust-tutorial 34 | 35 | run: 36 | cargo run 37 | 38 | release: 39 | cargo build --release 40 | 41 | deploy: 42 | #if git is not configured, configure it 43 | if [ -z "$(git config --global user.email)" ]; then git config --global user.email "noah.gift@gmail.com" &&\ 44 | git config --global user.name "Noah Gift"; fi 45 | 46 | #install mdbook if not installed 47 | if [ ! -x "$(command -v mdbook)" ]; then cargo install mdbook; fi 48 | @echo "====> deploying to github" 49 | # if worktree exists, remove it: git worktree remove --force /tmp/book 50 | # otherwise add it: git worktree add /tmp/book gh-pages 51 | if [ -d /tmp/book ]; then git worktree remove --force /tmp/book; fi 52 | git worktree add -f /tmp/book gh-pages 53 | mdbook build data-eng-rust-tutorial 54 | rm -rf /tmp/book/* 55 | cp -rp data-eng-rust-tutorial/book/* /tmp/book/ 56 | cd /tmp/book && \ 57 | git add -A && \ 58 | git commit -m "deployed on $(shell date) by ${USER}" && \ 59 | git push origin gh-pages 60 | git update-ref -d refs/heads/gh-pages 61 | git push --force 62 | 63 | all: format lint test run -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🎓 Pragmatic AI Labs | Join 1M+ ML Engineers 2 | 3 | ### 🔥 Hot Course Offers: 4 | * 🤖 [Master GenAI Engineering](https://ds500.paiml.com/learn/course/0bbb5/) - Build Production AI Systems 5 | * 🦀 [Learn Professional Rust](https://ds500.paiml.com/learn/course/g6u1k/) - Industry-Grade Development 6 | * 📊 [AWS AI & Analytics](https://ds500.paiml.com/learn/course/31si1/) - Scale Your ML in Cloud 7 | * ⚡ [Production GenAI on AWS](https://ds500.paiml.com/learn/course/ehks1/) - Deploy at Enterprise Scale 8 | * 🛠️ [Rust DevOps Mastery](https://ds500.paiml.com/learn/course/ex8eu/) - Automate Everything 9 | 10 | ### 🚀 Level Up Your Career: 11 | * 💼 [Production ML Program](https://paiml.com) - Complete MLOps & Cloud Mastery 12 | * 🎯 [Start Learning Now](https://ds500.paiml.com) - Fast-Track Your ML Career 13 | * 🏢 Trusted by Fortune 500 Teams 14 | 15 | Learn end-to-end ML engineering from industry veterans at [PAIML.COM](https://paiml.com) 16 | 17 | 18 | [![Clippy](https://github.com/nogibjj/rust-data-engineering/actions/workflows/lint.yml/badge.svg)](https://github.com/nogibjj/rust-data-engineering/actions/workflows/lint.yml) 19 | [![Tests](https://github.com/nogibjj/rust-data-engineering/actions/workflows/tests.yml/badge.svg)](https://github.com/nogibjj/rust-data-engineering/actions/workflows/tests.yml) 20 | 21 | 22 | # Rust Data Engineering 23 | 24 | Projects for Rust Data Engineering Coursera course. 25 | Website for projects here: [https://nogibjj.github.io/rust-data-engineering/](https://nogibjj.github.io/rust-data-engineering/) 26 | 27 | ## Environments 28 | 29 | * Works with both AWS CodeCatalyst and GitHub Codespaces 30 | 31 | ## Feedback 32 | 33 | * Any suggestions or feedback? Feel free file a ticket. 34 | 35 | ## Labs (in sequential Order) 36 | 37 | ### Week 1- Rust Data Structures: Collections 38 | 39 | #### Sequences 40 | 41 | * Print Rust data structures: `cd print-data-structs && cargo run` 42 | * Vector Fruit Salad: `cd vector-fruit-salad && cargo run` 43 | * VecDeque Fruit Salad: `cd vecdeque-fruit-salad && cargo run` 44 | * Linkedin List Fruit Salad: `cd linked-list-fruit-salad && cargo run` 45 | * Fruit Salad CLI: `cd cli-salad && cargo run -- --number 3` 46 | 47 | #### Maps 48 | 49 | * HashMap frequency counter: `cd hashmap-count && cargo run` 50 | * HashMap language comparison: `cd hashmap-language && cargo run` 51 | * BTreeMap language comparison: `cd BTreeMap-language && cargo run` 52 | 53 | #### Sets 54 | 55 | * HashSet fruits: `cd hashset-fruit && cargo run` 56 | * BTreeSet fruits: `cd btreeset-fruit && cargo run` 57 | 58 | #### Misc 59 | 60 | * Binary Heap Fruit Salad with Fig Priority: `cd binaryheap-fruit && cargo run` 61 | 62 | ### Week 2-Safety, Security, and Concurrency with Rust 63 | 64 | * mutable fruit salad: `cd mutable-fruit-salad && cargo run` 65 | * cli customize fruit salad: `cd cli-customize-fruit-salad && cargo run -- fruits.csv` or `cargo run -- --fruits "apple, pear"` 66 | * data race example: `cd data-race && cargo run` (will not compile because of data race) 67 | 68 | #### Ciphers vs Encryption 69 | 70 | The main differences between ciphers and encryption algorithms: 71 | 72 | * Ciphers operate directly on the plaintext, substituting or transposing the letters mathematically. Encryption algorithms operate on the binary data representations of the plaintext. 73 | 74 | * Ciphers typically have a small key space based on simple operations like letter mappings or transposition rules. Encryption algorithms use complex math and very large key sizes. 75 | 76 | * Ciphers provide security through obscuring letter frequencies but are still vulnerable to cryptanalysis. Encryption algorithms rely on computational hardness assumptions. 77 | 78 | * Ciphers only handle textual data well. Encryption algorithms can handle all binary data like images, video, etc. 79 | 80 | In summary: 81 | 82 | * Ciphers like homophonic substitution operate directly on textual plaintext with simple math operations and fixed small key spaces. 83 | 84 | * Encryption algorithms like AES operate on any binary data with complex math and very large key sizes. 85 | 86 | * Ciphers are considered obsolete for serious encryption use today due to vulnerabilities. 87 | 88 | * Modern encryption provides provable security based on mathematical problems assumed to be computationally infeasible to solve. 89 | 90 | 91 | #### Suggested Exercises 92 | 93 | * _Data Race Detector_: Create a multi-threaded application that attempts to produce a data race, then show how Rust's ownership rules prevent this from occurring. 94 | 95 | * _Memory Leak Preventer_: Build an application that would typically suffer from memory leaks in other languages, such as a complex tree structure. Rust's automatic memory management through RAII (Resource Acquisition Is Initialization) should ensure no memory leaks occur. 96 | 97 | * _Null Pointer Safety_: Create a project demonstrating how Rust's Option and Result types are used to handle potentially null or error-producing cases safely. 98 | 99 | * _Immutable by Default_: Design a system where mutability would cause bugs (for instance, a simulation with entities that should not be able to change once created) and show how Rust's immutability by default prevents these issues. 100 | 101 | * _System with Lifetimes_: Show how lifetimes can prevent use-after-free bugs by building an application where objects have distinct lifetimes that must be enforced. 102 | 103 | * _No Segfault System_: Create a project that would usually segfault in other languages, and demonstrate how Rust prevents this. 104 | 105 | * _Web Server_: Build a small multi-threaded web server. Show how Rust's safety features prevent common bugs in concurrent programming. 106 | 107 | * _Safe FFI_: Create a project that uses Rust's Foreign Function Interface (FFI) to interoperate with C libraries safely. 108 | 109 | * _Safe Transmute_: Write a program that demonstrates the use of safe transmutes in Rust. This could be a good way to show how Rust can avoid undefined behavior that's common in languages like C or C++. 110 | 111 | * _Bounds Checking_: Design a system that would typically have a lot of array bounds errors, then show how Rust's automatic bounds checking prevents these kinds of errors. 112 | 113 | * _Immutable Concurrency_: Create a project that takes advantage of Rust's ability to share immutable data among threads without data races. 114 | 115 | * _Command Line Application_: Build a command-line application that processes user input. Use Rust's strong type system and pattern matching to handle different types of input safely and cleanly 116 | 117 | ### Week 3-Rust Data Engineering Libraries and Tools 118 | 119 | #### Suggested Exercises 120 | 121 | - **CSV Data Processing**: A tool for processing large CSV files, showcasing efficient data reading, filtering, and aggregation capabilities of Rust. 122 | - **Database Interaction**: An application that interacts with a SQL database (like PostgreSQL) using Diesel, demonstrating CRUD operations, migrations, and complex queries. 123 | - **Data Visualization**: A CLI tool that generates graphs and charts from input data using plotters. 124 | - **Web Scraper**: A multi-threaded web scraper that fetches and parses data from several web pages concurrently. 125 | - **REST API Consumer**: An application that interacts with a REST API to fetch, process, and visualize data. 126 | - **Log Parser**: A tool to parse and analyze server log files. It can extract meaningful information and statistics and provide insights about the server performance. 127 | - **File System Analyzer**: An application that provides insights about disk usage, like the `du` command in Unix. 128 | - **Real-Time Twitter Analysis**: A real-time tweet analysis tool that uses Twitter Stream API to fetch tweets and analyze them (for example, performing sentiment analysis). 129 | - **Stock Market Analyzer**: An application that fetches stock market data from a free API and performs various analyses. 130 | - **Text Analytics**: A text analytics library that provides functionalities like sentiment analysis, named entity recognition, etc. 131 | - **Delta Lake Interaction**: A project demonstrating interaction with Delta Lake for processing large amounts of data. 132 | - **AWS SDK usage**: A project demonstrating the use of AWS SDK in Rust for tasks such as accessing S3 buckets, performing operations on DynamoDB, etc. 133 | - **Data Processing with Polars**: A project demonstrating how to perform large-scale data processing with the Polars library in Rust. 134 | - **Kafka Producer/Consumer**: An application that produces and consumes messages from Kafka. 135 | - **gRPC Microservices**: A basic microservices setup using gRPC, demonstrating how Rust can be used for backend development. 136 | - **Apache Arrow usage**: A project demonstrating how to use Apache Arrow for columnar data processing in Rust. 137 | - **Parquet File Processing**: An application that reads and writes Parquet files, demonstrating how Rust can be used for efficient data engineering tasks. 138 | - **Data Engineering with TiKV**: A project demonstrating how to use TiKV, a distributed transactional key-value database built in Rust. 139 | 140 | ### Week 4-Rust 141 | 142 | 143 | #### Suggested Exercises 144 | 145 | - **Rust-based ETL Pipeline**: Develop an ETL (Extract, Transform, Load) pipeline using various Rust libraries to process and transfer data between different storage systems. 146 | 147 | - **Web Scraper with Rust**: Build a concurrent web scraper that can efficiently scrape large amounts of data from web pages. 148 | 149 | - **Rust REST API Server**: Design a REST API server in Rust that serves data from a database. Use the Diesel ORM for database interactions. 150 | 151 | - **Real-time Data Streaming with Rust**: Implement a real-time data streaming application, processing streams of data in a concurrent manner. 152 | 153 | - **Rust-based Data Lake**: Use the Delta Lake Rust API to create a data lake solution. Implement CRUD operations on the data lake. 154 | 155 | - **Big Data Processing with Rust and Apache Arrow**: Use Apache Arrow to perform efficient in-memory big data processing. 156 | 157 | - **Rust and AWS SDK**: Use the AWS SDK for Rust to interact with AWS services such as S3 and DynamoDB. 158 | 159 | - **gRPC Service in Rust**: Implement a gRPC service in Rust that performs CRUD operations on a database. 160 | 161 | - **Log Analyzer**: Create a log analyzer that can process large log files concurrently and provide useful insights from logs. 162 | 163 | - **Distributed Systems with Rust**: Create a simple distributed system using Rust's concurrency features. This could be a simple key-value store or a message-passing system. 164 | 165 | - **Rust and GraphQL**: Implement a GraphQL API in Rust using libraries like Juniper. 166 | 167 | - **Data Serialization with Rust**: Use libraries like serde to perform data serialization and deserialization in various formats (JSON, XML, etc.) 168 | 169 | - **Rust and Kafka**: Use Rust to interact with Kafka, implementing a producer and consumer system. 170 | 171 | - **Data Validation Service**: Create a service that validates input data based on predefined rules. This could be a web service or a library that other services can use. 172 | 173 | - **Rust and Machine Learning**: Use Rust machine learning libraries to implement a simple prediction model. You could use the data processed in the ETL pipeline or the data lake for this. 174 | 175 | ### Lab: Modifying a Rust Command-Line Tool 176 | 177 | In this lab you will gain experience extending an existing Rust project by forking and modifying a simple command-line tool. 178 | 179 | **Steps** 180 | 181 | 1. Fork the repository at **https://github.com/nogibjj/rust-data-engineering** 182 | 183 | 2. Clone your forked repository 184 | 185 | 3. Navigate to one of the command-line tool projects 186 | 187 | 4. Make a small modification to the tool such as: 188 | 189 | - Adding a new command line argument 190 | 191 | - Supporting additional input file formats 192 | 193 | - Adding more processing logic 194 | 195 | - Changing output formatting 196 | 197 | 5. Run `cargo build` to compile your changes 198 | 199 | 6. Run `cargo run` to test your modified tool 200 | 201 | 7. Commit your changes and push to your forked repository 202 | 203 | 204 | **Deliverable** 205 | 206 | Submit a link to your forked repository showing the code changes. 207 | 208 | 209 | **Goals** 210 | 211 | This hands-on lab provides experience with: 212 | 213 | - Forking and cloning a Rust project 214 | 215 | - Modifying existing Rust code 216 | 217 | - Running `cargo build` and `cargo run` 218 | 219 | - Version control with git 220 | 221 | - Making a pull request (optional) 222 | 223 | 224 | ### Technical Notes 225 | 226 | ## Makefile 227 | 228 | Each subdirectory project uses this style to make it easy to test and run 229 | 230 | ``` 231 | format: 232 | cargo fmt --quiet 233 | 234 | lint: 235 | cargo clippy --quiet 236 | 237 | test: 238 | cargo test --quiet 239 | 240 | run: 241 | cargo run 242 | 243 | all: format lint test run 244 | ``` 245 | 246 | 247 | ## References 248 | 249 | * [Rust Collections](https://doc.rust-lang.org/std/collections/index.html) 250 | * [GitHub Copilot CLI](https://www.npmjs.com/package/@githubnext/github-copilot-cli) 251 | * [Rust Fundamentals](https://github.com/alfredodeza/rust-fundamentals) 252 | * [Rust Tutorial](https://nogibjj.github.io/rust-tutorial/) 253 | * [Rust MLOps Template](https://github.com/nogibjj/mlops-template) 254 | -------------------------------------------------------------------------------- /bashrc: -------------------------------------------------------------------------------- 1 | /home/vscode/.bashrc -------------------------------------------------------------------------------- /binaryheap-fruit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "binaryheap-fruit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /binaryheap-fruit/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /binaryheap-fruit/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use rand::thread_rng; 3 | use std::cmp::Ord; 4 | use std::collections::BinaryHeap; 5 | 6 | #[derive(Eq, PartialEq)] 7 | enum Fruit { 8 | Fig, 9 | Other(String), 10 | } 11 | 12 | // We define Figs as the highest priority by implementing Ord 13 | impl Ord for Fruit { 14 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 15 | match (self, other) { 16 | (Fruit::Fig, Fruit::Fig) => std::cmp::Ordering::Equal, 17 | (Fruit::Fig, Fruit::Other(_)) => std::cmp::Ordering::Greater, 18 | (Fruit::Other(_), Fruit::Fig) => std::cmp::Ordering::Less, 19 | (Fruit::Other(_), Fruit::Other(_)) => std::cmp::Ordering::Equal, 20 | } 21 | } 22 | } 23 | 24 | impl PartialOrd for Fruit { 25 | fn partial_cmp(&self, other: &Self) -> Option { 26 | Some(self.cmp(other)) 27 | } 28 | } 29 | 30 | fn generate_fruit_salad() -> BinaryHeap { 31 | let mut rng = thread_rng(); 32 | let fruits = vec![ 33 | "Apple", 34 | "Orange", 35 | "Pear", 36 | "Peach", 37 | "Banana", 38 | "Fig", 39 | "Fig", 40 | "Fig", 41 | "Fig", 42 | ]; 43 | let mut fruit_salad = BinaryHeap::new(); 44 | 45 | let mut figs_count = 0; 46 | while figs_count < 2 { 47 | let fruit = fruits.choose(&mut rng).unwrap(); 48 | if *fruit == "Fig" { 49 | figs_count += 1; 50 | fruit_salad.push(Fruit::Fig); 51 | } else { 52 | fruit_salad.push(Fruit::Other(fruit.to_string())); 53 | } 54 | } 55 | 56 | fruit_salad 57 | } 58 | 59 | fn main() { 60 | let fruit_salad = generate_fruit_salad(); 61 | println!("Random Fruit Salad With Two Servings of Figs:"); 62 | for fruit in fruit_salad.into_sorted_vec() { 63 | match fruit { 64 | Fruit::Fig => println!("Fig"), 65 | Fruit::Other(fruit_name) => println!("{}", fruit_name), 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /btree-language/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "btree-language" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /btree-language/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /btree-language/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | fn init_languages() -> BTreeMap { 4 | let mut languages = BTreeMap::new(); 5 | languages.insert("JavaScript".to_string(), 1995); 6 | languages.insert("HTML/CSS".to_string(), 1990); 7 | languages.insert("Python".to_string(), 1991); 8 | languages.insert("SQL".to_string(), 1974); 9 | languages.insert("TypeScript".to_string(), 2012); 10 | languages.insert("Bash/Shell".to_string(), 1989); 11 | languages.insert("Java".to_string(), 1995); 12 | languages.insert("C#".to_string(), 2000); 13 | languages.insert("C++".to_string(), 1985); 14 | languages.insert("C".to_string(), 1972); 15 | languages.insert("PHP".to_string(), 1995); 16 | languages.insert("PowerShell".to_string(), 2006); 17 | languages.insert("Go".to_string(), 2007); 18 | languages.insert("Rust".to_string(), 2010); 19 | 20 | languages 21 | } 22 | 23 | fn calculate_weights(languages: BTreeMap) -> BTreeMap { 24 | let mut years_active: BTreeMap = BTreeMap::new(); 25 | for (language, year) in languages { 26 | years_active.insert(language, 2024 - year); 27 | } 28 | 29 | let min_year = *years_active.values().min().unwrap_or(&0); 30 | let max_year = *years_active.values().max().unwrap_or(&0); 31 | 32 | let mut weights = BTreeMap::new(); 33 | for (language, &year) in years_active.iter() { 34 | let normalized_year = (year - min_year) as f64 / (max_year - min_year) as f64; 35 | let weight = (normalized_year * 99.0) as i32 + 1; // weight between 1 and 100 36 | weights.insert(language.to_string(), weight); 37 | } 38 | 39 | weights 40 | } 41 | 42 | fn main() { 43 | let languages = init_languages(); 44 | let weights = calculate_weights(languages); 45 | 46 | println!("Language weighing from 1-100 by age (1 is newest and 100 is oldest):"); 47 | for (language, weight) in &weights { 48 | println!("{}: {}", language, weight); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /btreeset-fruit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "btreeset-fruit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /btreeset-fruit/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /btreeset-fruit/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use rand::thread_rng; 3 | use std::collections::BTreeSet; 4 | 5 | fn main() { 6 | let fruits = vec![ 7 | "apple", 8 | "banana", 9 | "cherry", 10 | "date", 11 | "elderberry", 12 | "fig", 13 | "grape", 14 | "honeydew", 15 | ]; 16 | let amounts = [1, 3, 5, 7, 9]; 17 | 18 | let mut rng = thread_rng(); 19 | 20 | for amount in amounts.iter() { 21 | let mut fruit_set = BTreeSet::new(); 22 | let mut shuffled_fruits = fruits.clone(); 23 | shuffled_fruits.shuffle(&mut rng); 24 | 25 | for fruit in shuffled_fruits { 26 | fruit_set.insert(fruit); 27 | if fruit_set.len() >= *amount { 28 | break; 29 | } 30 | } 31 | 32 | println!("{}: {:?}", amount, fruit_set); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /caesar-cipher-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "caeser-cipher-cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "4.3.17", features = ["derive"] } 10 | -------------------------------------------------------------------------------- /caesar-cipher-cli/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /caesar-cipher-cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This code defines two functions: encrypt and decrypt. 3 | The encrypt function takes a plaintext string and a shift value, and returns the ciphertext string. The decrypt function takes a ciphertext string and a shift value, 4 | and returns the plaintext string. 5 | 6 | */ 7 | 8 | pub fn encrypt(text: &str, shift: u8) -> String { 9 | let mut result = String::new(); 10 | for c in text.chars() { 11 | if c.is_ascii_alphabetic() { 12 | let base = if c.is_ascii_lowercase() { b'a' } else { b'A' }; 13 | let offset = (c as u8 - base + shift) % 26; 14 | result.push((base + offset) as char); 15 | } else { 16 | result.push(c); 17 | } 18 | } 19 | result 20 | } 21 | 22 | pub fn decrypt(text: &str, shift: u8) -> String { 23 | encrypt(text, 26 - shift) 24 | } 25 | -------------------------------------------------------------------------------- /caesar-cipher-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | To run: 4 | 5 | cargo run -- --message "Off to the bunker. Every person for themselves" --encrypt --shift 10 6 | 7 | To decrypt: 8 | 9 | cargo run -- --message "Ypp dy dro lexuob. Ofobi zobcyx pyb drowcovfoc" --decrypt --shift 10 10 | 11 | */ 12 | 13 | 14 | use caeser_cipher_cli::{decrypt, encrypt}; 15 | use clap::Parser; 16 | 17 | /// CLI tool to encrypt and decrypt messages using the caeser cipher 18 | #[derive(Parser, Debug)] 19 | #[command(author, version, about, long_about = None)] 20 | struct Args { 21 | /// Encrypt the message 22 | #[arg(short, long)] 23 | encrypt: bool, 24 | 25 | /// decrypt the message 26 | #[arg(short, long)] 27 | decrypt: bool, 28 | 29 | /// The message to encrypt or decrypt 30 | #[arg(short, long)] 31 | message: String, 32 | 33 | /// The shift to use for the cipher 34 | /// Must be between 1 and 25, the default is 3 35 | #[arg(short, long, default_value = "3")] 36 | shift: u8, 37 | } 38 | 39 | // run it 40 | fn main() { 41 | let args = Args::parse(); 42 | if args.encrypt { 43 | println!("{}", encrypt(&args.message, args.shift)); 44 | } else if args.decrypt { 45 | println!("{}", decrypt(&args.message, args.shift)); 46 | } else { 47 | println!("Please specify either --encrypt or --decrypt"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /caesar-cipher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "caesar-cipher" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /caesar-cipher/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /caesar-cipher/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This code defines two functions: encrypt and decrypt. 3 | The encrypt function takes a plaintext string and a shift value, and returns the ciphertext string. The decrypt function takes a ciphertext string and a shift value, 4 | and returns the plaintext string. 5 | 6 | */ 7 | 8 | pub fn encrypt(text: &str, shift: u8) -> String { 9 | let mut result = String::new(); 10 | for c in text.chars() { 11 | if c.is_ascii_alphabetic() { 12 | let base = if c.is_ascii_lowercase() { b'a' } else { b'A' }; 13 | let offset = (c as u8 - base + shift) % 26; 14 | result.push((base + offset) as char); 15 | } else { 16 | result.push(c); 17 | } 18 | } 19 | result 20 | } 21 | 22 | pub fn decrypt(text: &str, shift: u8) -> String { 23 | encrypt(text, 26 - shift) 24 | } 25 | -------------------------------------------------------------------------------- /caesar-cipher/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This code defines two functions: encrypt and decrypt. 3 | The encrypt function takes a plaintext string and a shift value, 4 | and returns the ciphertext string. 5 | The decrypt function takes a ciphertext string and a shift value, 6 | and returns the plaintext string. 7 | 8 | */ 9 | 10 | use caesar_cipher::decrypt; 11 | use caesar_cipher::encrypt; 12 | 13 | fn main() { 14 | let plaintext = "the quick brown fox jumps over the lazy dog"; 15 | let shift = 3; 16 | let ciphertext = encrypt(plaintext, shift); 17 | let decrypted_text = decrypt(&ciphertext, shift); 18 | println!("Plaintext: {}", plaintext); 19 | println!("Ciphertext: {}", ciphertext); 20 | println!("Decrypted text: {}", decrypted_text); 21 | } 22 | -------------------------------------------------------------------------------- /calc-cli-with-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calc-cli-with-tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | clap = { version = "4.4", features = ["derive"] } -------------------------------------------------------------------------------- /calc-cli-with-tests/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /calc-cli-with-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This is a library that randomly returns fruits native to Portugual. 3 | The style of the code is that we use a constant array of strings to store the fruits. 4 | We then use this const in a function that later get called in the main.rs file as a CLI. 5 | 6 | The CLI should support the following: 7 | 8 | //the quantity of fruits to return 9 | --count 5 10 | */ 11 | 12 | use rand::Rng; 13 | 14 | // a vector of immutable strings that represent fruits native to Portugal and the Azores 15 | const FRUITS: [&str; 10] = [ 16 | "banana", 17 | "apple", 18 | "orange", 19 | "pear", 20 | "pineapple", 21 | "grape", 22 | "strawberry", 23 | "raspberry", 24 | "blueberry", 25 | "blackberry", 26 | ]; 27 | 28 | /* 29 | return a random fruit from the FRUITS vector 30 | Accepts a count of fruits to return 31 | */ 32 | 33 | pub fn get_fruits(count: u32) -> Vec { 34 | let mut fruits = Vec::new(); 35 | for _ in 0..count { 36 | let fruit = rand::thread_rng().gen_range(0..FRUITS.len()); 37 | fruits.push(FRUITS[fruit].to_string()); 38 | } 39 | fruits 40 | } 41 | 42 | /*Test 43 | 44 | A test that checks if the get_fruits function returns the correct number of fruits 45 | */ 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | 51 | #[test] 52 | fn test_get_fruits() { 53 | let fruits = get_fruits(5); 54 | assert_eq!(fruits.len(), 5); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /calc-cli-with-tests/src/main.rs: -------------------------------------------------------------------------------- 1 | /*A cli that generates random fruits */ 2 | 3 | use calc_cli_with_tests::get_fruits; 4 | use clap::Parser; 5 | 6 | /// CLI tool to return random fruits 7 | #[derive(Parser, Debug)] 8 | #[command(author, version, about, long_about = None)] 9 | struct Args { 10 | /// The quantity of fruits to return 11 | #[clap(short, long, default_value = "1")] 12 | count: u32, 13 | } 14 | 15 | fn main() { 16 | let args = Args::parse(); 17 | let fruits = get_fruits(args.count); 18 | println!("fruits: {:?}", fruits); 19 | } 20 | -------------------------------------------------------------------------------- /cli-customize-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli-customize-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "4.3.4", features = ["derive"] } 10 | csv = "1.1.6" 11 | rand = "0.8.5" 12 | 13 | [lib] 14 | name = "fruit_salad_maker" 15 | path = "src/lib.rs" -------------------------------------------------------------------------------- /cli-customize-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /cli-customize-fruit-salad/fruits.csv: -------------------------------------------------------------------------------- 1 | maçã, banana, laranja, morango, abacaxi, manga, pêssego, limão, melancia, uva -------------------------------------------------------------------------------- /cli-customize-fruit-salad/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This code defines a function called create_fruit_salad 3 | that takes a mutable vector of strings as input and returns 4 | a new vector of strings that contains the same elements as the input vector, 5 | but in a random order. 6 | */ 7 | 8 | use rand::seq::SliceRandom; 9 | use rand::thread_rng; 10 | 11 | pub fn create_fruit_salad(mut fruits: Vec) -> Vec { 12 | let mut rng = thread_rng(); 13 | fruits.shuffle(&mut rng); 14 | 15 | fruits 16 | } 17 | -------------------------------------------------------------------------------- /cli-customize-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Usage: 3 | 4 | cargo run -- fruits.csv 5 | or 6 | cargo run -- --fruits "apple, pear" 7 | 8 | */ 9 | 10 | use clap::Parser; 11 | use fruit_salad_maker::create_fruit_salad; 12 | 13 | #[derive(Parser)] 14 | #[clap( 15 | version = "1.0", 16 | author = "Your Name ", 17 | about = "Make a Fruit Salad" 18 | )] 19 | struct Opts { 20 | /// Fruits input as a string of comma separated values 21 | #[clap(short, long)] 22 | fruits: Option, 23 | csvfile: Option, 24 | } 25 | 26 | // Function that converts a csv file to a vector of strings 27 | fn csv_to_vec(csv: &str) -> Vec { 28 | csv.split(',') 29 | .map(|s| s.trim().to_string()) 30 | .collect() 31 | } 32 | fn display_fruit_salad(fruits: Vec) { 33 | println!("Your fruit salad contains:"); 34 | for fruit in fruits { 35 | println!("{}", fruit); 36 | } 37 | } 38 | 39 | fn main() { 40 | let opts: Opts = Opts::parse(); 41 | 42 | // Use fruits from CSV file or command-line input 43 | let fruit_list = match opts.csvfile { 44 | Some(filename) => { 45 | let fruits = std::fs::read_to_string(filename) 46 | .expect("Could not read file"); 47 | csv_to_vec(&fruits) 48 | }, 49 | None => { 50 | opts.fruits.unwrap_or_default() 51 | .split(',') 52 | .map(|s| s.trim().to_string()) 53 | .collect() 54 | }, 55 | }; 56 | 57 | // display fruit salad 58 | let fruit_salad = create_fruit_salad(fruit_list); 59 | display_fruit_salad(fruit_salad); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /cli-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "4.3.4", features = ["derive"] } 10 | rand = "0.8.5" 11 | 12 | -------------------------------------------------------------------------------- /cli-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /cli-salad/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use rand::thread_rng; 3 | 4 | pub fn create_fruit_salad(num_fruits: usize) -> Vec { 5 | let fruits = vec![ 6 | "Arbutus".to_string(), 7 | "Loquat".to_string(), 8 | "Strawberry Tree Berry".to_string(), 9 | "Pomegranate".to_string(), 10 | "Fig".to_string(), 11 | "Cherry".to_string(), 12 | "Orange".to_string(), 13 | "Pear".to_string(), 14 | "Peach".to_string(), 15 | "Apple".to_string(), 16 | ]; 17 | 18 | let mut rng = thread_rng(); 19 | let mut fruits = fruits; 20 | fruits.shuffle(&mut rng); 21 | 22 | fruits.into_iter().take(num_fruits).collect() 23 | } 24 | -------------------------------------------------------------------------------- /cli-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use cli_salad::create_fruit_salad; 3 | 4 | #[derive(Parser)] 5 | #[clap( 6 | version = "1.0", 7 | author = "Your Name ", 8 | about = "Number of fruits to include in the salad" 9 | )] 10 | struct Opts { 11 | #[clap(short, long)] 12 | number: usize, 13 | } 14 | 15 | fn main() { 16 | let opts: Opts = Opts::parse(); 17 | 18 | // Get the number of fruits the user requested 19 | let num_fruits = opts.number; 20 | 21 | // Create the fruit salad 22 | create_fruit_salad(num_fruits); 23 | 24 | // Print the fruit salad in human readable format with a count of fruits used 25 | println!( 26 | "Created Fruit salad with {} fruits: {:?}", 27 | num_fruits, 28 | create_fruit_salad(num_fruits) 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /community-detection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "community-detection" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | petgraph = "0.6.3" 10 | -------------------------------------------------------------------------------- /community-detection/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /community-detection/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub const TWITTER_USERNAMES: [&str; 140] = [ 2 | "blackmattersus", 3 | "bleepthepolice", 4 | "jenn_abrams", 5 | "leroylovesusa", 6 | "missourinewsus", 7 | "rightnpr", 8 | "ten_gop", 9 | "traceyhappymom", 10 | "trayneshacole", 11 | "traceyhappymom", 12 | "ten_gop", 13 | "leroylovesusa", 14 | "leroylovesusa", 15 | "traceyhappymom", 16 | "traceyhappymom", 17 | "traceyhappymom", 18 | "ten_gop", 19 | "traceyhappymom", 20 | "jenn_abrams", 21 | "ten_gop", 22 | "rightnpr", 23 | "traceyhappymom", 24 | "leroylovesusa", 25 | "ten_gop", 26 | "ten_gop", 27 | "jenn_abrams", 28 | "leroylovesusa", 29 | "leroylovesusa", 30 | "ten_gop", 31 | "traceyhappymom", 32 | "ten_gop", 33 | "leroylovesusa", 34 | "ten_gop", 35 | "traceyhappymom", 36 | "jenn_abrams", 37 | "trayneshacole", 38 | "ten_gop", 39 | "ten_gop", 40 | "leroylovesusa", 41 | "leroylovesusa", 42 | "leroylovesusa", 43 | "leroylovesusa", 44 | "ten_gop", 45 | "ten_gop", 46 | "leroylovesusa", 47 | "ten_gop", 48 | "ten_gop", 49 | "traceyhappymom", 50 | "traceyhappymom", 51 | "ten_gop", 52 | "traceyhappymom", 53 | "ten_gop", 54 | "jenn_abrams", 55 | "ten_gop", 56 | "ten_gop", 57 | "leroylovesusa", 58 | "worldofhashtags", 59 | "traceyhappymom", 60 | "ten_gop", 61 | "leroylovesusa", 62 | "ten_gop", 63 | "traceyhappymom", 64 | "traceyhappymom", 65 | "ten_gop", 66 | "traceyhappymom", 67 | "traceyhappymom", 68 | "worldofhashtags", 69 | "ten_gop", 70 | "traceyhappymom", 71 | "ten_gop", 72 | "ten_gop", 73 | "ten_gop", 74 | "rightnpr", 75 | "ten_gop", 76 | "leroylovesusa", 77 | "traceyhappymom", 78 | "leroylovesusa", 79 | "leroylovesusa", 80 | "traceyhappymom", 81 | "traceyhappymom", 82 | "traceyhappymom", 83 | "ten_gop", 84 | "leroylovesusa", 85 | "traceyhappymom", 86 | "ten_gop", 87 | "blackmattersus", 88 | "ten_gop", 89 | "leroylovesusa", 90 | "ten_gop", 91 | "traceyhappymom", 92 | "jenn_abrams", 93 | "trayneshacole", 94 | "ten_gop", 95 | "ten_gop", 96 | "leroylovesusa", 97 | "leroylovesusa", 98 | "leroylovesusa", 99 | "leroylovesusa", 100 | "ten_gop", 101 | "ten_gop", 102 | "leroylovesusa", 103 | "ten_gop", 104 | "ten_gop", 105 | "traceyhappymom", 106 | "traceyhappymom", 107 | "worldofhashtags", 108 | "blackmattersus", 109 | "jenn_abrams", 110 | "traceyhappymom", 111 | "leroylovesusa", 112 | "jenn_abrams", 113 | "leroylovesusa", 114 | "traceyhappymom", 115 | "leroylovesusa", 116 | "jenn_abrams", 117 | "ten_gop", 118 | "leroylovesusa", 119 | "ten_gop", 120 | "ten_gop", 121 | // The fake community of journalists 122 | "journalist1", 123 | "journalist2", 124 | "journalist3", 125 | "journalist1", 126 | "journalist2", 127 | "journalist1", 128 | "journalist3", 129 | "journalist2", 130 | "journalist1", 131 | "journalist3", 132 | "journalist2", 133 | "journalist3", 134 | "journalist1", 135 | "journalist2", 136 | "journalist1", 137 | "journalist3", 138 | "journalist2", 139 | "journalist1", 140 | "journalist3", 141 | "journalist2", 142 | "journalist3", 143 | ]; 144 | -------------------------------------------------------------------------------- /community-detection/src/main.rs: -------------------------------------------------------------------------------- 1 | use community_detection::TWITTER_USERNAMES; 2 | use petgraph::algo::kosaraju_scc; 3 | use petgraph::prelude::*; 4 | use std::collections::HashMap; 5 | 6 | fn main() { 7 | // Create a new directed Graph 8 | let mut graph = DiGraph::<&str, &str>::new(); 9 | 10 | // Create a HashMap to store node indices by user name 11 | let mut nodes = HashMap::new(); 12 | 13 | // Iterate over the data to populate the graph 14 | for window in TWITTER_USERNAMES.windows(2) { 15 | let user = window[0]; 16 | let mention = window[1]; 17 | 18 | // Add the nodes to the graph and to the HashMap 19 | let user_node = *nodes.entry(user).or_insert_with(|| graph.add_node(user)); 20 | let mention_node = *nodes 21 | .entry(mention) 22 | .or_insert_with(|| graph.add_node(mention)); 23 | 24 | // Add the edge to the graph 25 | graph.add_edge(user_node, mention_node, "retweets"); 26 | } 27 | 28 | // Use the Kosaraju's algorithm to detect strongly connected components 29 | let scc = kosaraju_scc(&graph); 30 | for component in scc { 31 | println!("{} nodes in community discovered", component.len()); 32 | let usernames: Vec<&str> = component 33 | .iter() 34 | .map(|&node_index| graph[node_index]) 35 | .collect(); 36 | println!("{:?}", usernames); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /csv-demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "csv-demo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | csv = "1.2.2" -------------------------------------------------------------------------------- /csv-demo/data/text.txt: -------------------------------------------------------------------------------- 1 | city:region:country:population 2 | Southborough:MA:United States:9686 3 | Northbridge:MA:United States:14061 4 | Westborough:MA:United States:29313 5 | Marlborough:MA:United States:38334 6 | Springfield:MA:United States:152227 7 | Springfield:MO:United States:150443 8 | Springfield:NJ:United States:14976 9 | Springfield:OH:United States:64325 10 | Springfield:OR:United States:56032 11 | Concord:NH:United States:42605 -------------------------------------------------------------------------------- /csv-demo/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, io, process}; 2 | 3 | fn example() -> Result<(), Box> { 4 | // Build the CSV reader and iterate over each record. 5 | let mut rdr = csv::Reader::from_reader(io::stdin()); 6 | for result in rdr.records() { 7 | // The iterator yields Result, so we check the 8 | // error here.. 9 | let record = result?; 10 | println!("{:?}", record); 11 | } 12 | Ok(()) 13 | } 14 | 15 | fn main() { 16 | if let Err(err) = example() { 17 | println!("error running example: {}", err); 18 | process::exit(1); 19 | } 20 | } -------------------------------------------------------------------------------- /data-eng-rust-tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /data-eng-rust-tutorial/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Noah Gift"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Rust Data Engineering" 7 | -------------------------------------------------------------------------------- /data-eng-rust-tutorial/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /data-eng-rust-tutorial/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | 3 | ## Section 1: Designing Data Processing Systems 4 | 5 | ### Topics 6 | 7 | * Storage technology selection 8 | * Data pipeline design 9 | * Data processing solution design 10 | * Data Warehousing & Processing Migration 11 | 12 | ### Demos 13 | 14 | ## Section 2: Building & Operationalizing Data Processing Systems 15 | 16 | ### Topics 17 | 18 | * Storage system implementation 19 | * Pipeline Building & Operationalization 20 | * Processing infrastructure implementation 21 | 22 | ### Demos 23 | 24 | ## Section 3: Operationalizing ML Models 25 | 26 | ### Topics 27 | 28 | * Pre-built ML models as a service 29 | * ML pipeline deployment 30 | * Training & serving infrastructure selection 31 | * ML model measurement, monitoring & troubleshooting 32 | 33 | ### Demos 34 | 35 | ## Section 4: Ensuring Solution Quality 36 | 37 | ### Topics 38 | * Security & compliance design 39 | * Scalability & efficiency assurance 40 | * Reliability & fidelity assurance 41 | * Flexibility & portability assurance 42 | 43 | ### Demos -------------------------------------------------------------------------------- /data-race/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data-race" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /data-race/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /data-race/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | // Mutex that protects the data vector, and then we spawn three threads 4 | //that each acquire a lock on the mutex and modify an element of the vector. 5 | 6 | use std::sync::Mutex; 7 | use std::thread; 8 | 9 | fn main() { 10 | let data = Mutex::new(vec![1, 2, 3]); 11 | 12 | let handles: Vec<_> = (0..3).map(|i| { 13 | let data = data.clone(); 14 | thread::spawn(move || { 15 | let mut data = data.lock().unwrap(); 16 | data[i] += 1; 17 | }) 18 | }).collect(); 19 | 20 | for handle in handles { 21 | handle.join().unwrap(); 22 | } 23 | 24 | println!("{:?}", data); 25 | } 26 | 27 | */ 28 | 29 | use std::thread; 30 | 31 | fn main() { 32 | let mut data = vec![1, 2, 3]; 33 | 34 | for i in 0..3 { 35 | // Try to capture a mutable reference in multiple threads 36 | // This will fail to compile! 37 | thread::spawn(move || { 38 | data[i] += 1; 39 | }); 40 | } 41 | 42 | // No data race can occur, this will not compile. 43 | } 44 | -------------------------------------------------------------------------------- /decoder-ring/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoder-ring" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "4.3.17", features = ["derive"] } 10 | -------------------------------------------------------------------------------- /decoder-ring/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /decoder-ring/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn gen_counts() -> HashMap { 4 | // Reference letter frequencies in English 5 | let mut eng_freq: HashMap = HashMap::new(); 6 | 7 | // Accounts for 80% of all letters in English 8 | eng_freq.insert('e', 12.7); 9 | eng_freq.insert('t', 9.1); 10 | eng_freq.insert('a', 8.2); 11 | eng_freq.insert('o', 7.5); 12 | eng_freq.insert('i', 7.0); 13 | eng_freq.insert('n', 6.7); 14 | eng_freq.insert('s', 6.3); 15 | eng_freq.insert('h', 6.1); 16 | eng_freq.insert('r', 6.0); 17 | eng_freq.insert('d', 4.3); 18 | 19 | eng_freq 20 | } 21 | 22 | fn stats_analysis(text: &str) -> Vec<(char, u32, f32, Option, f32)> { 23 | let mut counts: HashMap = HashMap::new(); 24 | 25 | for c in text.chars() { 26 | *counts.entry(c).or_insert(0) += 1; 27 | } 28 | 29 | let total: u32 = counts.values().sum(); 30 | 31 | let eng_freq_map = gen_counts(); 32 | let eng_freq_map: HashMap = eng_freq_map.iter().map(|(k, v)| (*k, *v)).collect(); 33 | 34 | let mut results = Vec::new(); 35 | 36 | for (letter, count) in &counts { 37 | let freq = (*count as f32 / total as f32) * 100.0; 38 | let eng_freq = eng_freq_map.get(&letter.to_ascii_lowercase()).cloned(); 39 | 40 | let eng_freq_diff = eng_freq.map_or(0.0, |f| (freq - f).abs()); 41 | 42 | results.push((*letter, *count, freq, eng_freq, eng_freq_diff)); 43 | } 44 | results 45 | } 46 | 47 | pub fn print_stats_analysis(text: &str) { 48 | let stats = stats_analysis(text); 49 | for (letter, count, freq, eng_freq, eng_freq_diff) in stats { 50 | println!( 51 | "{}: {} ({}%), English Freq: {} ({}%)", 52 | letter, 53 | count, 54 | freq, 55 | eng_freq.unwrap_or(0.0), 56 | eng_freq_diff 57 | ); 58 | } 59 | } 60 | 61 | pub fn decrypt(text: &str, shift: u8) -> String { 62 | let mut result = String::new(); 63 | 64 | for c in text.chars() { 65 | if c.is_ascii_alphabetic() { 66 | let base = if c.is_ascii_lowercase() { b'a' } else { b'A' }; 67 | let offset = (c as u8 - base + shift) % 26; 68 | result.push((base + offset) as char); 69 | } else { 70 | result.push(c); 71 | } 72 | } 73 | 74 | result 75 | } 76 | 77 | /* 78 | Guess Shift: 79 | 80 | First, uses statistical analysis to determine the most likely shift. 81 | Then, uses the most likely shift to decrypt the message. 82 | Accepts: 83 | * text: the message to decrypt 84 | * depth: the number of shifts to try 85 | Returns: 86 | * depth: the number of shifts to tried 87 | * shift: the most likely shift 88 | * decrypted: the decrypted message 89 | */ 90 | 91 | pub fn guess_shift(text: &str, depth: u8) -> (u8, u8, String, f32) { 92 | let mut max_score = 0.0; 93 | let mut best_shift = 0; 94 | let mut decrypted = String::new(); 95 | 96 | for shift in 0..depth { 97 | let decrypted_text = decrypt(text, shift); 98 | let stats = stats_analysis(&decrypted_text); 99 | 100 | let mut score = 0.0; 101 | for (_, _, freq, eng_freq, eng_freq_diff) in stats { 102 | if let Some(eng_freq) = eng_freq { 103 | score += (1.0 - eng_freq_diff / eng_freq) * freq; 104 | } 105 | } 106 | println!("Shift: {}, Score: {}", shift, score); 107 | if score > max_score { 108 | max_score = score; 109 | best_shift = shift; 110 | decrypted = decrypted_text; 111 | } 112 | } 113 | 114 | (depth, best_shift, decrypted, max_score) 115 | } 116 | -------------------------------------------------------------------------------- /decoder-ring/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Attempts to statistically decode a Caesar cipher. 3 | Here's an example of how to use it: 4 | 5 | This is a shift 16 message: "Off to the bunker. Every person for themselves" 6 | "Ypp dy dro lexuob. Ofobi zobcyx pyb drowcovfoc" 7 | 8 | cargo run -- --message "Ypp dy dro lexuob. Ofobi zobcyx pyb drowcovfoc" --guess 9 | 10 | Here is an example of it in action: 11 | 12 | Shift: 0, Score: 7.538945 13 | Shift: 1, Score: 10.078025 14 | Shift: 2, Score: 20.755177 15 | Shift: 3, Score: 11.284368 16 | Shift: 4, Score: 7.5232525 17 | Shift: 5, Score: 23.558884 18 | Shift: 6, Score: 21.086407 19 | Shift: 7, Score: 9.926911 20 | Shift: 8, Score: 5.5866623 21 | Shift: 9, Score: 15.310673 22 | Shift: 10, Score: 17.950832 23 | Shift: 11, Score: 21.200842 24 | Shift: 12, Score: 23.447578 25 | Shift: 13, Score: 14.035946 26 | Shift: 14, Score: 13.314641 27 | Shift: 15, Score: 2.055822 28 | Shift: 16, Score: 40.54977 29 | Shift: 17, Score: 15.98934 30 | Shift: 18, Score: 18.178614 31 | Shift: 19, Score: 8.523561 32 | Shift: 20, Score: 9.371011 33 | Shift: 21, Score: 12.385875 34 | Shift: 22, Score: 8.159725 35 | Shift: 23, Score: 10.439689 36 | Shift: 24, Score: 17.104122 37 | Shift: 25, Score: 14.300304 38 | Best shift: 16 (out of 26), score: 40.54977 39 | Decrypted message: Off to the bunker. Every person for themselves 40 | 41 | */ 42 | 43 | use clap::Parser; 44 | use decoder_ring::print_stats_analysis; 45 | 46 | /// CLI tool to reverse engineer a Caesar cipher 47 | #[derive(Parser, Debug)] 48 | #[command(author, version, about, long_about = None)] 49 | struct Args { 50 | /// The message to decrypt 51 | #[arg(short, long)] 52 | message: String, 53 | 54 | //statistical information about the message 55 | #[arg(short, long)] 56 | stats: bool, 57 | 58 | //guess the shift 59 | #[arg(short, long)] 60 | guess: bool, 61 | } 62 | 63 | // run it 64 | fn main() { 65 | let args = Args::parse(); 66 | //stats 67 | if args.stats { 68 | print_stats_analysis(&args.message); 69 | } 70 | //guess 71 | if args.guess { 72 | let (depth, best_shift, decrypted, max_score) = decoder_ring::guess_shift(&args.message, 26); 73 | println!( 74 | "Best shift: {} (out of {}), score: {}", 75 | best_shift, depth, max_score 76 | ); 77 | println!("Decrypted message: {}", decrypted); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /dining-philosopher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dining-philosopher" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | 11 | -------------------------------------------------------------------------------- /dining-philosopher/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /dining-philosopher/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The dining philosophers problem involves multiple threads needing 3 | * synchronized access to shared resources, risking deadlock. 4 | * 5 | * This code models philosophers as threads and forks as shared Mutex<()> 6 | * wrapped in Arc for thread-safe reference counting. 7 | * 8 | * To prevent deadlock from a "deadly embrace" of waiting for neighboring 9 | * forks, philosophers acquire lower numbered forks first. This breaks 10 | * symmetry and avoids circular waiting. 11 | * 12 | * The Mutexes provide exclusive fork access. The Arc allows sharing forks 13 | * between philosophers. 14 | * 15 | * The simulation prints start time, eating duration, and total time for 16 | * all philosophers. Total time approximately equals philosophers divided 17 | * by forks, as that number can eat concurrently. 18 | * 19 | * Key techniques: 20 | * - Used Mutex<()> to represent exclusive fork access 21 | * - Wrapped in Arc to share Mutexes between threads 22 | * - Numbered philosophers and acquire lower fork first 23 | * - Prints timing metrics for simulation 24 | */ 25 | 26 | use std::sync::{Arc, Mutex}; 27 | use std::thread; 28 | use std::time::{Duration, Instant}; 29 | 30 | struct Fork { 31 | id: u32, 32 | mutex: Mutex<()>, 33 | } 34 | 35 | struct Philosopher { 36 | id: u32, 37 | name: String, 38 | left_fork: Arc, 39 | right_fork: Arc, 40 | } 41 | 42 | impl Philosopher { 43 | fn new(id: u32, name: &str, left_fork: Arc, right_fork: Arc) -> Philosopher { 44 | Philosopher { 45 | id, 46 | name: name.to_string(), 47 | left_fork, 48 | right_fork, 49 | } 50 | } 51 | 52 | fn eat(&self) { 53 | let (first_fork, second_fork) = if self.id % 2 == 0 { 54 | (&self.left_fork, &self.right_fork) 55 | } else { 56 | (&self.right_fork, &self.left_fork) 57 | }; 58 | 59 | let _first_guard = first_fork.mutex.lock().unwrap(); 60 | println!("{} picked up fork {}.", self.name, first_fork.id); 61 | let _second_guard = second_fork.mutex.lock().unwrap(); 62 | println!("{} picked up fork {}.", self.name, second_fork.id); 63 | 64 | println!("{} is eating.", self.name); 65 | thread::sleep(Duration::from_secs(1)); 66 | println!("{} finished eating.", self.name); 67 | 68 | println!("{} put down fork {}.", self.name, first_fork.id); 69 | println!("{} put down fork {}.", self.name, second_fork.id); 70 | } 71 | } 72 | 73 | fn main() { 74 | println!("Dining Philosophers Problem: 15 Philosophers, 4 Forks...Yikes!!"); 75 | 76 | //we only have 4 forks at the table 77 | let forks = (0..4) 78 | .map(|id| { 79 | Arc::new(Fork { 80 | id, 81 | mutex: Mutex::new(()), 82 | }) 83 | }) 84 | .collect::>(); 85 | 86 | let philosophers = vec![ 87 | ("Jürgen Habermas", 0, 1), 88 | ("Friedrich Engels", 1, 2), 89 | ("Karl Marx", 2, 3), 90 | ("Thomas Piketty", 3, 0), 91 | ("Michel Foucault", 0, 1), 92 | ("Socrates", 1, 2), 93 | ("Plato", 2, 3), 94 | ("Aristotle", 3, 0), 95 | ("Pythagoras", 0, 1), 96 | ("Heraclitus", 1, 2), 97 | ("Democritus", 2, 3), 98 | ("Diogenes", 3, 0), 99 | ("Epicurus", 0, 1), 100 | ("Zeno of Citium", 1, 2), 101 | ("Thales of Miletus", 2, 3), 102 | ] 103 | .into_iter() 104 | .enumerate() 105 | .map(|(id, (name, left, right))| { 106 | Philosopher::new( 107 | id as u32, 108 | name, 109 | Arc::clone(&forks[left]), 110 | Arc::clone(&forks[right]), 111 | ) 112 | }) 113 | .collect::>(); 114 | 115 | let start = Instant::now(); 116 | 117 | let handles = philosophers 118 | .into_iter() 119 | .map(|philosopher| { 120 | thread::spawn(move || { 121 | philosopher.eat(); 122 | }) 123 | }) 124 | .collect::>(); 125 | 126 | for handle in handles { 127 | handle.join().unwrap(); 128 | } 129 | 130 | println!("Total time: {:?}", start.elapsed()); 131 | } 132 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ## Format all code directories in the repostitory using cargo fmt. 3 | 4 | for DIR in */; do 5 | DIRNAME=$(basename "$DIR") 6 | echo "==> $DIRNAME <==" 7 | (cd $DIR && cargo fmt -- ) 8 | done 9 | 10 | echo "Format complete." -------------------------------------------------------------------------------- /graph-centrality-ufc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "graph-centrality-ufc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | petgraph = "0.6.3" -------------------------------------------------------------------------------- /graph-centrality-ufc/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /graph-centrality-ufc/src/main.rs: -------------------------------------------------------------------------------- 1 | use petgraph::graph::{NodeIndex, UnGraph}; 2 | use petgraph::Direction; 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | struct Fighter { 7 | name: String, 8 | } 9 | /* 10 | This is a bit like the following Python code: 11 | 12 | class Fighter: 13 | def __init__(self, name): 14 | self.name = name 15 | */ 16 | impl Fighter { 17 | fn new(name: &str) -> Self { 18 | Self { 19 | name: name.to_string(), 20 | } 21 | } 22 | } 23 | 24 | impl fmt::Display for Fighter { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | write!(f, "{}", self.name) 27 | } 28 | } 29 | 30 | fn add_edge(graph: &mut UnGraph<&Fighter, f32>, nodes: &[NodeIndex], a: usize, b: usize) { 31 | graph.add_edge(nodes[a], nodes[b], 1.0); 32 | } 33 | 34 | fn main() { 35 | let mut graph = UnGraph::new_undirected(); 36 | 37 | let fighters = [ 38 | Fighter::new("Dustin Poirier"), 39 | Fighter::new("Khabib Nurmagomedov"), 40 | Fighter::new("Jose Aldo"), 41 | Fighter::new("Conor McGregor"), 42 | Fighter::new("Nate Diaz"), 43 | ]; 44 | 45 | let fighter_nodes: Vec = fighters 46 | .iter() 47 | .map(|fighter| graph.add_node(fighter)) 48 | .collect(); 49 | 50 | add_edge(&mut graph, &fighter_nodes, 0, 1); // Dustin Poirier vs. Khabib Nurmagomedov 51 | add_edge(&mut graph, &fighter_nodes, 1, 3); // Khabib Nurmagomedov vs. Conor McGregor 52 | add_edge(&mut graph, &fighter_nodes, 3, 0); // Conor McGregor vs. Dustin Poirier 53 | add_edge(&mut graph, &fighter_nodes, 3, 2); // Conor McGregor vs. Jose Aldo 54 | add_edge(&mut graph, &fighter_nodes, 3, 4); // Conor McGregor vs. Nate Diaz 55 | add_edge(&mut graph, &fighter_nodes, 0, 4); // Dustin Poirier vs. Nate Diaz 56 | add_edge(&mut graph, &fighter_nodes, 2, 4); // Jose Aldo vs. Nate Diaz 57 | 58 | for (i, &node) in fighter_nodes.iter().enumerate() { 59 | let name = &fighters[i].name; 60 | let degree = graph.edges_directed(node, Direction::Outgoing).count() as f32; 61 | let closeness = 1.0 / degree; 62 | println!("The closeness centrality of {} is {:.2}", name, closeness); 63 | 64 | // Explanation 65 | match name.as_str() { 66 | "Conor McGregor" => println!( 67 | "{} has the lowest centrality because he has fought with all other fighters in the network. In this context, a lower centrality value means a higher number of fights.", 68 | name 69 | ), 70 | "Dustin Poirier" | "Nate Diaz" => println!( 71 | "{} has a centrality of {:.2}, implying they had less fights compared to Conor McGregor but more than Khabib Nurmagomedov and Jose Aldo.", 72 | name, closeness 73 | ), 74 | "Khabib Nurmagomedov" | "Jose Aldo" => println!( 75 | "{} has the highest centrality of {:.2} as they have fought with the least number of fighters.", 76 | name, closeness 77 | ), 78 | _ => {} 79 | } 80 | println!("-----------------"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /graph-visualize/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "graph-visualize" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rasciigraph = "0.2.0" -------------------------------------------------------------------------------- /graph-visualize/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /graph-visualize/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rasciigraph; 2 | 3 | use rasciigraph::{plot, Config}; 4 | 5 | fn main() { 6 | let cities = vec!["Lisbon", "Madrid", "Paris", "Berlin", "Copenhagen", "Stockholm", "Moscow"]; 7 | let distances_travelled = vec![0.0, 502.56, 1053.36, 2187.27, 2636.42, 3117.23, 4606.35]; 8 | 9 | println!("{}", cities.join(" > ")); 10 | 11 | println!( 12 | "{}", 13 | plot( 14 | distances_travelled.into_iter().map(|d| d as f64).collect(), 15 | Config::default() 16 | .with_offset(10) 17 | .with_height(10) 18 | .with_caption("Travelled distances (km)".to_string()) 19 | ) 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /hashmap-count/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hashmap-count" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /hashmap-count/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /hashmap-count/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This example code counts the frequency of each number in the vector. 3 | */ 4 | use std::collections::HashMap; 5 | 6 | fn logic(numbers: Vec) -> Vec<(i32, u32)> { 7 | let mut frequencies = HashMap::new(); 8 | 9 | for num in numbers { 10 | let frequency = frequencies.entry(num).or_insert(0); 11 | *frequency += 1; 12 | } 13 | 14 | let mut result = Vec::new(); 15 | 16 | for (num, frequency) in frequencies { 17 | result.push((num, frequency)); 18 | } 19 | 20 | result 21 | } 22 | 23 | fn main() { 24 | let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3]; 25 | let result = logic(numbers); 26 | //print the results in a human readable format that explains what the result is. 27 | println!( 28 | "The frequency of each number in the vector is: {:?}", 29 | result 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /hashmap-language/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hashmap-language" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /hashmap-language/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /hashmap-language/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn init_languages() -> HashMap { 4 | let mut languages = HashMap::new(); 5 | languages.insert("JavaScript".to_string(), 1995); 6 | languages.insert("HTML/CSS".to_string(), 1990); 7 | languages.insert("Python".to_string(), 1991); 8 | languages.insert("SQL".to_string(), 1974); 9 | languages.insert("TypeScript".to_string(), 2012); 10 | languages.insert("Bash/Shell".to_string(), 1989); 11 | languages.insert("Java".to_string(), 1995); 12 | languages.insert("C#".to_string(), 2000); 13 | languages.insert("C++".to_string(), 1985); 14 | languages.insert("C".to_string(), 1972); 15 | languages.insert("PHP".to_string(), 1995); 16 | languages.insert("PowerShell".to_string(), 2006); 17 | languages.insert("Go".to_string(), 2007); 18 | languages.insert("Rust".to_string(), 2010); 19 | 20 | languages 21 | } 22 | 23 | fn calculate_weights(years_active: &mut HashMap) -> HashMap { 24 | // Subtract the creation year from 2024 to get the number of years active. 25 | for year in years_active.values_mut() { 26 | *year = 2024 - *year; 27 | } 28 | 29 | let min_year = *years_active.values().min().unwrap_or(&0); 30 | let max_year = *years_active.values().max().unwrap_or(&0); 31 | 32 | let mut weights = HashMap::new(); 33 | 34 | for (language, &year) in years_active.iter() { 35 | let normalized_year = (year - min_year) as f64 / (max_year - min_year) as f64; 36 | let weight = (normalized_year * 99.0) as i32 + 1; // weight between 1 and 100 37 | weights.insert(language.to_string(), weight); 38 | } 39 | 40 | weights 41 | } 42 | 43 | fn main() { 44 | let mut languages = init_languages(); 45 | let weights = calculate_weights(&mut languages); 46 | 47 | println!("Language weighing from 1-100 by age (1 is newest and 100 is oldest):"); 48 | for (language, weight) in &weights { 49 | println!("{}: {}", language, weight); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /hashset-fruit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hashset-fruit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /hashset-fruit/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /hashset-fruit/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use rand::thread_rng; 3 | use std::collections::HashSet; 4 | 5 | fn generate_fruit() -> &'static str { 6 | let fruits = [ 7 | "Apple", 8 | "Banana", 9 | "Cherry", 10 | "Date", 11 | "Elderberry", 12 | "Fig", 13 | "Grape", 14 | "Honeydew", 15 | ]; 16 | let mut rng = thread_rng(); 17 | fruits.choose(&mut rng).unwrap() 18 | } 19 | 20 | fn main() { 21 | let mut fruit_set = HashSet::new(); 22 | println!("Generating 100 random fruits..."); 23 | for _ in 0..100 { 24 | fruit_set.insert(generate_fruit()); 25 | } 26 | 27 | println!("Number of unique fruits generated: {}", fruit_set.len()); 28 | } 29 | -------------------------------------------------------------------------------- /homophonic-cipher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "homophonic-cipher" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /homophonic-cipher/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /homophonic-cipher/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Generates a list of random homophones for each lowercase letter in the 3 | * English alphabet. Maps each character in the plaintext to one of its 4 | * random homophones to create the cipher text. Prints the plaintext, 5 | * cipher text, and homophonic mapping. Returns the cipher text and 6 | * homophonic mapping. 7 | * 8 | * 9 | * Here is an example: 10 | * Plaintext: the quick brown fox jumps over the lazy dog 11 | * Ciphertext: acrsalgzuwxsgpeqqrjrnekrwvnnwdgfuqn 12 | * Mapping: { 13 | * 't': ['q', 'a', 'v'], 'y': ['f', 's'], 'q': ['s', 'u'], 14 | * 'l': ['w', 'z', 'o'], 's': ['i', 'w', 'n'], 'b': ['u', 'f'], 15 | * 'h': ['n', 'n', 'c'], 'k': ['z', 'r'], 'j': ['s', 'w', 'q'], 16 | * 'x': ['g', 'g', 'q'], 'i': ['l', 'k'], 'g': ['n', 'g'], 17 | * 'm': ['s', 'j', 'w'], 'p': ['k', 'r'], 'a': ['d', 'm', 'w'], 18 | * 'r': ['w', 'o', 'o'], 'o': ['q', 'x', 'e'], 'e': ['n', 'r'], 19 | * 'f': ['i', 'p', 'e'], 'c': ['g', 'z'], 'u': ['a', 'd', 'r'], 20 | * 'v': ['h', 'f', 'k'], 'd': ['s', 'r', 'u'], 'n': ['d', 'g', 'l'], 21 | * 'w': ['s', 'c'], 'z': ['g', 'b'] 22 | * } 23 | * The mapping {'t': ['q', 'a', 'v'], ...} is a part of the homophonic 24 | * cipher mapping from plaintext characters to their cipher characters. 25 | * 26 | 27 | * In this specific example, the plaintext character 't' can be represented 28 | * in the ciphertext by either 'q', 'a', or 'v'. This introduces ambiguity 29 | * into the encryption, which makes the homophonic cipher harder to break 30 | * compared to simple substitution ciphers. 31 | * 32 | * The homophonic cipher is more secure than a simple substitution cipher, 33 | * it is still not secure for serious cryptographic uses. 34 | * 35 | * Given the ciphertext and the mapping, you can reverse-engineer the 36 | * plaintext. Let's start with the first three characters of the 37 | * ciphertext: 'a', 'c', and 'r'. 38 | * 39 | * 'a': Looking at the mapping, you can see that 'a' can be a cipher for 40 | * 'u' or 't', as 'u' and 't' have 'a' in their list of homophones. So 41 | * the possible plaintext letters for 'a' are 'u' and 't'. 42 | * 43 | * 'c': Looking at the mapping again, 'c' can be a cipher for 'h' or 'w' 44 | * since 'h' and 'w' have 'c' in their list of homophones. So the 45 | * possible plaintext letters for 'c' are 'h' and 'w'. 46 | * 47 | * 'r': 'r' can be a cipher for 'e', 'o', or 't' since 'e', 'o', and 't' 48 | * have 'r' in their list of homophones. So the possible plaintext 49 | * letters for 'r' are 'e', 'o', and 't'. 50 | * 51 | * So the first three characters of the plaintext could be any combination 52 | * of the possible plaintext letters for 'a', 'c', and 'r'. For example, 53 | * it could be 'u', 'h', 'e', or 't', 'w', 'o', etc. 54 | * 55 | * Remember, homophonic ciphers are designed to provide many possible 56 | * plaintexts for a single ciphertext, which makes it much harder to crack 57 | * the code without having more information. One possible approach to 58 | * decode the message is using a frequency analysis or a known-plaintext 59 | * attack if you have a part of the original message. Another way is to 60 | * use the context of the message if it's known. 61 | */ 62 | 63 | use rand::Rng; 64 | use std::collections::HashMap; 65 | 66 | fn homophonic_cipher(plaintext: &str) -> (String, HashMap>) { 67 | let mut rng = rand::thread_rng(); 68 | let alphabet: Vec = ('a'..='z').collect(); 69 | let mut ciphertext = String::new(); 70 | let mut mapping: HashMap> = HashMap::new(); 71 | 72 | for c in &alphabet { 73 | let homophones: Vec = (0..rng.gen_range(2..4)) 74 | .map(|_| rng.gen_range('a'..='z')) 75 | .collect(); 76 | mapping.insert(*c, homophones); 77 | } 78 | 79 | for c in plaintext.chars() { 80 | if let Some(c) = c.to_lowercase().next() { 81 | if let Some(homophones) = mapping.get(&c) { 82 | if let Some(&homophone) = homophones.get(rng.gen_range(0..homophones.len())) { 83 | ciphertext.push(homophone); 84 | } else { 85 | eprintln!("Error: No homophones for character {}", c); 86 | } 87 | } 88 | } else { 89 | ciphertext.push(c); 90 | } 91 | } 92 | 93 | println!("Plaintext: {}", plaintext); 94 | println!("Ciphertext: {}", ciphertext); 95 | println!("Mapping: {:?}", mapping); 96 | 97 | (ciphertext, mapping) 98 | } 99 | 100 | fn main() { 101 | let plaintext = "the quick brown fox jumps over the lazy dog"; 102 | homophonic_cipher(plaintext); 103 | } 104 | -------------------------------------------------------------------------------- /immutable-testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "immutable-testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /immutable-testing/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /immutable-testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* Calculator that uses an immutable vector 2 | Lets make our vector a constant so that we can use it in our tests 3 | */ 4 | 5 | // a vector of immutable integers 6 | const V: [i32; 3] = [1, 2, 3]; 7 | 8 | // add all the elements in the vector 9 | pub fn add() -> i32 { 10 | let mut sum = 0; 11 | for i in &V { 12 | sum += i; 13 | } 14 | sum 15 | } 16 | 17 | /*These are the tests */ 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | #[test] 22 | fn test_add() { 23 | assert_eq!(add(), 6); 24 | } 25 | } -------------------------------------------------------------------------------- /immutable-testing/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Uses lib.rs to add all the elements in the vector 3 | */ 4 | use immutable_testing::add; 5 | 6 | fn main() { 7 | //print the result 8 | println!("The sum of the elements in the vector is: {}", add()); 9 | } 10 | -------------------------------------------------------------------------------- /immutable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "immutable" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /immutable/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /immutable/immutable-py.py: -------------------------------------------------------------------------------- 1 | def add_one_to_list(lst): 2 | for i in range(len(lst)): 3 | lst[i] += 1 4 | 5 | lst = [1, 2, 3] 6 | add_one_to_list(lst) 7 | print(lst) 8 | 9 | # This will modify the list outside of the function 10 | lst[0] += 1 11 | print(lst) -------------------------------------------------------------------------------- /immutable/src/main.rs: -------------------------------------------------------------------------------- 1 | /* An example of an immutable list */ 2 | 3 | //This will generate a compliation error because mut is not needed 4 | fn main(){ 5 | // a vector of immutable integers 6 | let mut v = vec![1, 2, 3]; 7 | //print all the elements in the vector 8 | for i in &v { 9 | println!("{}", i); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /linked-list-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linked-list-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /linked-list-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /linked-list-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | As with the VecDeque example, this code starts by creating a LinkedList of fruits, 3 | converts it to a Vec for shuffling, and then converts it back to a LinkedList. 4 | After the shuffling, it adds "Pomegranate", "Fig", and "Cherry" to the end of the list. 5 | Finally, it prints out the final fruit salad. 6 | 7 | This example shows how to use a LinkedList, but remember that LinkedList 8 | has a higher memory overhead and worse cache locality than Vec or VecDeque, 9 | so it's typically not the best choice unless you have a specific need for the properties 10 | of a linked list. In Rust, it's usually better to use a Vec or VecDeque. 11 | 12 | A LinkedList is a doubly-linked list, which means that each element in the list 13 | has a pointer to the next element and the previous element. 14 | A great example of when to use a LinkedList is when you need to insert or remove elements 15 | from the middle of the list. 16 | */ 17 | 18 | use rand::seq::SliceRandom; // rand is a random number generation library in Rust 19 | use rand::thread_rng; 20 | use std::collections::LinkedList; 21 | 22 | fn main() { 23 | let mut fruit: LinkedList<&str> = LinkedList::new(); 24 | fruit.push_back("Arbutus"); 25 | fruit.push_back("Loquat"); 26 | fruit.push_back("Strawberry Tree Berry"); 27 | 28 | /* 29 | Please note that converting a LinkedList to a Vec and back to a LinkedList 30 | isn't a common operation in practice. I included 31 | it in this example to keep the code as similar as possible 32 | to the original VecDeque example. 33 | */ 34 | 35 | // Scramble (shuffle) the fruit 36 | let mut rng = thread_rng(); 37 | let mut fruit: Vec<_> = fruit.into_iter().collect(); 38 | fruit.shuffle(&mut rng); 39 | 40 | // Convert it back to LinkedList 41 | let mut fruit: LinkedList<_> = fruit.into_iter().collect(); 42 | 43 | // Add fruits to the both ends of the list after shuffling 44 | fruit.push_front("Pomegranate"); 45 | fruit.push_back("Fig"); 46 | fruit.push_back("Cherry"); 47 | 48 | // Print out the fruit salad 49 | println!("Fruit Salad:"); 50 | for (i, item) in fruit.iter().enumerate() { 51 | if i != fruit.len() - 1 { 52 | print!("{}, ", item); 53 | } else { 54 | println!("{}", item); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ## Format all code directories in the repostitory using cargo fmt. 3 | 4 | for DIR in */; do 5 | DIRNAME=$(basename "$DIR") 6 | echo "==> $DIRNAME <==" 7 | (cd $DIR && cargo clippy --all-targets --all-features -- -D warnings) 8 | done 9 | 10 | echo "Format complete." -------------------------------------------------------------------------------- /lisbon-shortest-path/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lisbon-shortest-path" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | petgraph = "0.6.3" -------------------------------------------------------------------------------- /lisbon-shortest-path/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /lisbon-shortest-path/src/main.rs: -------------------------------------------------------------------------------- 1 | use petgraph::algo::dijkstra; 2 | use petgraph::prelude::*; 3 | 4 | fn main() { 5 | let mut graph = Graph::<&str, u32, Undirected>::new_undirected(); 6 | 7 | let belem_tower = graph.add_node("Belem Tower"); 8 | let monastery = graph.add_node("Jerónimos Monastery"); 9 | let lx_factory = graph.add_node("LX Factory"); 10 | let commerce_square = graph.add_node("Commerce Square"); 11 | let lisbon_cathedral = graph.add_node("Lisbon Cathedral"); 12 | 13 | graph.extend_with_edges([ 14 | (belem_tower, monastery, 1), // The distance from Belem Tower to Jerónimos Monastery is 1 km 15 | (belem_tower, lx_factory, 3), // The distance from Belem Tower to LX Factory is 3 km 16 | (belem_tower, commerce_square, 7), // The distance from Belem Tower to Commerce Square is 7 km 17 | (monastery, lx_factory, 3), // The distance from Jerónimos Monastery to LX Factory is 3 km 18 | (monastery, commerce_square, 6), // The distance from Jerónimos Monastery to Commerce Square is 6 km 19 | (lx_factory, commerce_square, 5), // The distance from LX Factory to Commerce Square is 5 km 20 | (commerce_square, lisbon_cathedral, 1), // The distance from Commerce Square to Lisbon Cathedral is 1 km 21 | ]); 22 | 23 | let node_map = dijkstra(&graph, belem_tower, Some(lisbon_cathedral), |e| *e.weight()); 24 | 25 | if let Some(distance) = node_map.get(&lisbon_cathedral) { 26 | println!( 27 | "The shortest distance from Belem Tower to Lisbon Cathedral is {} km", 28 | distance 29 | ); 30 | } else { 31 | println!("No route found from Belem Tower to Lisbon Cathedral."); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lowmem-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lowmem-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | 11 | [lib] 12 | name = "fruit_salad_maker" 13 | path = "src/lib.rs" -------------------------------------------------------------------------------- /lowmem-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /lowmem-fruit-salad/fruits.csv: -------------------------------------------------------------------------------- 1 | maçã, banana, laranja, morango, abacaxi, manga, pêssego, limão, melancia, uva -------------------------------------------------------------------------------- /lowmem-fruit-salad/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use rand::thread_rng; 3 | 4 | pub fn create_fruit_salad(mut fruits: Vec) -> Vec { 5 | let mut rng = thread_rng(); 6 | fruits.shuffle(&mut rng); 7 | 8 | fruits 9 | } 10 | -------------------------------------------------------------------------------- /lowmem-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, BufRead}; 3 | use std::path::Path; 4 | use fruit_salad_maker::create_fruit_salad; 5 | 6 | fn read_fruits_from_file(path: &Path) -> io::Result> { 7 | let file = File::open(path)?; 8 | let reader = io::BufReader::new(file); 9 | let mut fruits = Vec::new(); 10 | 11 | for line in reader.lines() { 12 | let line = line?; 13 | fruits.extend(line.split(',').map(|s| s.trim().to_string())); 14 | } 15 | Ok(fruits) 16 | } 17 | 18 | fn main() -> io::Result<()> { 19 | loop { 20 | // This reads the fruits from the file in each iteration of the loop. 21 | // In other languages, if not properly managed, this could cause a memory leak. 22 | // But in Rust, the memory used for the list of fruits is automatically deallocated at the end of each loop iteration. 23 | let fruits = read_fruits_from_file(Path::new("fruits.csv"))?; 24 | 25 | let fruit_salad = create_fruit_salad(fruits); 26 | 27 | println!( 28 | "Created Fruit salad with {} fruits: {:?}", 29 | fruit_salad.len(), 30 | fruit_salad 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mutable-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mutable-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /mutable-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /mutable-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Create a vector of fruits. 3 | let fruit_salad = vec!["apple", "banana", "cherry", "dates", "elderberries"]; 4 | println!("Original fruit salad: {:?}", fruit_salad); 5 | 6 | // Uncommenting the following line will cause a compilation error because fruit_salad 7 | // is immutable. 8 | // fruit_salad.push("figs"); 9 | 10 | // To mutate the vector, we need to declare it as mutable: 11 | let mut fruit_salad = vec!["apple", "banana", "cherry", "dates", "elderberries"]; 12 | fruit_salad.push("figs"); 13 | println!("Modified fruit salad: {:?}", fruit_salad); 14 | } 15 | -------------------------------------------------------------------------------- /pagerank/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pagerank" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | textwrap = "0.16.0" -------------------------------------------------------------------------------- /pagerank/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /pagerank/src/main.rs: -------------------------------------------------------------------------------- 1 | // Importing the fill function from the textwrap crate to wrap text at 78 characters per line. 2 | use textwrap::fill; 3 | 4 | // The PageRank struct holds the damping factor and the number of iterations to run the algorithm. 5 | struct PageRank { 6 | damping: f64, 7 | iterations: usize, 8 | } 9 | 10 | impl PageRank { 11 | // The new function creates a new instance of the PageRank struct. 12 | fn new(damping: f64, iterations: usize) -> Self { 13 | Self { damping, iterations } 14 | } 15 | 16 | // The rank function calculates and returns the PageRank for each node in the graph. 17 | fn rank(&self, graph: &Vec>) -> Vec { 18 | // The number of nodes in the graph. 19 | let n = graph.len(); 20 | 21 | // The initial PageRank value for each node. 22 | let mut ranks = vec![1.0 / (n as f64); n]; 23 | 24 | // Iterates the specified number of times. 25 | for _ in 0..self.iterations { 26 | // A new vector to hold the updated PageRank values. 27 | let mut new_ranks = vec![0.0; n]; 28 | 29 | // Iterates over each node and its edges in the graph. 30 | for (node, edges) in graph.iter().enumerate() { 31 | // The amount of PageRank value this node contributes to its linked nodes. 32 | let contribution = ranks[node] / (edges.len() as f64); 33 | 34 | // Distributes the PageRank value to the linked nodes. 35 | for &edge in edges { 36 | new_ranks[edge] += contribution; 37 | } 38 | } 39 | 40 | // Updates the PageRank values using the damping factor. 41 | for rank in &mut new_ranks { 42 | *rank = *rank * self.damping + (1.0 - self.damping) / (n as f64); 43 | } 44 | 45 | // Replaces the old PageRank values with the new ones. 46 | ranks = new_ranks; 47 | } 48 | 49 | // Returns the final PageRank values. 50 | ranks 51 | } 52 | } 53 | 54 | fn main() { 55 | // The graph represents links between sports websites. Each index represents a website, 56 | // and the values in the vectors are the indexes of the websites they link to. 57 | let graph = vec![ 58 | vec![1, 2], // ESPN links to NFL, NBA 59 | vec![0], // NFL links to ESPN 60 | vec![0, 3], // NBA links to ESPN, UFC 61 | vec![0], // UFC links to ESPN 62 | vec![0, 1], // MLB links to ESPN, NFL 63 | ]; 64 | 65 | // The names corresponding to the indexes of the websites. 66 | let names = vec!["ESPN", "NFL", "NBA", "UFC", "MLB"]; 67 | 68 | // Initializes the PageRank struct. 69 | let pagerank = PageRank::new(0.85, 100); 70 | 71 | // Calculates the PageRank values. 72 | let ranks = pagerank.rank(&graph); 73 | 74 | // Prints the PageRank values. 75 | for (i, rank) in ranks.iter().enumerate() { 76 | println!("The PageRank of {} is {}", names[i], rank); 77 | } 78 | 79 | // Explanation of how PageRank works. 80 | let explanation = "PageRank is a link analysis algorithm used by Google that uses the hyperlink structure of the web to determine a quality ranking for each web page. It works by counting the number and quality of links to a page to determine a rough estimate of how important the website is."; 81 | 82 | // Prints the explanation wrapped at 78 characters per line. 83 | println!("{}", fill(explanation, 78)); 84 | } 85 | -------------------------------------------------------------------------------- /polars-hello-world-code-whisperer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polars-hello-world-code-whisperer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | polars = { version = "0.33.2", features = ["lazy"] } 10 | -------------------------------------------------------------------------------- /polars-hello-world-code-whisperer/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /polars-hello-world-code-whisperer/data/iris.csv: -------------------------------------------------------------------------------- 1 | sepal_length,sepal_width,petal_length,petal_width,species 2 | 5.1,3.5,1.4,0.2,setosa 3 | 4.9,3.0,1.4,0.2,setosa 4 | 4.7,3.2,1.3,0.2,setosa 5 | 4.6,3.1,1.5,0.2,setosa 6 | 5.0,3.6,1.4,0.2,setosa 7 | 5.4,3.9,1.7,0.4,setosa 8 | 4.6,3.4,1.4,0.3,setosa 9 | 5.0,3.4,1.5,0.2,setosa 10 | 4.4,2.9,1.4,0.2,setosa 11 | 4.9,3.1,1.5,0.1,setosa 12 | 5.4,3.7,1.5,0.2,setosa 13 | 4.8,3.4,1.6,0.2,setosa 14 | 4.8,3.0,1.4,0.1,setosa 15 | 4.3,3.0,1.1,0.1,setosa 16 | 5.8,4.0,1.2,0.2,setosa 17 | 5.7,4.4,1.5,0.4,setosa 18 | 5.4,3.9,1.3,0.4,setosa 19 | 5.1,3.5,1.4,0.3,setosa 20 | 5.7,3.8,1.7,0.3,setosa 21 | 5.1,3.8,1.5,0.3,setosa 22 | 5.4,3.4,1.7,0.2,setosa 23 | 5.1,3.7,1.5,0.4,setosa 24 | 4.6,3.6,1.0,0.2,setosa 25 | 5.1,3.3,1.7,0.5,setosa 26 | 4.8,3.4,1.9,0.2,setosa 27 | 5.0,3.0,1.6,0.2,setosa 28 | 5.0,3.4,1.6,0.4,setosa 29 | 5.2,3.5,1.5,0.2,setosa 30 | 5.2,3.4,1.4,0.2,setosa 31 | 4.7,3.2,1.6,0.2,setosa 32 | 4.8,3.1,1.6,0.2,setosa 33 | 5.4,3.4,1.5,0.4,setosa 34 | 5.2,4.1,1.5,0.1,setosa 35 | 5.5,4.2,1.4,0.2,setosa 36 | 4.9,3.1,1.5,0.1,setosa 37 | 5.0,3.2,1.2,0.2,setosa 38 | 5.5,3.5,1.3,0.2,setosa 39 | 4.9,3.1,1.5,0.1,setosa 40 | 4.4,3.0,1.3,0.2,setosa 41 | 5.1,3.4,1.5,0.2,setosa 42 | 5.0,3.5,1.3,0.3,setosa 43 | 4.5,2.3,1.3,0.3,setosa 44 | 4.4,3.2,1.3,0.2,setosa 45 | 5.0,3.5,1.6,0.6,setosa 46 | 5.1,3.8,1.9,0.4,setosa 47 | 4.8,3.0,1.4,0.3,setosa 48 | 5.1,3.8,1.6,0.2,setosa 49 | 4.6,3.2,1.4,0.2,setosa 50 | 5.3,3.7,1.5,0.2,setosa 51 | 5.0,3.3,1.4,0.2,setosa 52 | 7.0,3.2,4.7,1.4,versicolor 53 | 6.4,3.2,4.5,1.5,versicolor 54 | 6.9,3.1,4.9,1.5,versicolor 55 | 5.5,2.3,4.0,1.3,versicolor 56 | 6.5,2.8,4.6,1.5,versicolor 57 | 5.7,2.8,4.5,1.3,versicolor 58 | 6.3,3.3,4.7,1.6,versicolor 59 | 4.9,2.4,3.3,1.0,versicolor 60 | 6.6,2.9,4.6,1.3,versicolor 61 | 5.2,2.7,3.9,1.4,versicolor 62 | 5.0,2.0,3.5,1.0,versicolor 63 | 5.9,3.0,4.2,1.5,versicolor 64 | 6.0,2.2,4.0,1.0,versicolor 65 | 6.1,2.9,4.7,1.4,versicolor 66 | 5.6,2.9,3.6,1.3,versicolor 67 | 6.7,3.1,4.4,1.4,versicolor 68 | 5.6,3.0,4.5,1.5,versicolor 69 | 5.8,2.7,4.1,1.0,versicolor 70 | 6.2,2.2,4.5,1.5,versicolor 71 | 5.6,2.5,3.9,1.1,versicolor 72 | 5.9,3.2,4.8,1.8,versicolor 73 | 6.1,2.8,4.0,1.3,versicolor 74 | 6.3,2.5,4.9,1.5,versicolor 75 | 6.1,2.8,4.7,1.2,versicolor 76 | 6.4,2.9,4.3,1.3,versicolor 77 | 6.6,3.0,4.4,1.4,versicolor 78 | 6.8,2.8,4.8,1.4,versicolor 79 | 6.7,3.0,5.0,1.7,versicolor 80 | 6.0,2.9,4.5,1.5,versicolor 81 | 5.7,2.6,3.5,1.0,versicolor 82 | 5.5,2.4,3.8,1.1,versicolor 83 | 5.5,2.4,3.7,1.0,versicolor 84 | 5.8,2.7,3.9,1.2,versicolor 85 | 6.0,2.7,5.1,1.6,versicolor 86 | 5.4,3.0,4.5,1.5,versicolor 87 | 6.0,3.4,4.5,1.6,versicolor 88 | 6.7,3.1,4.7,1.5,versicolor 89 | 6.3,2.3,4.4,1.3,versicolor 90 | 5.6,3.0,4.1,1.3,versicolor 91 | 5.5,2.5,4.0,1.3,versicolor 92 | 5.5,2.6,4.4,1.2,versicolor 93 | 6.1,3.0,4.6,1.4,versicolor 94 | 5.8,2.6,4.0,1.2,versicolor 95 | 5.0,2.3,3.3,1.0,versicolor 96 | 5.6,2.7,4.2,1.3,versicolor 97 | 5.7,3.0,4.2,1.2,versicolor 98 | 5.7,2.9,4.2,1.3,versicolor 99 | 6.2,2.9,4.3,1.3,versicolor 100 | 5.1,2.5,3.0,1.1,versicolor 101 | 5.7,2.8,4.1,1.3,versicolor 102 | 6.3,3.3,6.0,2.5,virginica 103 | 5.8,2.7,5.1,1.9,virginica 104 | 7.1,3.0,5.9,2.1,virginica 105 | 6.3,2.9,5.6,1.8,virginica 106 | 6.5,3.0,5.8,2.2,virginica 107 | 7.6,3.0,6.6,2.1,virginica 108 | 4.9,2.5,4.5,1.7,virginica 109 | 7.3,2.9,6.3,1.8,virginica 110 | 6.7,2.5,5.8,1.8,virginica 111 | 7.2,3.6,6.1,2.5,virginica 112 | 6.5,3.2,5.1,2.0,virginica 113 | 6.4,2.7,5.3,1.9,virginica 114 | 6.8,3.0,5.5,2.1,virginica 115 | 5.7,2.5,5.0,2.0,virginica 116 | 5.8,2.8,5.1,2.4,virginica 117 | 6.4,3.2,5.3,2.3,virginica 118 | 6.5,3.0,5.5,1.8,virginica 119 | 7.7,3.8,6.7,2.2,virginica 120 | 7.7,2.6,6.9,2.3,virginica 121 | 6.0,2.2,5.0,1.5,virginica 122 | 6.9,3.2,5.7,2.3,virginica 123 | 5.6,2.8,4.9,2.0,virginica 124 | 7.7,2.8,6.7,2.0,virginica 125 | 6.3,2.7,4.9,1.8,virginica 126 | 6.7,3.3,5.7,2.1,virginica 127 | 7.2,3.2,6.0,1.8,virginica 128 | 6.2,2.8,4.8,1.8,virginica 129 | 6.1,3.0,4.9,1.8,virginica 130 | 6.4,2.8,5.6,2.1,virginica 131 | 7.2,3.0,5.8,1.6,virginica 132 | 7.4,2.8,6.1,1.9,virginica 133 | 7.9,3.8,6.4,2.0,virginica 134 | 6.4,2.8,5.6,2.2,virginica 135 | 6.3,2.8,5.1,1.5,virginica 136 | 6.1,2.6,5.6,1.4,virginica 137 | 7.7,3.0,6.1,2.3,virginica 138 | 6.3,3.4,5.6,2.4,virginica 139 | 6.4,3.1,5.5,1.8,virginica 140 | 6.0,3.0,4.8,1.8,virginica 141 | 6.9,3.1,5.4,2.1,virginica 142 | 6.7,3.1,5.6,2.4,virginica 143 | 6.9,3.1,5.1,2.3,virginica 144 | 5.8,2.7,5.1,1.9,virginica 145 | 6.8,3.2,5.9,2.3,virginica 146 | 6.7,3.3,5.7,2.5,virginica 147 | 6.7,3.0,5.2,2.3,virginica 148 | 6.3,2.5,5.0,1.9,virginica 149 | 6.5,3.0,5.2,2.0,virginica 150 | 6.2,3.4,5.4,2.3,virginica 151 | 5.9,3.0,5.1,1.8,virginica 152 | -------------------------------------------------------------------------------- /polars-hello-world-code-whisperer/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Polars hello world script that uses AWS Code Catalyst and Code Whisperer 3 | */ 4 | use polars::prelude::*; 5 | 6 | pub fn calculate() -> Result { 7 | // Read the CSV data using CsvReader 8 | let df = CsvReader::new("data/iris.csv") 9 | .has_header(true) 10 | .finish()? 11 | .lazy() 12 | .collect()?; 13 | Ok(df) 14 | } 15 | 16 | fn main() { 17 | let df = calculate().unwrap(); 18 | println!("{}", df); 19 | } -------------------------------------------------------------------------------- /print-data-structs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "print-data-structs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /print-data-structs/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /print-data-structs/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Common Rust Collections:"); 3 | 4 | // Sequences 5 | println!("\n\tSequences:"); 6 | println!("\t\tVec: https://doc.rust-lang.org/std/vec/struct.Vec.html"); 7 | println!("\t\tVecDeque: https://doc.rust-lang.org/std/collections/struct.VecDeque.html"); 8 | println!("\t\tLinkedList: https://doc.rust-lang.org/std/collections/struct.LinkedList.html"); 9 | 10 | // Maps 11 | println!("\n\tMaps:"); 12 | println!("\t\tHashMap: https://doc.rust-lang.org/std/collections/struct.HashMap.html"); 13 | println!("\t\tBTreeMap: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html"); 14 | 15 | // Sets 16 | println!("\n\tSets:"); 17 | println!("\t\tHashSet: https://doc.rust-lang.org/std/collections/struct.HashSet.html"); 18 | println!("\t\tBTreeSet: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html"); 19 | 20 | // Misc 21 | println!("\n\tMisc:"); 22 | println!("\t\tBinaryHeap: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html"); 23 | } 24 | -------------------------------------------------------------------------------- /sha3-dupe-detector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sha3-dupe-detector" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sha3 = "0.10.8" 10 | rand = "0.8.4" 11 | hex = "0.4.3" -------------------------------------------------------------------------------- /sha3-dupe-detector/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /sha3-dupe-detector/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::prelude::SliceRandom; 2 | use rand::thread_rng; 3 | use rand::Rng; 4 | use sha3::Digest; 5 | use sha3::Sha3_256; 6 | use std::collections::HashMap; 7 | 8 | // List of phrases 9 | static PHRASES: [&str; 10] = [ 10 | "man can be destroyed but not defeated", 11 | "but man is not made for defeat", 12 | "a man can be destroyed but not defeated", 13 | "the old man was thin and gaunt", 14 | "everything about him was old", 15 | "the sail was patched with flour sacks", 16 | "he was an old man who fished alone", 17 | "the old man had taught the boy to fish", 18 | "the old man looked at him with his sun burned confident loving eyes", 19 | "his eyes were cheerful and undefeated", 20 | ]; 21 | 22 | // Generate random phrases 23 | pub fn generate_random_phrases() -> Vec<&'static str> { 24 | let mut rng = thread_rng(); 25 | let mut phrases = Vec::new(); 26 | 27 | for &phrase in PHRASES.iter() { 28 | let copies = rng.gen_range(1..=3); 29 | 30 | for _ in 0..copies { 31 | phrases.push(phrase); 32 | } 33 | } 34 | 35 | phrases.shuffle(&mut rng); 36 | 37 | phrases 38 | } 39 | 40 | // Analyze duplicates 41 | pub fn analyze_duplicates(phrases: &[&str]) { 42 | let mut hashes: HashMap<_, (usize, &str)> = HashMap::new(); 43 | println!("Total number of phrases: {}", phrases.len()); 44 | 45 | for phrase in phrases { 46 | let hash = Sha3_256::digest(phrase.as_bytes()); 47 | let entry = hashes.entry(hash).or_insert((0, phrase)); 48 | entry.0 += 1; 49 | } 50 | 51 | let total_unique_phrases = hashes.len(); 52 | 53 | let mut total_unique_duplicates = 0; 54 | let mut total_combined_duplicates = 0; 55 | 56 | for (hash, (count, phrase)) in &hashes { 57 | if *count > 1 { 58 | total_unique_duplicates += 1; 59 | total_combined_duplicates += count - 1; // subtract one to exclude the original 60 | println!("{} - {} times: {}", hex::encode(hash), count, phrase); 61 | } 62 | } 63 | 64 | println!("Total Unique Phrases: {}", total_unique_phrases); 65 | println!("Total Unique Duplicates: {}", total_unique_duplicates); 66 | println!("Total Combined Duplicates: {}", total_combined_duplicates); 67 | } 68 | -------------------------------------------------------------------------------- /sha3-dupe-detector/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Generates random duplicate phrases from a list of phrases 3 | and prints the number of unique phrases and the number of duplicate phrases. 4 | 5 | Example output: 6 | 7 | Total number of phrases: 24 8 | 131a931202f9f1e7821ece767d0a9041aeb0270a40def3583de149c849683cb2 - 3 times: the old man looked at him with his sun burned confident loving eyes 9 | ad604cf092d30c844c8f1820de47771efef7a66763468cd2a68bbed8637579d2 - 3 times: his eyes were cheerful and undefeated 10 | 796599a1f14554fde9514bf41dca747548570d3a7a38a74cc19caa07ae55ca70 - 2 times: a man can be destroyed but not defeated 11 | 6931bbb80ba33090d9e7015551e7e21676a4813d50238b1e75c9a9ce38845b1e - 2 times: but man is not made for defeat 12 | 864fd2e3cac4fa99278594867c3accc16aacfb3f543e0ed53bf71cd8d23273e1 - 3 times: man can be destroyed but not defeated 13 | 0a9247da949c7ff1ff5b64bf3482a46cb90b87de9cade88ca26ff4bd101b1017 - 3 times: everything about him was old 14 | 7f7f417bb4ff8b62b19edc25c3e359c6a32f7d2f06883ce40062eae093a23ad1 - 3 times: the old man had taught the boy to fish 15 | c33fc7b48db132dfdc5fc6aac5514ad867fc35b21c06e55725720057034c6a56 - 2 times: he was an old man who fished alone 16 | d40a2c3a380fbb5f5b15db16a9acb87b14c91906aac595e6f920827ee6187ef6 - 2 times: the sail was patched with flour sacks 17 | Total Unique Phrases: 10 18 | Total Unique Duplicates: 9 19 | Total Combined Duplicates: 14 20 | 21 | */ 22 | use sha3_dupe_detector::generate_random_phrases; 23 | 24 | fn main() { 25 | let phrases = generate_random_phrases(); 26 | sha3_dupe_detector::analyze_duplicates(&phrases); 27 | } 28 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ## Format all code directories in the repostitory using cargo fmt. 3 | 4 | for DIR in */; do 5 | DIRNAME=$(basename "$DIR") 6 | echo "==> $DIRNAME <==" 7 | (cd $DIR && cargo test) 8 | done 9 | 10 | echo "Format complete." -------------------------------------------------------------------------------- /vecdeque-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vecdeque-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /vecdeque-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /vecdeque-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This code starts with an initial VecDeque, 3 | converts it to a Vec for shuffling, and then converts it back to a VecDeque. 4 | After that, it pushes "Pomegranate" to the front of the deque, and "Fig" and "Cherry" 5 | to the back of the deque. Finally, it prints out the final fruit salad. 6 | 7 | A VecDeque is a double-ended queue, which means that you can push and pop from both ends 8 | of the queue. 9 | */ 10 | 11 | use rand::seq::SliceRandom; // rand is a random number generation library in Rust 12 | use rand::thread_rng; 13 | use std::collections::VecDeque; 14 | 15 | fn main() { 16 | let mut fruit: VecDeque<&str> = VecDeque::new(); 17 | fruit.push_back("Arbutus"); 18 | fruit.push_back("Loquat"); 19 | fruit.push_back("Strawberry Tree Berry"); 20 | 21 | // Scramble (shuffle) the fruit 22 | let mut rng = thread_rng(); 23 | let mut fruit: Vec<_> = fruit.into_iter().collect(); 24 | fruit.shuffle(&mut rng); 25 | 26 | // Convert it back to VecDeque 27 | let mut fruit: VecDeque<_> = fruit.into_iter().collect(); 28 | 29 | // Add fruits to the both ends of the queue after shuffling 30 | fruit.push_front("Pomegranate"); 31 | fruit.push_back("Fig"); 32 | fruit.push_back("Cherry"); 33 | 34 | // Print out the fruit salad 35 | println!("Fruit Salad:"); 36 | for (i, item) in fruit.iter().enumerate() { 37 | if i != fruit.len() - 1 { 38 | print!("{}, ", item); 39 | } else { 40 | println!("{}", item); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vector-fruit-salad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vector-fruit-salad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" -------------------------------------------------------------------------------- /vector-fruit-salad/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /vector-fruit-salad/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This program creates a fruit salad by scrambling (shuffling) a list of fruit. 3 | A vector is a growable array. It can grow or shrink in size and is one of the most 4 | useful data structures in Rust. A vector is represented using the Vec type. 5 | */ 6 | 7 | use rand::seq::SliceRandom; // rand is a random number generation library in Rust 8 | use rand::thread_rng; 9 | 10 | fn main() { 11 | let mut fruit = vec![ 12 | "Orange", 13 | "Fig", 14 | "Pomegranate", 15 | "Cherry", 16 | "Apple", 17 | "Pear", 18 | "Peach", 19 | ]; 20 | 21 | // Scramble (shuffle) the fruit 22 | let mut rng = thread_rng(); 23 | fruit.shuffle(&mut rng); 24 | 25 | // Print out the fruit salad 26 | println!("Fruit Salad:"); 27 | for (i, item) in fruit.iter().enumerate() { 28 | if i != fruit.len() - 1 { 29 | print!("{}, ", item); 30 | } else { 31 | println!("{}", item); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /webcrawl-wikipedia-rayon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webcrawl-wikipedia-rayon" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | wikipedia = "0.3.4" 10 | rayon = "1.7.0" -------------------------------------------------------------------------------- /webcrawl-wikipedia-rayon/Makefile: -------------------------------------------------------------------------------- 1 | format: 2 | cargo fmt --quiet 3 | 4 | lint: 5 | cargo clippy --quiet 6 | 7 | test: 8 | cargo test --quiet 9 | 10 | run: 11 | cargo run 12 | 13 | all: format lint test run -------------------------------------------------------------------------------- /webcrawl-wikipedia-rayon/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | * Uses wikipedia crate to fetch pages 4 | 5 | * Processes page content 6 | 7 | * Collects timing metrics 8 | 9 | * Concurrent page processing 10 | 11 | * Shows crate usage and concurrency in Rust 12 | */ 13 | 14 | use rayon::prelude::*; 15 | use wikipedia::http::default::Client; 16 | use wikipedia::Page; 17 | use wikipedia::Wikipedia; 18 | 19 | struct ProcessedPage { 20 | title: String, 21 | data: String, 22 | } 23 | 24 | const PAGES: [&str; 9] = [ 25 | "Giannis Antetokounmpo", 26 | "James Harden", 27 | "Russell Westbrook", 28 | "Stephen Curry", 29 | "Kevin Durant", 30 | "LeBron James", 31 | "Kobe Bryant", 32 | "Michael Jordan", 33 | "Shaquille O'Neal", 34 | ]; 35 | 36 | fn process_page(page: &Page) -> ProcessedPage { 37 | let title = page.get_title().unwrap(); 38 | let content = page.get_content().unwrap(); 39 | ProcessedPage { 40 | title, 41 | data: content, 42 | } 43 | } 44 | 45 | //times how long it takes to process the pages and total time 46 | fn main() { 47 | //start timer 48 | let start = std::time::Instant::now(); 49 | let wikipedia = Wikipedia::::default(); 50 | let pages: Vec<_> = PAGES 51 | .par_iter() //parallel iterator 52 | .map(|&p| wikipedia.page_from_title(p.to_string())) 53 | .collect(); 54 | 55 | let processed_pages: Vec = pages.par_iter().map(process_page).collect(); 56 | for page in processed_pages { 57 | //time how long it takes to process each page 58 | let start_page = std::time::Instant::now(); 59 | 60 | println!("Title: {}", page.title.as_str()); 61 | //grab first sentence of the page 62 | let first_sentence = page.data.split('.').next().unwrap(); 63 | println!("First sentence: {}", first_sentence); 64 | //count the number of words in the page 65 | let word_count = page.data.split_whitespace().count(); 66 | println!("Word count: {}", word_count); 67 | //prints time it took to process each page 68 | println!("Page time: {:?}", start_page.elapsed()); 69 | } 70 | //descriptive statistics of: total time, average time per page, and total number of pages, as well as the number of threads used 71 | println!("Total time: {:?}", start.elapsed()); 72 | println!( 73 | "Average time per page: {:?}", 74 | start.elapsed() / PAGES.len() as u32 75 | ); 76 | println!("Total number of pages: {}", PAGES.len()); 77 | println!("Number of threads: {}", rayon::current_num_threads()); 78 | } 79 | --------------------------------------------------------------------------------