diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ed45c6c1..6254942d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,4 +26,4 @@ jobs: HOME: /root run: | . "$HOME/.cargo/env" - scons -Q + scons -Q --random -j2 diff --git a/.github/workflows/publish_container.yml b/.github/workflows/publish_container.yml index d1edec703..e49663542 100644 --- a/.github/workflows/publish_container.yml +++ b/.github/workflows/publish_container.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@master - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@master + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: algorithm-archivists/aaa-langs username: ${{ github.actor }} diff --git a/.gitignore b/.gitignore index 09a7ab178..5e1e79300 100644 --- a/.gitignore +++ b/.gitignore @@ -478,7 +478,7 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc - +*.ipynb_checkpoints* # Cake - Uncomment if you are using it # tools/** # !tools/packages.config diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fc3113ecc..8bf39132c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -61,4 +61,6 @@ This file lists everyone, who contributed to this repo and wanted to show up her - Hugo Salou - Dimitri Belopopsky - Henrik Abel Christensen +- K. Shudipto Amin - Peanutbutter_Warrior +- Thijs Raymakers diff --git a/SConstruct b/SConstruct index 09c939af3..150ed3b89 100644 --- a/SConstruct +++ b/SConstruct @@ -8,63 +8,128 @@ Currently, the aim is to provide a way to compile or copy the implementation fil To run the compilation for all implementations in one language, e.g. C, run the command `scons build/c`, and the resulting executables will be available in the `build/c` directory, each in their respective algorithm directory, containing the executable.""" from pathlib import Path +from collections import namedtuple import os -rust_cargo_builder = Builder(action=['cargo build --bins --manifest-path $MANIFEST', - Move('$TARGET$PROGSUFFIX', '$SOURCE_DIR/target/debug/main$PROGSUFFIX')]) +import SCons +SCons.Warnings.warningAsException() -rust_rustc_builder = Builder(action='rustc $SOURCE -o $TARGET$PROGSUFFIX') - -go_builder = Builder(action='go build -o $TARGET$PROGSUFFIX $SOURCE') +# For interpreted languages to copy to build directory +copy_builder = Builder(action=Copy('$TARGET', '$SOURCE')) env = Environment(ENV=os.environ, - BUILDERS={'rustc': rust_rustc_builder, - 'cargo': rust_cargo_builder, - 'Go': go_builder}, - tools=['gcc', 'gnulink', 'g++', 'gas', 'gfortran']) + BUILDERS={'Copier': copy_builder}, + tools=[ + 'g++', 'gas', 'gcc', 'gfortran', 'gnulink', 'javac'], + toolpath=['builders']) + +available_languages = { + 'asm-x64', + 'bash', + 'c', + 'cpp', + 'fortran', + 'java', + 'julia', + 'lolcode' + 'lua', + 'php', + 'powershell', + 'python', + 'ruby', + 'viml', +} + +languages_to_import = { + 'coconut': ['coconut'], + 'go': ['go'], + 'rust': ['rustc', 'cargo'], + 'kotlin': ['kotlin'], +} + +for language, tools in languages_to_import.items(): + for tool in tools: + try: + env.Tool(tool) + except SCons.Warnings.SConsWarning as w: + print(f'{w.args[0][0]}, ignoring') + break + else: + available_languages.add(language) + + +Export('env') -env['CFLAGS'] = '-Wall -Wextra -Werror' -env['CXXFLAGS'] = '-std=c++17' +env['CCFLAGS'] = '-Wall -Wextra -Werror -pedantic -Wconversion' +env['CFLAGS'] = '-std=gnu99' +env['CXXFLAGS'] = '-std=c++17 -Wold-style-cast' env['ASFLAGS'] = '--64' +env['COCONUTFLAGS'] = '--target 3.8' # Add other languages here when you want to add language targets # Put 'name_of_language_directory' : 'file_extension' + languages = { + 'asm-x64': 's', + 'bash': 'bash', 'c': 'c', + 'coconut': 'coco', 'cpp': 'cpp', - 'asm-x64': 's', - 'rust': 'rs', - 'go': 'go', 'fortran': 'f90', + 'go': 'go', + 'java': 'java', + 'javascript': 'js', + 'julia': 'jl', + 'kotlin': 'kt', + 'lolcode': 'lol', + 'lua': 'lua', + 'php': 'php', + 'powershell': 'ps1', + 'python': 'py', + 'ruby': 'rb', + 'rust': 'rs', + 'viml': 'vim', } +# Do not add new Builders here, add them to the BUILDERS argument in the call to Environment above env.C = env.Program env.CPlusPlus = env.Program env.X64 = env.Program env.Fortran = env.Program -Export('env') +for language in available_languages: + Alias(language, f'#/build/{language}') sconscripts = [] -files_to_compile = {language: [] for language in languages} - -for chapter_dir in Path.cwd().joinpath('contents').iterdir(): - if (code_dir := (chapter_dir / 'code')).exists(): - for path in code_dir.iterdir(): - if path.stem in languages: - # Check for overriding sconscript - if (sconscript_path := path / 'SConscript').exists(): - sconscripts.append(sconscript_path) - SConscript(sconscript_path, exports='env') +files_to_compile = {language: [] for language in languages if language in available_languages} + +FileInformation = namedtuple('FileInformation', ['path', 'chapter', 'language']) + + +contents_path = Path.cwd().joinpath('contents') +for chapter_dir in contents_path.iterdir(): + for code_dir in chapter_dir.glob('**/code'): + # For nested chapters e.g. contents/convolutions/1d/ + extended_chapter_path = code_dir.relative_to(contents_path).parent + + for language_dir in code_dir.iterdir(): + if (language := language_dir.stem) in available_languages: + new_files = [FileInformation(path=file_path, + chapter=extended_chapter_path, + language=language) + for file_path in language_dir.glob(f'**/*.{languages[language]}') + ] + # Check for overriding SConscript + if (sconscript_path := language_dir / 'SConscript').exists(): + SConscript(sconscript_path, exports={'files_to_compile': new_files}) else: - files_to_compile[path.stem].extend(path.glob(f'*.{languages[path.stem]}')) + files_to_compile[language].extend(new_files) -sconscript_dir_path = Path('sconscripts') +sconscript_dir_path = Path.cwd().joinpath('sconscripts') for language, files in files_to_compile.items(): if files: if (sconscript_path := sconscript_dir_path / f"{language}_SConscript").exists(): - SConscript(sconscript_path, exports = {'files_to_compile': files, - 'language': language}) + SConscript(sconscript_path, exports = {'files_to_compile': files}) else: print(f'{language} file found at {files[0]}, but no sconscript file is present ') diff --git a/SUMMARY.md b/SUMMARY.md index 4d4597b1c..412b7fd12 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,9 +20,13 @@ * [Multiplication as a Convolution](contents/convolutions/multiplication/multiplication.md) * [Convolutions of Images (2D)](contents/convolutions/2d/2d.md) * [Convolutional Theorem](contents/convolutions/convolutional_theorem/convolutional_theorem.md) + * [Box Muller Transform](contents/box_muller/box_muller.md) + * [How costly is rejection sampling?](contents/box_muller/box_muller_rejection.md) + * [Probability Distributions](contents/probability_distributions/distributions.md) * [Tree Traversal](contents/tree_traversal/tree_traversal.md) * [Euclidean Algorithm](contents/euclidean_algorithm/euclidean_algorithm.md) * [Monte Carlo](contents/monte_carlo_integration/monte_carlo_integration.md) + * [Metropolis](contents/metropolis/metropolis.md) * [Matrix Methods](contents/matrix_methods/matrix_methods.md) * [Gaussian Elimination](contents/gaussian_elimination/gaussian_elimination.md) * [Thomas Algorithm](contents/thomas_algorithm/thomas_algorithm.md) @@ -42,5 +46,6 @@ * [Computer Graphics](contents/computer_graphics/computer_graphics.md) * [Flood Fill](contents/flood_fill/flood_fill.md) * [Quantum Information](contents/quantum_information/quantum_information.md) +* [Cryptography](contents/cryptography/cryptography.md) * [Computus](contents/computus/computus.md) * [Approximate Counting Algorithm](contents/approximate_counting/approximate_counting.md) diff --git a/builders/cargo.py b/builders/cargo.py new file mode 100644 index 000000000..0ac22e086 --- /dev/null +++ b/builders/cargo.py @@ -0,0 +1,41 @@ +from SCons.Builder import Builder +from SCons.Script import Move +import SCons.Util + +class ToolCargoWarning(SCons.Warnings.SConsWarning): + pass + +class CargoNotFound(ToolCargoWarning): + pass + +SCons.Warnings.enableWarningClass(ToolCargoWarning) + +def _detect(env): + try: + return env['cargo'] + except KeyError: + pass + + cargo = env.WhereIs('cargo') + if cargo: + return cargo + + SCons.Warnings.warn(CargoNotFound, 'Could not detect cargo') + +def exists(env): + return env.Detect('cargo') + + +def generate(env): + env['CARGO'] = _detect(env) + env['CARGOFLAGS'] = [] + env['MANIFEST'] = [] + + rust_cargo_builder = Builder( + action=['"$CARGO" build $CARGOFLAGS --bins --manifest-path $MANIFEST', + Move('$TARGET$PROGSUFFIX', + '$SOURCE_DIR/target/debug/main$PROGSUFFIX') + ], + suffix='$PROGSUFFIX', + ) + env.Append(BUILDERS={'cargo': rust_cargo_builder}) diff --git a/builders/coconut.py b/builders/coconut.py new file mode 100644 index 000000000..36498412b --- /dev/null +++ b/builders/coconut.py @@ -0,0 +1,40 @@ +from SCons.Builder import Builder +import SCons.Util + +class ToolCocoWarning(SCons.Warnings.SConsWarning): + pass + +class CoconutNotFound(ToolCocoWarning): + pass + +SCons.Warnings.enableWarningClass(ToolCocoWarning) + +def _detect(env): + try: + return env['coconut'] + except KeyError: + pass + + coconut = env.WhereIs('coconut') + if coconut: + return coconut + + SCons.Warnings.warn(CoconutNotFound, 'Could not find Coconut executable') + + +def generate(env): + env['COCONUT'] = _detect(env) + env['COCONUTFLAGS'] = [] + + coconut_compiler = Builder( + action='"$COCONUT" $COCONUTFLAGS $SOURCE $TARGET', + src_suffix='.coco', + suffix='.py', + ) + + env.Append(BUILDERS={'Coconut': coconut_compiler}) + +def exists(env): + return env.Detect('coconut') + + diff --git a/builders/go.py b/builders/go.py new file mode 100644 index 000000000..261789092 --- /dev/null +++ b/builders/go.py @@ -0,0 +1,37 @@ +from SCons.Builder import Builder +import SCons.Util + +class ToolGoWarning(SCons.Warnings.SConsWarning): + pass + +class GoNotFound(ToolGoWarning): + pass + +SCons.Warnings.enableWarningClass(ToolGoWarning) + +def _detect(env): + try: + return env['go'] + except KeyError: + pass + + go = env.WhereIs('go') + if go: + return go + + SCons.Warnings.warn(GoNotFound, 'Could not find go executable') + +def exists(env): + env.Detect('go') + +def generate(env): + env['GO'] = _detect(env) + env['GOFLAGS'] = [] + + go_builder = Builder( + action='"$GO" build -o $TARGET $GOFLAGS $SOURCE', + src_suffix='.go', + suffix='$PROGSUFFIX', + ) + + env.Append(BUILDERS={'Go': go_builder}) diff --git a/builders/kotlin.py b/builders/kotlin.py new file mode 100644 index 000000000..fc1d9ecb1 --- /dev/null +++ b/builders/kotlin.py @@ -0,0 +1,184 @@ +# MIT License +# +# Copyright The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""SCons.Tool.kotlin +Tool-specific initialization for Kotlin. +""" + +import SCons.Action +import SCons.Builder +import SCons.Util + + +class ToolKotlinWarning(SCons.Warnings.SConsWarning): + pass + + +class KotlinNotFound(ToolKotlinWarning): + pass + + +SCons.Warnings.enableWarningClass(ToolKotlinWarning) + + +def _detect(env): + """ Try to detect the kotlinc binary """ + try: + return env["kotlinc"] + except KeyError: + pass + + kotlin = env.Detect("kotlinc") + if kotlin: + return kotlin + + SCons.Warnings.warn(KotlinNotFound, "Could not find kotlinc executable") + + +# +# Builders +# +kotlinc_builder = SCons.Builder.Builder( + action=SCons.Action.Action("$KOTLINCCOM", "$KOTLINCCOMSTR"), + suffix="$KOTLINCLASSSUFFIX", + src_suffix="$KOTLINSUFFIX", + single_source=True, +) # file by file + +kotlin_jar_builder = SCons.Builder.Builder( + action=SCons.Action.Action("$KOTLINJARCOM", "$KOTLINJARCOMSTR"), + suffix="$KOTLINJARSUFFIX", + src_suffix="$KOTLINSUFFIX", + single_source=True, +) # file by file + +kotlin_rtjar_builder = SCons.Builder.Builder( + action=SCons.Action.Action("$KOTLINRTJARCOM", "$KOTLINRTJARCOMSTR"), + suffix="$KOTLINJARSUFFIX", + src_suffix="$KOTLINSUFFIX", + single_source=True, +) # file by file + + +def Kotlin(env, target, source=None, *args, **kw): + """ + A pseudo-Builder wrapper for the kotlinc executable. + kotlinc [options] file + """ + if not SCons.Util.is_List(target): + target = [target] + if not source: + source = target[:] + if not SCons.Util.is_List(source): + source = [source] + + result = [] + kotlinc_suffix = env.subst("$KOTLINCLASSSUFFIX") + kotlinc_extension = env.subst("$KOTLINEXTENSION") + for t, s in zip(target, source): + t_ext = t + if not t.endswith(kotlinc_suffix): + if not t.endswith(kotlinc_extension): + t_ext += kotlinc_extension + + t_ext += kotlinc_suffix + # Ensure that the case of first letter is upper-case + t_ext = t_ext[:1].upper() + t_ext[1:] + # Call builder + kotlin_class = kotlinc_builder.__call__(env, t_ext, s, **kw) + result.extend(kotlin_class) + + return result + + +def KotlinJar(env, target, source=None, *args, **kw): + """ + A pseudo-Builder wrapper for creating JAR files with the kotlinc executable. + kotlinc [options] file -d target + """ + if not SCons.Util.is_List(target): + target = [target] + if not source: + source = target[:] + if not SCons.Util.is_List(source): + source = [source] + + result = [] + for t, s in zip(target, source): + # Call builder + kotlin_jar = kotlin_jar_builder.__call__(env, t, s, **kw) + result.extend(kotlin_jar) + + return result + + +def KotlinRuntimeJar(env, target, source=None, *args, **kw): + """ + A pseudo-Builder wrapper for creating standalone JAR files with the kotlinc executable. + kotlinc [options] file -d target -include-runtime + """ + if not SCons.Util.is_List(target): + target = [target] + if not source: + source = target[:] + if not SCons.Util.is_List(source): + source = [source] + + result = [] + for t, s in zip(target, source): + # Call builder + kotlin_jar = kotlin_rtjar_builder.__call__(env, t, s, **kw) + result.extend(kotlin_jar) + + return result + + +def generate(env): + """Add Builders and construction variables for kotlinc to an Environment.""" + + env["KOTLINC"] = _detect(env) + + env.SetDefault( + KOTLINC="kotlinc", + KOTLINSUFFIX=".kt", + KOTLINEXTENSION="Kt", + KOTLINCLASSSUFFIX=".class", + KOTLINJARSUFFIX=".jar", + KOTLINCFLAGS=SCons.Util.CLVar(), + KOTLINJARFLAGS=SCons.Util.CLVar(), + KOTLINRTJARFLAGS=SCons.Util.CLVar(["-include-runtime"]), + KOTLINCCOM="$KOTLINC $KOTLINCFLAGS $SOURCE", + KOTLINCCOMSTR="", + KOTLINJARCOM="$KOTLINC $KOTLINJARFLAGS -d $TARGET $SOURCE", + KOTLINJARCOMSTR="", + KOTLINRTJARCOM="$KOTLINC $KOTLINRTJARFLAGS -d $TARGET $SOURCE", + KOTLINRTJARCOMSTR="", + ) + + env.AddMethod(Kotlin, "Kotlin") + env.AddMethod(KotlinJar, "KotlinJar") + env.AddMethod(KotlinRuntimeJar, "KotlinRuntimeJar") + + +def exists(env): + return _detect(env) diff --git a/builders/rustc.py b/builders/rustc.py new file mode 100644 index 000000000..f07cf4fad --- /dev/null +++ b/builders/rustc.py @@ -0,0 +1,45 @@ +from SCons.Builder import Builder +import SCons.Util + +class ToolRustcWarning(SCons.Warnings.SConsWarning): + pass + +class RustcNotFound(ToolRustcWarning): + pass + +SCons.Warnings.enableWarningClass(ToolRustcWarning) + +def _detect(env): + try: + return env['rustc'] + except KeyError: + pass + + cargo = env.WhereIs('rustc') + if cargo: + return cargo + + SCons.Warnings.warn(RustcNotFound, 'Could not detect rustc') + + +def exists(env): + return env.Detect('rustc') + +def rustc_emitter(target, source, env): + src_name = str(source[0]) + pdb_name = src_name.replace(source[0].suffix, '.pdb') + env.SideEffect(pdb_name, target) + env.Clean(target, pdb_name) + return (target, source) + +def generate(env): + env['RUSTC'] = _detect(env) + env['RUSTCFLAGS'] = [] + + rust_cargo_builder = Builder( + action='"$RUSTC" $RUSTCFLAGS -o $TARGET $SOURCE', + suffix='$PROGSUFFIX', + src_suffix='.rs', + emitter=rustc_emitter, + ) + env.Append(BUILDERS={'rustc': rust_cargo_builder}) diff --git a/contents/IFS/IFS.md b/contents/IFS/IFS.md index f7d017e36..1e1e45d37 100644 --- a/contents/IFS/IFS.md +++ b/contents/IFS/IFS.md @@ -20,8 +20,8 @@ To begin the discussion of Iterated Function Systems (IFSs), we will first discu Sierpinsky Triangle Chaos Game This image is clearly a set of triangles embedded in a larger triangle in such a way that it can be continually cut into three identical pieces and still retain its internal structure. -This idea is known as self-similarity {{"self-similar" | cite }}, and it is usually the first aspect of fractals to catch an audience's attention. -In fact, there are plenty of uses of fractals and their mathematical underpinnings, such as estimating the coastline of Britain {{ "mandelbrot1967long" | cite}}, identifying fingerprints {{ "jampour2010new" | cite }}, and image compression {{ "fractal-compression" | cite }}{{ "saupe1994review" | cite }}. +This idea is known as self-similarity {{ "self-similar" | cite }}, and it is usually the first aspect of fractals to catch an audience's attention. +In fact, there are plenty of uses of fractals and their mathematical underpinnings, such as estimating the coastline of Britain {{ "mandelbrot1967long" | cite }}, identifying fingerprints {{ "jampour2010new" | cite }}, and image compression {{ "fractal-compression" | cite }}{{ "saupe1994review" | cite }}. In many more rigorous definitions, a fractal can be described as any system that has a non-integer Hausdorff dimension {{ "3b1bfractal" | cite }}{{ "hausdorff" | cite }}{{ "gneiting2012estimators" | cite }}. Though this is an incredibly interesting concept, the discussion of this chapter will instead focus on methods to generate fractal patterns through iterated function systems. @@ -41,7 +41,7 @@ f_3(P) &= \frac{P + C}{2}\\ $$ Each function will read in a particular location in space (here, $$P \in \mathbb{R}^2$$) and output a new location that is the midpoint between the input location and $$A$$, $$B$$, or $$C$$ for $$f_1$$, $$f_2$$, and $$f_3$$ respectively. -The union of all of these functions (the set of all possible functions available for use) is often notated as the _Hutchinson operator_ {{ "hutchinson-operator" | cite }}{{ "hutchinson1981fractals" | cite}}, and for this case it would look like this: +The union of all of these functions (the set of all possible functions available for use) is often notated as the _Hutchinson operator_ {{ "hutchinson-operator" | cite }}{{ "hutchinson1981fractals" | cite }}, and for this case it would look like this: $$ H(P) = \bigcup_{i=1}^3f_i(P) @@ -144,6 +144,8 @@ Here, instead of tracking children of children, we track a single individual tha [import:5-14, lang:"lisp"](code/clisp/ifs.lisp) {% sample lang="coco" %} [import:4-16, lang:"coconut"](code/coconut/IFS.coco) +{% sample lang="rust" %} +[import:9-20, lang:"rust"](code/rust/IFS.rs) {% sample lang="java" %} [import:16-39, lang:"java"](code/java/IFS.java) {% sample lang="ps1" %} @@ -206,7 +208,7 @@ If you are interested, please let me know and I will be more than willing to add Here is a video describing iterated function systems:
-
@@ -232,6 +234,8 @@ In addition, we have written the chaos game code to take in a set of points so t [import, lang:"lisp"](code/clisp/ifs.lisp) {%sample lang="coco" %} [import, lang:"coconut"](code/coconut/IFS.coco) +{%sample lang="rust" %} +[import, lang:"rust"](code/rust/IFS.rs) {%sample lang="java" %} [import, lang:"java"](code/java/IFS.java) {% sample lang="ps1" %} diff --git a/contents/IFS/code/c/IFS.c b/contents/IFS/code/c/IFS.c index e4aab3b50..ffd56f6fe 100644 --- a/contents/IFS/code/c/IFS.c +++ b/contents/IFS/code/c/IFS.c @@ -29,18 +29,18 @@ void chaos_game(struct point *in, size_t in_n, struct point *out, } int main() { - const int point_count = 10000; + const size_t point_count = 10000; struct point shape_points [3] = {{0.0,0.0}, {0.5,sqrt(0.75)}, {1.0,0.0}}; struct point out_points[point_count]; - srand(time(NULL)); + srand((unsigned int)time(NULL)); chaos_game(shape_points, 3, out_points, point_count); FILE *fp = fopen("sierpinksi.dat", "w+"); - for (int i = 0; i < point_count; ++i) { + for (size_t i = 0; i < point_count; ++i) { fprintf(fp, "%f\t%f\n", out_points[i].x, out_points[i].y); } diff --git a/contents/IFS/code/rust/Cargo.toml b/contents/IFS/code/rust/Cargo.toml new file mode 100644 index 000000000..90fbca22d --- /dev/null +++ b/contents/IFS/code/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.4" + +[[bin]] +path = "./IFS.rs" +name = "main" \ No newline at end of file diff --git a/contents/IFS/code/rust/IFS.rs b/contents/IFS/code/rust/IFS.rs new file mode 100644 index 000000000..476565f0f --- /dev/null +++ b/contents/IFS/code/rust/IFS.rs @@ -0,0 +1,36 @@ +use rand::*; + +#[derive(Clone, Copy)] +struct Point { + x: f64, + y: f64, +} + +fn chaos_game(iters: usize, shapes: Vec) -> Vec { + let mut rng = rand::thread_rng(); + let mut p = Point{x: rng.gen(), y: rng.gen()}; + + (0..iters).into_iter().map(|_| { + let old_point = p; + let tmp = shapes[rng.gen_range(0..shapes.len())]; + p.x = 0.5 * (p.x + tmp.x); + p.y = 0.5 * (p.y + tmp.y); + old_point + }).collect() +} + +fn main() { + let shapes = vec![ + Point{x: 0., y: 0.}, + Point{x: 0.5, y: 0.75_f64.sqrt()}, + Point{x: 1., y: 0.}, + ]; + + let mut out = String::new(); + + for point in chaos_game(10_000, shapes) { + out += format!("{}\t{}\n", point.x, point.y).as_str(); + } + + std::fs::write("./sierpinski.dat", out).unwrap(); +} \ No newline at end of file diff --git a/contents/affine_transformations/affine_transformations.md b/contents/affine_transformations/affine_transformations.md index 439440b80..d832f9fc0 100644 --- a/contents/affine_transformations/affine_transformations.md +++ b/contents/affine_transformations/affine_transformations.md @@ -297,7 +297,7 @@ I think that is a nice note to end on: affine transformations are linear transfo Here is a video describing affine transformations:
- +
+ +## License + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/main/LICENSE.md)). + +##### Text + +The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + +#### Images/Graphics + +- The image "[IFS triangle 1](../IFS/res/IFS_triangle_1.png)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The image "[IFS square 3](../IFS/res/IFS_square_3.png)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The image "[Simple Barnsley fern](res/full_fern.png)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine random transform 0](res/affine_rnd_0.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine random transform 1](res/affine_rnd_1.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine random transform 2](res/affine_rnd_2.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine random transform 3](res/affine_rnd_3.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine fern transform 0](res/affine_fern_0.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine fern transform 1](res/affine_fern_1.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine fern transform 2](res/affine_fern_2.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Affine fern transform 3](res/affine_fern_3.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Fern twiddle 0](res/fern_twiddle_0.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Fern twiddle 1](res/fern_twiddle_1.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Fern twiddle 2](res/fern_twiddle_2.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The video "[Fern twiddle 3](res/fern_twiddle_3.mp4)" was created by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). diff --git a/contents/box_muller/box_muller_rejection.md b/contents/box_muller/box_muller_rejection.md new file mode 100644 index 000000000..37e1c0e3d --- /dev/null +++ b/contents/box_muller/box_muller_rejection.md @@ -0,0 +1,126 @@ +# Just how costly is rejection sampling anyway? + +Let's imagine we want to have a final Gaussian distribution with $$n$$ particles in it. +With the Cartesian Box—Muller method, this is easy: start the initial distribution(s) with $$n$$ particles and then do the transform. +Things *can* be just as easy with the Polar Box—Muller method as well, so long as we start with a uniformly distributed *circle* instead of a uniformly distributed *square*. +That is to say, so long as we do the rejection sampling before-hand, the Polar Box—Muller method will always be more efficient. +To be fair, there are methods to generate a uniform distribution of points within a circle without rejection sampling, but let's assume that we require rejection sampling for this example + +This means that someone somehow needs to do the rejection sampling for the Polar method, which is sometimes a painful process. +This also means that the Box—Muller method can be used to teach some of the fundamentals of General-Purpose GPU computing. +Note that because of the specificity of this problem, all the code in this subsection will be in Julia and using the package KernelAbstractions.jl, which allows us to execute the same kernels on either CPU or GPU hardware depending on how we configure things. + +Let's first consider the case where we do the rejection sampling as a part of the polar Box—Muller kernel instead of as a pre-processing step. +In this case, we can imagine 2 separate ways of writing our kernel: +1. With replacement: In this case, we *absolutely require* the final number of points in our Gaussian distribution to be $$n$$, so if we find a point outside of the unit circle while running the kernel, we will "re-roll" again for a new point that *is* within the circle. +2. Without replacement: This means that we will start with a uniform distribution of $$n$$ points, but end with a Gaussian of $$m < n$$ points. In this case, if we find a point outside of the unit circle while running the kernel, we just ignore it by setting the output values to NaNs (or something similar). + +OK, so first with replacement: + +[import:70-84, lang:"julia"](code/julia/performance.jl) + +This is an awful idea for a number of reasons. +Here are a few: +1. If we find a point outside of the unit circle, we have to continually look for new points until we *do* find one inside of the circle. Because we are running this program in parallel, where each thread transforms one point at a time, some threads might take literally forever to find a new point (if we are really unlucky). +2. To generate new points, we need to re-generate a uniform distribution, but what if our uniform distribution is not random? What if it's a grid (or something similar) instead? In this case, we really shouldn't look for a new point on the inside of the circle as all those points have already been accounted for. +3. The `rand()` function is kinda tricky on some parallel platforms (like GPUs) and might not work out of the box. In fact, the implementation shown above can only be run on the CPU. + +OK, fine. +I don't think anyone expected a kernel with a `while` loop inside of it to be fast. +So what about a method without replacement? +Surely there is no problem if we just ignore the `while` loop altogether! +Well, the problem with this approach is a bit less straightforward, but first, code: + +[import:53-68, lang:"julia"](code/julia/performance.jl) + +To start discussing why a polar kernel without replacement is *also* a bad idea, let's go back to the [Monte Carlo chapter](../monte_carlo/monte_carlo.md), where we calculated the value of $$\pi$$ by embedding it into a circle. +There, we found that the probability of a randomly chosen point falling within the unit circle to be $$\frac{\pi r^2}{(2r)^2} = \frac{pi}{4} \sim 78.54\%$$, shown in the visual below: + +

+ +

+ +This means that a uniform distribution of points within a circle will reject $$\sim 21.46\%$$ of points on the square. +This also means that if we have a specific $$n$$ value we want for the final distribution, we will need $$\frac{1}{0.7853} \sim 1.273 \times$$ more input values on average! + +No problem! +In this hypothetical case, we don't need *exactly* $$n$$ points, so we can just start the initial distributions with $$1.273 \times n$$ points, right? + +Right. +That will work well on parallel CPU hardware, but on the GPU this will still have an issue. + +On the GPU, computation is all done in parallel, but there is a minimum unit of parallelism called a *warp*. +The warp is the smallest number of threads that can execute something in parallel and is usually about 32. +This means that if an operation is queued, all 32 threads will do it at the same time. +If 16 threads need to execute something and the other 16 threads need to execute something else, this will lead to *warp divergence* where 2 actions need to be performed instead of 1: + +

+ +

+ +In this image, every odd thread needs to perform the pink action, while the even threads need to perform the blue action. +This means that 2 separate parallel tasks will be performed, one for the even threads, another for the odd threads. +This means that if $$\ell$$ separate operations are queued, it could take $$\ell\times$$ as long for all the threads to do their work! +This is why `if` statements in a kernel can be dangerous! +If used improperly, they can cause certain threads in a warp to do different things! + +So let's imagine that the above image is part of a larger array of values, such that there are a bunch of warps with the same divergence issue. +In this case, we could sort the array before-hand so that all even elements come before all odd elements. +This would mean that the warps will almost certainly not diverge because the elements queued will all be of the same type and require the same operations. +Unfortunately, this comes at the cost of a sorting operation which is prohibitively expensive. + +If we look at the above kernel, we are essentially asking $$21.47\%$$ of our threads to do something different than everyone else, and because we are usually inputting a uniform random distribution, this means that *most* warps will have to queue up 2 parallel actions instead of 1. + +Essentially, we need to pick our poison: +* Slow $$\sin$$ and $$\cos$$ operations with the Cartesian method +* Warp divergence with the Polar method + +The only way to know which is better is to perform benchmarks, which we will show in a bit, but there is one final scenario we should consider: what about doing the rejection sampling as a pre-processing step? +This would mean that we pre-initialize the polar kernel with a uniform distribution of points in the unit circle. +This means no warp divergence, so we can get the best of both worlds, right? + +Well, not exactly. +The polar Box—Muller method will definitely be faster, but again: someone somewhere needed to do rejection sampling and if we include that step into the process, things become complicated again. +The truth is that this pre-processing step is difficult to get right, so it might require a chapter in it's own right. + +In many cases, it's worth spending a little time before-hand to make sure subsequent operations are fast, but in this case, we only have a single operation, not a set of operations. +The Box—Muller method will usually only be used once at the start of the simulation, which means that the pre-processing step of rejection sampling might end up being overkill. + +No matter the case, benchmarks will show the true nature of what we are dealing with here: + +| Method | CPU | GPU | +| ------------------------- | ---------------------- | ---------------------- | +| Cartesian | $$385.819 \pm 1.9$$ms | $$19.347 \pm 0.618$$ms | +| Polar without replacement | $$273.308 \pm 2.81$$ms | $$26.712 \pm 0.592$$ms | +| Polar with replacement | $$433.644 \pm 2.64$$ms | NA | + +These were run with an Nvidia GTX 970 GPU and a Ryzen 3700X 16 core CPU. +For those interested, the code can be found below. +For these benchmarks, we used Julia's inbuilt benchmarking suite from `BenchmarkTools`, making sure to sync the GPU kernels with `CUDA.@sync`. +We also ran with $$4096^2$$ input points. + +Here, we see an interesting divergence in the results. +On the CPU, the polar method is *always* faster, but on the GPU, both methods are comparable. +I believe this is the most important lesson to be learned from the Box—Muller method: sometimes, no matter how hard you try to optimize your code, different hardware can provide radically different results! +It's incredibly important to benchmark code to make sure it is actually is as performant as you think it is! + +## Full Script + +[import, lang:"julia"](code/julia/performance.jl) + + + +## License + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/main/LICENSE.md)). + +##### Text + +The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + diff --git a/contents/box_muller/code/julia/box_muller.jl b/contents/box_muller/code/julia/box_muller.jl new file mode 100644 index 000000000..82f614bd6 --- /dev/null +++ b/contents/box_muller/code/julia/box_muller.jl @@ -0,0 +1,156 @@ +using DelimitedFiles, LinearAlgebra +using Test + +function create_grid(n, endpoints) + + grid_extents = endpoints[2] - endpoints[1] + + # number of points along any given axis + # For 2D, we take the sqrt(n) and then round up + axis_num = ceil(Int, sqrt(n)) + + # we are now rounding n up to the nearest square if it was not already one + if sqrt(n) != axis_num + n = axis_num^2 + end + + # Distance between each point + dx = grid_extents / (axis_num) + + # Initializing the array, particles along the column, dimensions along rows + a = zeros(n, 2) + + # This loops over the relevant dimensions + for i = 1:axis_num + for j = 1:axis_num + a[(i - 1) * axis_num + j, :] .= + [(i - 0.5) * dx + endpoints[1], + (j - 0.5) * dx + endpoints[1]] + end + end + + return a +end + +function create_rand_dist(n, endpoints) + grid_extents = endpoints[2] - endpoints[1] + return(rand(n,2) * grid_extents .+ endpoints[1]) +end + +# This function reads in a pair of input points and performs the Cartesian +# Box--Muller transform +function cartesian_box_muller(input_pts, sigma, mu) + r = sqrt(-2 * log(input_pts[1])) + theta = 2 * pi * input_pts[2] + + return [sigma * r * cos(theta) + mu[1], + sigma * r * sin(theta) + mu[2]] + +end + +# This function reads in a pair of input points and performs the Cartesian +# Box--Muller transform +function polar_box_muller(input_pts, sigma, mu) + r_0 = input_pts[1]^2 + input_pts[2]^2 + + # this method is only valid for points within the unit circle + if r_0 == 0 || r_0 > 1 + return [NaN, NaN] + end + + return [sigma * input_pts[1] * sqrt(-2 * log(r_0) / r_0) + mu[1], + sigma * input_pts[2] * sqrt(-2 * log(r_0) / r_0) + mu[2]] + +end + +function is_gaussian(input_pts; bounds = [-1 1; -1 1], dx = 0.1, + sigma = 1, mu = [0,0], threshold = 0.1) + histogram = zeros(ceil(Int,(bounds[1,2]-bounds[1,1])/dx), + ceil(Int,(bounds[2,2]-bounds[2,1])/dx)) + + for i = 1:size(input_pts)[1] + input_x = input_pts[i, 1] + input_y = input_pts[i, 2] + if !(isnan(input_x) || isnan(input_y)) + + bin = CartesianIndex(ceil(Int, (input_x - bounds[1,1]) / dx), + ceil(Int, (input_y - bounds[2,1]) / dx)) + + if bin[1] <= size(histogram)[1] && bin[1] > 0 && + bin[2] <= size(histogram)[2] && bin[2] > 0 + histogram[bin] += 1 + end + end + end + + n = sum(histogram) + normalize!(histogram) + + rms = 0 + for i = 1:size(histogram)[1] + x = bounds[1,1] + i*dx + for j = 1:size(histogram)[2] + y = bounds[2,1] + j*dx + gaussian_value = exp(-(((x+mu[1])^2)/(2*sigma^2) + + ((y+mu[2])^2)/(2*sigma^2))) + rms += (gaussian_value - histogram[i,j])^2 + end + end + + return sqrt(rms/n) < threshold +end + +function main(n) + + # This casts the input onto the nearest square for the cartesian grids + n = Int(ceil(sqrt(n))^2) + + cartesian_grid = create_grid(n, [0,1]) + polar_grid = create_grid(n, [-1,1]) + cartesian_rand = create_rand_dist(n, [0,1]) + polar_rand = create_rand_dist(n, [-1,1]) + + cartesian_grid_output = similar(cartesian_grid) + polar_grid_output = similar(polar_grid) + cartesian_rand_output = similar(cartesian_rand) + polar_rand_output = similar(polar_rand) + + # going through each pair of points and using the x,y coordinates in + # their respective functions + for i = 1:size(cartesian_grid)[1] + cartesian_grid_output[i,:] .= + cartesian_box_muller(cartesian_grid[i,:], 1, [0,0]) + + polar_grid_output[i,:] .= polar_box_muller(polar_grid[i,:], 1, [0,0]) + + cartesian_rand_output[i,:] .= + cartesian_box_muller(cartesian_rand[i,:], 1, [0,0]) + + polar_rand_output[i,:] .= polar_box_muller(polar_rand[i,:], 1, [0,0]) + end + + @testset "histogram tests of Box--Muller Gaussianness" begin + @test is_gaussian(cartesian_grid_output; + bounds = [-3 3; -3 3], dx = 0.3, + sigma = 1, mu = [0,0]) + @test is_gaussian(cartesian_rand_output; + bounds = [-3 3; -3 3], dx = 0.3, + sigma = 1, mu = [0,0]) + @test is_gaussian(polar_grid_output; + bounds = [-3 3; -3 3], dx = 0.3, + sigma = 1, mu = [0,0]) + @test is_gaussian(polar_rand_output; + bounds = [-3 3; -3 3], dx = 0.3, + sigma = 1, mu = [0,0]) + end + + writedlm("cartesian_grid_output.dat", cartesian_grid_output) + writedlm("polar_grid_output.dat", polar_grid_output) + writedlm("cartesian_rand_output.dat", cartesian_rand_output) + writedlm("polar_rand_output.dat", polar_rand_output) + + writedlm("cartesian_grid.dat", cartesian_grid) + writedlm("polar_grid.dat", polar_grid) + writedlm("cartesian_rand.dat", cartesian_rand) + writedlm("polar_rand.dat", polar_rand) +end diff --git a/contents/box_muller/code/julia/performance.jl b/contents/box_muller/code/julia/performance.jl new file mode 100644 index 000000000..d8732df33 --- /dev/null +++ b/contents/box_muller/code/julia/performance.jl @@ -0,0 +1,142 @@ +using KernelAbstractions +using CUDA + +if has_cuda_gpu() + using CUDAKernels +end + +function create_grid(n, endpoints; AT = Array) + + grid_extents = endpoints[2] - endpoints[1] + + # number of points along any given axis + # For 2D, we take the sqrt(n) and then round up + axis_num = ceil(Int, sqrt(n)) + + # we are now rounding n up to the nearest square if it was not already one + if sqrt(n) != axis_num + n = axis_num^2 + end + + # Distance between each point + dx = grid_extents / (axis_num) + + # This is warning in the case that we do not have a square number + if sqrt(n) != axis_num + println("Cannot evenly divide ", n, " into 2 dimensions!") + end + + # Initializing the array, particles along the column, dimensions along rows + a = AT(zeros(n, 2)) + + # This works by firxt generating an N dimensional tuple with the number + # of particles to be places along each dimension ((10,10) for 2D and n=100) + # Then we generate the list of all CartesianIndices and cast that onto a + # grid by multiplying by dx and subtracting grid_extents/2 + for i = 1:axis_num + for j = 1:axis_num + a[(i - 1) * axis_num + j, 1] = i * dx + endpoints[1] + a[(i - 1) * axis_num + j, 2] = j * dx + endpoints[1] + end + end + + return a +end + +function create_rand_dist(n, endpoints; AT = Array) + grid_extents = endpoints[2] - endpoints[1] + return(AT(rand(n,2)) * grid_extents .+ endpoints[1]) +end + +# This function reads in a pair of input points and performs the Cartesian +# Box--Muller transform +@kernel function polar_muller_noreplacement!(input_pts, output_pts, sigma, mu) + tid = @index(Global, Linear) + @inbounds r_0 = input_pts[tid, 1]^2 + input_pts[tid, 2]^2 + + # this method is only valid for points within the unit circle + if r_0 == 0 || r_0 > 1 + @inbounds output_pts[tid,1] = NaN + @inbounds output_pts[tid,2] = NaN + else + @inbounds output_pts[tid,1] = sigma * input_pts[tid,1] * + sqrt(-2 * log(r_0) / r_0) + mu + @inbounds output_pts[tid,2] = sigma * input_pts[tid, 2] * + sqrt(-2 * log(r_0) / r_0) + mu + end + +end + +@kernel function polar_muller_replacement!(input_pts, output_pts, sigma, mu) + tid = @index(Global, Linear) + @inbounds r_0 = input_pts[tid, 1]^2 + input_pts[tid, 2]^2 + + while r_0 > 1 || r_0 == 0 + p1 = rand()*2-1 + p2 = rand()*2-1 + r_0 = p1^2 + p2^2 + end + + @inbounds output_pts[tid,1] = sigma * input_pts[tid,1] * + sqrt(-2 * log(r_0) / r_0) + mu + @inbounds output_pts[tid,2] = sigma * input_pts[tid, 2] * + sqrt(-2 * log(r_0) / r_0) + mu +end + + +function polar_box_muller!(input_pts, output_pts, sigma, mu; + numthreads = 256, numcores = 4, + f = polar_muller_noreplacement!) + if isa(input_pts, Array) + kernel! = f(CPU(), numcores) + else + kernel! = f(CUDADevice(), numthreads) + end + kernel!(input_pts, output_pts, sigma, mu, ndrange=size(input_pts)[1]) +end + + +@kernel function cartesian_kernel!(input_pts, output_pts, sigma, mu) + tid = @index(Global, Linear) + + @inbounds r = sqrt(-2 * log(input_pts[tid,1])) + @inbounds theta = 2 * pi * input_pts[tid, 2] + + @inbounds output_pts[tid,1] = sigma * r * cos(theta) + mu + @inbounds output_pts[tid,2] = sigma * r * sin(theta) + mu +end + +function cartesian_box_muller!(input_pts, output_pts, sigma, mu; + numthreads = 256, numcores = 4) + if isa(input_pts, Array) + kernel! = cartesian_kernel!(CPU(), numcores) + else + kernel! = cartesian_kernel!(CUDADevice(), numthreads) + end + + kernel!(input_pts, output_pts, sigma, mu, ndrange=size(input_pts)[1]) +end + +function main() + + input_pts = create_rand_dist(4096^2,[0,1]) + output_pts = create_rand_dist(4096^2,[0,1]) + + wait(cartesian_box_muller!(input_pts, output_pts, 1, 0)) + @time wait(cartesian_box_muller!(input_pts, output_pts, 1, 0)) + wait(polar_box_muller!(input_pts, output_pts, 1, 0)) + @time wait(polar_box_muller!(input_pts, output_pts, 1, 0)) + + if has_cuda_gpu() + input_pts = create_rand_dist(4096^2,[0,1], AT = CuArray) + output_pts = create_rand_dist(4096^2,[0,1], AT = CuArray) + + wait(cartesian_box_muller!(input_pts, output_pts, 1, 0)) + CUDA.@time wait(cartesian_box_muller!(input_pts, output_pts, 1, 0)) + wait(polar_box_muller!(input_pts, output_pts, 1, 0)) + CUDA.@time wait(polar_box_muller!(input_pts, output_pts, 1, 0)) + end + +end + +main() diff --git a/contents/box_muller/code/julia/plot.gp b/contents/box_muller/code/julia/plot.gp new file mode 100644 index 000000000..7dbbf4808 --- /dev/null +++ b/contents/box_muller/code/julia/plot.gp @@ -0,0 +1,31 @@ +set terminal pngcairo +set size square + +set output "cartesian_grid.png" +p "cartesian_grid.dat" pt 7 title "" + +set output "cartesian_rand.png" +p "cartesian_rand.dat" pt 7 title "" + +set output "polar_rand.png" +p "polar_rand.dat" pt 7 title "" + +set output "polar_grid.png" +p "polar_grid.dat" pt 7 title "" + + +set xrange[-3:3] +set yrange[-3:3] + +set output "polar_grid_output.png" +p "polar_grid_output.dat" pt 7 title "" + +set output "polar_rand_output.png" +p "polar_rand_output.dat" pt 7 title "" + +set output "cartesian_rand_output.png" +p "cartesian_rand_output.dat" pt 7 title "" + +set output "cartesian_grid_output.png" +p "cartesian_grid_output.dat" pt 7 title "" + diff --git a/contents/box_muller/res/cartesian_grid.png b/contents/box_muller/res/cartesian_grid.png new file mode 100644 index 000000000..c96144a65 Binary files /dev/null and b/contents/box_muller/res/cartesian_grid.png differ diff --git a/contents/box_muller/res/cartesian_grid_output.png b/contents/box_muller/res/cartesian_grid_output.png new file mode 100644 index 000000000..daf0151af Binary files /dev/null and b/contents/box_muller/res/cartesian_grid_output.png differ diff --git a/contents/box_muller/res/cartesian_grid_transform.png b/contents/box_muller/res/cartesian_grid_transform.png new file mode 100644 index 000000000..c5e88c484 Binary files /dev/null and b/contents/box_muller/res/cartesian_grid_transform.png differ diff --git a/contents/box_muller/res/cartesian_grid_transform.xcf b/contents/box_muller/res/cartesian_grid_transform.xcf new file mode 100644 index 000000000..b81accf3d Binary files /dev/null and b/contents/box_muller/res/cartesian_grid_transform.xcf differ diff --git a/contents/box_muller/res/cartesian_rand.png b/contents/box_muller/res/cartesian_rand.png new file mode 100644 index 000000000..8c21b32d7 Binary files /dev/null and b/contents/box_muller/res/cartesian_rand.png differ diff --git a/contents/box_muller/res/cartesian_rand_output.png b/contents/box_muller/res/cartesian_rand_output.png new file mode 100644 index 000000000..3be2945ce Binary files /dev/null and b/contents/box_muller/res/cartesian_rand_output.png differ diff --git a/contents/box_muller/res/cartesian_rand_transform.png b/contents/box_muller/res/cartesian_rand_transform.png new file mode 100644 index 000000000..08018bf34 Binary files /dev/null and b/contents/box_muller/res/cartesian_rand_transform.png differ diff --git a/contents/box_muller/res/grid.png b/contents/box_muller/res/grid.png new file mode 100644 index 000000000..bc4e80890 Binary files /dev/null and b/contents/box_muller/res/grid.png differ diff --git a/contents/box_muller/res/polar_grid.png b/contents/box_muller/res/polar_grid.png new file mode 100644 index 000000000..36088f773 Binary files /dev/null and b/contents/box_muller/res/polar_grid.png differ diff --git a/contents/box_muller/res/polar_grid_output.png b/contents/box_muller/res/polar_grid_output.png new file mode 100644 index 000000000..5fb774e3f Binary files /dev/null and b/contents/box_muller/res/polar_grid_output.png differ diff --git a/contents/box_muller/res/polar_grid_transform.png b/contents/box_muller/res/polar_grid_transform.png new file mode 100644 index 000000000..1335cf06a Binary files /dev/null and b/contents/box_muller/res/polar_grid_transform.png differ diff --git a/contents/box_muller/res/polar_rand.png b/contents/box_muller/res/polar_rand.png new file mode 100644 index 000000000..e4ff7ac58 Binary files /dev/null and b/contents/box_muller/res/polar_rand.png differ diff --git a/contents/box_muller/res/polar_rand_output.png b/contents/box_muller/res/polar_rand_output.png new file mode 100644 index 000000000..52944893f Binary files /dev/null and b/contents/box_muller/res/polar_rand_output.png differ diff --git a/contents/box_muller/res/polar_rand_transform.png b/contents/box_muller/res/polar_rand_transform.png new file mode 100644 index 000000000..aeefa3e4d Binary files /dev/null and b/contents/box_muller/res/polar_rand_transform.png differ diff --git a/contents/box_muller/res/rand100.png b/contents/box_muller/res/rand100.png new file mode 100644 index 000000000..5a09a248a Binary files /dev/null and b/contents/box_muller/res/rand100.png differ diff --git a/contents/box_muller/res/rand1000.png b/contents/box_muller/res/rand1000.png new file mode 100644 index 000000000..94609737d Binary files /dev/null and b/contents/box_muller/res/rand1000.png differ diff --git a/contents/box_muller/res/rand10000.png b/contents/box_muller/res/rand10000.png new file mode 100644 index 000000000..62adeaa1f Binary files /dev/null and b/contents/box_muller/res/rand10000.png differ diff --git a/contents/box_muller/res/rand_dist.png b/contents/box_muller/res/rand_dist.png new file mode 100644 index 000000000..d56d8f588 Binary files /dev/null and b/contents/box_muller/res/rand_dist.png differ diff --git a/contents/box_muller/res/right_arrow.pdf b/contents/box_muller/res/right_arrow.pdf new file mode 100644 index 000000000..9105474fa Binary files /dev/null and b/contents/box_muller/res/right_arrow.pdf differ diff --git a/contents/box_muller/res/warp_divergence.png b/contents/box_muller/res/warp_divergence.png new file mode 100644 index 000000000..7b385b82c Binary files /dev/null and b/contents/box_muller/res/warp_divergence.png differ diff --git a/contents/computus/computus.md b/contents/computus/computus.md index c3ec51f09..1eeec678d 100644 --- a/contents/computus/computus.md +++ b/contents/computus/computus.md @@ -291,7 +291,7 @@ Sure, this can be done straightforwardly with a calculator, but it is no doubt a Here is a video describing key elements of Gauss's Easter Algorithm:
- +
## Example Code diff --git a/contents/cooley_tukey/code/asm-x64/SConscript b/contents/cooley_tukey/code/asm-x64/SConscript index 05360fe6c..2a10fbc14 100644 --- a/contents/cooley_tukey/code/asm-x64/SConscript +++ b/contents/cooley_tukey/code/asm-x64/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.X64(f'#/build/asm-x64/{dirname}', Glob('*.s'), LIBS=['m'], LINKFLAGS='-no-pie') +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.X64(build_target, str(file_info.path), LIBS='m', LINKFLAGS='-no-pie') + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/contents/cooley_tukey/code/c/SConscript b/contents/cooley_tukey/code/c/SConscript index 2cd13de37..bb40f4a85 100644 --- a/contents/cooley_tukey/code/c/SConscript +++ b/contents/cooley_tukey/code/c/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.C(f'#/build/c/{dirname}', Glob('*.c'), LIBS=['m', 'fftw3']) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.C(build_target, str(file_info.path), LIBS=['m', 'fftw3']) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/contents/cooley_tukey/code/c/fft.c b/contents/cooley_tukey/code/c/fft.c index f87e12afd..212b272b1 100644 --- a/contents/cooley_tukey/code/c/fft.c +++ b/contents/cooley_tukey/code/c/fft.c @@ -11,7 +11,7 @@ void fft(double complex *x, size_t n) { memset(y, 0, sizeof(y)); fftw_plan p; - p = fftw_plan_dft_1d(n, (fftw_complex*)x, (fftw_complex*)y, + p = fftw_plan_dft_1d((int)n, (fftw_complex*)x, (fftw_complex*)y, FFTW_FORWARD, FFTW_ESTIMATE); fftw_execute(p); @@ -27,7 +27,7 @@ void dft(double complex *X, const size_t N) { for (size_t i = 0; i < N; ++i) { tmp[i] = 0; for (size_t j = 0; j < N; ++j) { - tmp[i] += X[j] * cexp(-2.0 * M_PI * I * j * i / N); + tmp[i] += X[j] * cexp(-2.0 * M_PI * I * (double)j * (double)i / (double)N); } } @@ -49,7 +49,7 @@ void cooley_tukey(double complex *X, const size_t N) { cooley_tukey(X + N / 2, N / 2); for (size_t i = 0; i < N / 2; ++i) { - X[i + N / 2] = X[i] - cexp(-2.0 * I * M_PI * i / N) * X[i + N / 2]; + X[i + N / 2] = X[i] - cexp(-2.0 * I * M_PI * (double)i / (double)N) * X[i + N / 2]; X[i] -= (X[i + N / 2]-X[i]); } } @@ -58,7 +58,7 @@ void cooley_tukey(double complex *X, const size_t N) { void bit_reverse(double complex *X, size_t N) { for (size_t i = 0; i < N; ++i) { size_t n = i; - int a = i; + size_t a = i; int count = (int)log2((double)N) - 1; n >>= 1; @@ -67,7 +67,7 @@ void bit_reverse(double complex *X, size_t N) { count--; n >>= 1; } - n = (a << count) & ((1 << (int)log2((double)N)) - 1); + n = (a << count) & (size_t)((1 << (size_t)log2((double)N)) - 1); if (n > i) { double complex tmp = X[i]; @@ -81,8 +81,8 @@ void iterative_cooley_tukey(double complex *X, size_t N) { bit_reverse(X, N); for (int i = 1; i <= log2((double)N); ++i) { - size_t stride = pow(2, i); - double complex w = cexp(-2.0 * I * M_PI / stride); + size_t stride = (size_t)pow(2, i); + double complex w = cexp(-2.0 * I * M_PI / (double)stride); for (size_t j = 0; j < N; j += stride) { double complex v = 1.0; for (size_t k = 0; k < stride / 2; ++k) { @@ -105,7 +105,7 @@ void approx(double complex *X, double complex *Y, size_t N) { } int main() { - srand(time(NULL)); + srand((unsigned int)time(NULL)); double complex x[64], y[64], z[64]; for (size_t i = 0; i < 64; ++i) { x[i] = rand() / (double) RAND_MAX; diff --git a/contents/cooley_tukey/code/cpp/fft.cpp b/contents/cooley_tukey/code/cpp/fft.cpp index 5d4772f10..d4697d1df 100644 --- a/contents/cooley_tukey/code/cpp/fft.cpp +++ b/contents/cooley_tukey/code/cpp/fft.cpp @@ -55,7 +55,7 @@ void cooley_tukey(Iter first, Iter last) { // now combine each of those halves with the butterflies for (int k = 0; k < size / 2; ++k) { - auto w = std::exp(complex(0, -2.0 * pi * k / size)); + auto w = std::exp(complex(0, -2.0 * pi * k / static_cast(size))); auto& bottom = first[k]; auto& top = first[k + size / 2]; @@ -78,7 +78,7 @@ void sort_by_bit_reverse(Iter first, Iter last) { b = (((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2)); b = (((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4)); b = (((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8)); - b = ((b >> 16) | (b << 16)) >> (32 - std::uint32_t(log2(size))); + b = ((b >> 16) | (b << 16)) >> (32 - std::uint32_t(log2(static_cast(size)))); if (b > i) { swap(first[b], first[i]); } diff --git a/contents/cryptography/cryptography.md b/contents/cryptography/cryptography.md new file mode 100644 index 000000000..dd136dae8 --- /dev/null +++ b/contents/cryptography/cryptography.md @@ -0,0 +1,135 @@ +# Cryptography + +Humans have almost always been interested in sending secret messages that only the sender and receiver understand. +The reason for this is obvious: secret messages should remain secret. +The easiest way for this to happen is to talk behind closed doors, but that simply doesn't work if the sender and receiver are separated by a significant distance. +In this case, they need to rely on a messenger or mailman to send the message. + +For simplicity, let's assume they are sending a written letter for the purpose of negotiating war tactics in ancient Greece or Rome. +Obviously, the message can remain secret if both the sender and receiver also trust the messenger; however, what if the messenger is actually an evil spy? +What if the messenger is killed and the letter is stolen? +What if (in an elaborate ruse), some third party slips into the messenger's tent in the dead-of-night and replaces the letter with another one entirely different? + +These are all important questions cryptography addresses. + +The idea is simple: we procedurally scramble the message we are sending and only provide the unscrambling procedure to trusted parties. +In this way, the message would seem like utter gobbledygook to anyone other than the sender and receiver. +It doesn't matter if the messenger is evil. +They cannot read the message anyway. +It's also fine if the message is replaced, because then the receiver won't be able to properly decode the message and can just ask for another message to be sent (probably on another path with a different messenger). +Unsurprisingly, a very early method of encryption was supposedly developed by Julius Caeser and called the "Caesar Cipher" {{ "ceasar_cipher_wiki" | cite }}. +Here, every character in the message is replaced by another character based on some pre-defined table or chart that only the sender and receiver have. +The table is created by simply rotating the alphabet by $$n$$ spaces, where $$n$$ is chosen in a discussion between the sender and receiver before-hand. + + | $$n$$ | 0 | 2 | 14 | 18 | 21 | 24 | + | ----- | - | - | -- | -- | -- | -- | + | a | a | c | o | s | v | y | + | b | b | d | p | t | w | z | + | c | c | e | q | u | x | a | + | d | d | f | r | v | y | b | + | e | e | g | s | w | z | c | + | f | f | h | t | x | a | d | + | g | g | i | u | y | b | e | + | h | h | j | v | z | c | f | + | i | i | k | w | a | d | g | + | j | j | l | x | b | e | h | + | k | k | m | y | c | f | i | + | l | l | n | z | d | g | j | + | m | m | o | a | e | h | k | + | n | n | p | b | f | i | l | + | o | o | q | c | g | j | m | + | p | p | r | d | h | k | n | + | q | q | s | e | i | l | o | + | r | r | t | f | j | m | p | + | s | s | u | g | k | n | q | + | t | t | v | h | l | o | r | + | u | u | w | i | m | p | s | + | v | v | x | j | n | q | t | + | w | w | y | k | o | r | u | + | x | x | z | l | p | s | v | + | y | y | a | m | q | t | w | + | z | z | b | n | r | u | x | + +It is certainly not the most complicated scheme out there, but it is generally the first encryption scheme people come up with when trying to encode secret messages to one another. +Honestly, I remember sending messages back and forth to friends in elementary school, but we would never provide the necessary table to decode the message. +Instead, we would provide enough text that they could find the table themselves from context. +If a bunch of elementary school kids can figure out how to break this encryption scheme, it cannot be too robust. +In fact, it's interesting to see how the field of cryptography has grown since the Caesar cipher was developed. +In the cryptographic literature, there is always a sender, receiver, and eavesdropper. +For some reason beyond my own comprehension, the first two people are almost always given the names Alice (sender) and Bob (receiver). +Meanwhile, the attacker or eavesdropper is usually called either Eve or Charlie +These names are consistent even with quantum cryptography, so they are here to stay. + +In general, there are two different types of encryption: symmetric and asymmetric. +Both of which are described in the following sections. + +Cryptographic systems are a cornerstone to modern information technology and lie at the heart of everything from WiFi to online banking. +If an attacker manages to crack modern cryptographic algorithms, they could cause serious damage. +For this reason, it is important to keep a few things in mind: +* Because cryptography has become such an advanced field cryptographic systems should be analyzed by trained professionals and have undergo extensive testing and vetting. + This means that whenever possible, one should use a widely accepted cryptography library instead of writing their own cypher. +* Kerckhoffs's principle says that when determining the robustness of a cryptographic system it should be assumed that the attacker knows the encryption and decryption algorithm {{ "Kerckhoffs_principle_wiki" | cite }}. + This does not include any pre-shared or secret keys. +* With the advances in technology, cryptography often hits its limits. + Many formerly state-of-the-art hashing algorithms have become obsolete because the computers used to crack them have gotten faster and better. + Another field that cryptography will have to face is [quantum computing](../quantum_information/quantum_information.md). + Quantum computers will have a big impact on cryptography and especially asymmetric cryptography. + This whole set of problems is summarized in the field of post-quantum cryptography {{ "post-quantum_crypto_wiki" | cite }}. + +## Symmetric Cryptography + +Symmetric cryptography is called symmetric because the key that is used is the same for encrypting and decrypting. +For this to work Alice and Bob both need the same key, which they have to share before communicating. +Some examples for symmetric cryptography are: +* **Ceasar Cipher**: Alice and Bob rotate the alphabet by $$n$$ characters and use that as a table to encode and decode their message {{ "ceasar_cipher_wiki" | cite }}. +* **Rot13**: This is a special case of the Caeser Cipher where the alphabet is rotated by 13, hence the name "Rot13" {{ "rot13_wiki" | cite }} +* **Permutation Cipher**: Here we choose a permutation $$\pi$$ (i.e. $$\pi=(3,1,2,4)$$) and reorder the the letters according to that $$\pi$$ which is the key {{ "CC_permutation" | cite }}. +* **XOR encryption**: This method works on bit strings and combines the message and a key of equal bit length with the XOR operator {{ "xor_cipher_wiki" | cite }}. + To decrypt, simply XOR again with the same key. +* **DES or Data Encryption Standard**: This is a newer encryption algorithm which was standardized in 1977 {{ "DES_wiki" | cite }}. + It has since been deemed insecure and is superseded by AES. +* **AES or Advanced Encryption Standard**: The actual algorithm is called "Rijndael" {{ "AES_wiki" | cite }}. + Like with XOR or DES we generate a bit string (depending on which AES you use 128/192 or 256 bit long) which is your key. +* **Blowfish**: This algorithm was also a good contender for the AES but lost to Rijndael {{ "blowfish_cipher_wiki" | cite }}. + +This section is currently a work-in-progress, and all of these methods will have corresponding chapters in the near future. + +## Asymmetric Cryptography + +Asymmetric cryptography is sometimes called "public key cryptography" (or PK cryptography in short) because Bob and Alice both need a shared public key and a private key they keep to themselves. +These algorithms are called asymmetric because what is encrypted with the public key can only be decrypted with the private key and vice versa. +This can be used for a number of different applications, like digital signing, encrypted communication, or secretly sharing keys. +For example, if Alice wants to send a message to Bob and this message has to be kept private, Alice will encrypt the message with Bob's public key. +Now only Bob can decrypt the message again and read it. +If Charlie were to alter Alice's message, Bob couldn't decrypt it anymore. +If Bob wants to make sure the message is actually from Alice, Alice can encrypt the already encrypted message with her private key again. +This is to keep Charlie from sending forged or altered messages since Bob couldn't decrypt that layer with Alice's public key. +Some examples for public key cryptography: +* **RSA**: This algorithm calculates a public and a private key from two very large primes {{ "RSA_wiki" | cite }}. + It is (hopefully) near impossible to factor the product of two such primes in a feasible amount of time. +* **ECC or Elliptic-curve cryptography**: Here you calculate the private and public key from two points on an elliptic curve {{ "ECC_crypto_wiki" | cite }}. + This has the positive side effect that you need smaller numbers than non-ECC algorithms like RSA to achieve the same level of security. + +This section is currently a work-in-progress. These methods will also have corresponding chapters in the near future. + +### Bibliography + +{% references %} {% endreferences %} + + + +## License +The text of this chapter was written by [Liikt](https://github.com/Liikt) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +The code examples are licensed under the MIT license (found in LICENSE.md). + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). + +##### Text + +The text of this chapter was written by [Liikt](https://github.com/Liikt) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) diff --git a/contents/cryptography/res/table.jl b/contents/cryptography/res/table.jl new file mode 100644 index 000000000..314c238c4 --- /dev/null +++ b/contents/cryptography/res/table.jl @@ -0,0 +1,33 @@ +function print_table(a::Array{T, 2}, + header::Vector{String}) where T <: Union{Char, String} + print(" | ") + for i = 1:length(header) + print(string(header[i]), " | ") + end + println() + + print(" | ") + for i = 1:length(header) + print("---", " | ") + end + println() + + for i = 1:size(a)[1] + print(" | ") + for j = 1:size(a)[2] + print(string(a[i,j]), " | ") + end + println() + end +end + +alphabet = [char for char = 'a':'z'] +offsets = Int.([0, 0, 2, 14, 18, 21, 24]) +alphabet_array = Array{Char}(undef, 26, length(offsets)) + +for i = 1:length(offsets) + alphabet_array[:,i] = vcat(alphabet[offsets[i]+1:26],alphabet[1:offsets[i]]) +end + +header = vcat(string.(offsets)) +print_table(alphabet_array, header) diff --git a/contents/domain_coloring/domain_coloring.md b/contents/domain_coloring/domain_coloring.md index 9c536bb50..746c52241 100644 --- a/contents/domain_coloring/domain_coloring.md +++ b/contents/domain_coloring/domain_coloring.md @@ -164,7 +164,7 @@ We will be using domain coloring in other contexts throughout this text when des Here is a video describing domain coloring:
- +
## Example Code diff --git a/contents/euclidean_algorithm/code/asm-x64/euclidean_example.s b/contents/euclidean_algorithm/code/asm-x64/euclidean_example.s index c08bb0878..53a008908 100644 --- a/contents/euclidean_algorithm/code/asm-x64/euclidean_example.s +++ b/contents/euclidean_algorithm/code/asm-x64/euclidean_example.s @@ -1,7 +1,8 @@ .intel_syntax noprefix .section .rodata - fmt: .string "%d\n" + euclid_mod_fmt: .string "[#]\nModulus-based euclidean algorithm result:\n%d\n" + euclid_sub_fmt: .string "[#]\nSubtraction-based euclidean algorithm result:\n%d\n" .section .text .global main @@ -59,14 +60,14 @@ main: mov rdi, 4288 # Call euclid_mod mov rsi, 5184 call euclid_mod - mov rdi, OFFSET fmt # Print output + mov rdi, OFFSET euclid_mod_fmt # Print output mov rsi, rax xor rax, rax call printf mov rdi, 1536 # Call euclid_sub mov rsi, 9856 call euclid_sub - mov rdi, OFFSET fmt # Print output + mov rdi, OFFSET euclid_sub_fmt # Print output mov rsi, rax xor rax, rax call printf diff --git a/contents/euclidean_algorithm/code/bash/euclid.bash b/contents/euclidean_algorithm/code/bash/euclid.bash index bef4b5da0..0ddd49537 100755 --- a/contents/euclidean_algorithm/code/bash/euclid.bash +++ b/contents/euclidean_algorithm/code/bash/euclid.bash @@ -38,6 +38,6 @@ euclid_sub() { } result=$(euclid_mod $((64 * 67)) $((64 * 81))) -echo "$result" +echo -e "[#]\nModulus-based euclidean algorithm result:\n$result" result=$(euclid_sub $((128 * 12)) $((128 * 77))) -echo "$result" +echo -e "[#]\nSubtraction-based euclidean algorithm result:\n$result" diff --git a/contents/euclidean_algorithm/code/c/euclidean_example.c b/contents/euclidean_algorithm/code/c/euclidean_example.c index 12892e1aa..d1c0b69d1 100644 --- a/contents/euclidean_algorithm/code/c/euclidean_example.c +++ b/contents/euclidean_algorithm/code/c/euclidean_example.c @@ -33,8 +33,8 @@ int main() { int check1 = euclid_mod(64 * 67, 64 * 81); int check2 = euclid_sub(128 * 12, 128 * 77); - printf("%d\n", check1); - printf("%d\n", check2); + printf("[#]\nModulus-based euclidean algorithm result:\n%d\n", check1); + printf("[#]\nSubtraction-based euclidean algorithm result:\n%d\n", check2); return 0; } diff --git a/contents/euclidean_algorithm/code/clisp/euclidean.lisp b/contents/euclidean_algorithm/code/clisp/euclidean.lisp index 62f525ac3..19cba7358 100644 --- a/contents/euclidean_algorithm/code/clisp/euclidean.lisp +++ b/contents/euclidean_algorithm/code/clisp/euclidean.lisp @@ -17,8 +17,10 @@ (abs a) (euclid-mod b (mod a b)))) -(print (euclid-sub (* 64 67) (* 64 81))) -(print (euclid-mod (* 128 12) (* 128 77))) +(format T "[#]~%Modulus-based euclidean algorithm result:~%") +(format T "~d~%" (euclid-sub (* 64 67) (* 64 81))) +(format T "[#]~%Subtraction-based euclidean algorithm result:~%") +(format T "~d~%" (euclid-mod (* 128 12) (* 128 77))) ;; Quick test (assert diff --git a/contents/euclidean_algorithm/code/coconut/euclidean.coco b/contents/euclidean_algorithm/code/coconut/euclidean.coco index 8f5b5c9d7..08cb851a1 100644 --- a/contents/euclidean_algorithm/code/coconut/euclidean.coco +++ b/contents/euclidean_algorithm/code/coconut/euclidean.coco @@ -15,5 +15,7 @@ addpattern def euclid_mod(0, b is int) = b addpattern def euclid_mod(a is int, b is int) = euclid_mod(b, a % b) if __name__ == '__main__': - print('Euclidean mod:', euclid_mod(64 * 67, 64 * 81)) - print('Euclidean sub:', euclid_sub(128 * 12, 128 * 77)) + print('[#]\nModulus-based euclidean algorithm result:') + print(euclid_mod(64 * 67, 64 * 81)) + print('[#]\nSubtraction-based euclidean algorithm result:') + print(euclid_sub(128 * 12, 128 * 77)) diff --git a/contents/euclidean_algorithm/code/cpp/euclidean.cpp b/contents/euclidean_algorithm/code/cpp/euclidean.cpp index f7b818802..1f6c04e27 100644 --- a/contents/euclidean_algorithm/code/cpp/euclidean.cpp +++ b/contents/euclidean_algorithm/code/cpp/euclidean.cpp @@ -34,6 +34,6 @@ int main() { auto check1 = euclid_mod(64 * 67, 64 * 81); auto check2 = euclid_sub(128 * 12, 128 * 77); - std::cout << check1 << '\n'; - std::cout << check2 << '\n'; + std::cout << "[#]\nModulus-based euclidean algorithm result:\n" << check1 << '\n'; + std::cout << "[#]\nSubtraction-based euclidean algorithm result:\n" << check2 << '\n'; } diff --git a/contents/euclidean_algorithm/code/csharp/Program.cs b/contents/euclidean_algorithm/code/csharp/Program.cs index edf1edfd4..80857df27 100644 --- a/contents/euclidean_algorithm/code/csharp/Program.cs +++ b/contents/euclidean_algorithm/code/csharp/Program.cs @@ -7,12 +7,13 @@ class Program { static void Main(string[] args) { - Console.WriteLine("EuclideanAlgorithm"); var euclideanAlgorithm = new EuclideanAlgorithm(); int check = euclideanAlgorithm.EuclidMod(64 * 67, 64 * 81); int check2 = euclideanAlgorithm.EuclidSub(128 * 12, 128 * 77); + Console.WriteLine("[#]\nModulus-based euclidean algorithm result:"); Console.WriteLine(check); + Console.WriteLine("[#]\nSubtraction-based euclidean algorithm result:"); Console.WriteLine(check2); } } diff --git a/contents/euclidean_algorithm/code/d/euclidean_algorithm.d b/contents/euclidean_algorithm/code/d/euclidean_algorithm.d index 042a9bae1..585d0aa1b 100644 --- a/contents/euclidean_algorithm/code/d/euclidean_algorithm.d +++ b/contents/euclidean_algorithm/code/d/euclidean_algorithm.d @@ -37,6 +37,6 @@ void main() auto check1 = euclid_mod(64 * 67, 64 * 81); auto check2 = euclid_sub(128 * 12, 128 * 77); - writeln("Modulus-based euclidean algorithm result: ", check1); - writeln("Subtraction-based euclidean algorithm result: ", check2); + writeln("[#]\nModulus-based euclidean algorithm result:\n", check1); + writeln("[#]\nSubtraction-based euclidean algorithm result:\n", check2); } diff --git a/contents/euclidean_algorithm/code/expected.json b/contents/euclidean_algorithm/code/expected.json new file mode 100644 index 000000000..f6b6ffb70 --- /dev/null +++ b/contents/euclidean_algorithm/code/expected.json @@ -0,0 +1,8 @@ +{ + "Description": "euclidean algorithm", + "Delta" : 0.0, + "OutputValues" : [ + "64", + "128" + ] +} diff --git a/contents/euclidean_algorithm/code/fortran/SConscript b/contents/euclidean_algorithm/code/fortran/SConscript deleted file mode 100644 index 8146feee9..000000000 --- a/contents/euclidean_algorithm/code/fortran/SConscript +++ /dev/null @@ -1,6 +0,0 @@ -Import('*') -from pathlib import Path - -dirname = Path.cwd().parents[1].stem - -env.Fortran(f'#/build/fortran/{dirname}', 'euclidean.f90') diff --git a/contents/euclidean_algorithm/code/fortran/euclidean.f90 b/contents/euclidean_algorithm/code/fortran/euclidean.f90 index 3107e4de2..e0dc9610e 100644 --- a/contents/euclidean_algorithm/code/fortran/euclidean.f90 +++ b/contents/euclidean_algorithm/code/fortran/euclidean.f90 @@ -38,12 +38,18 @@ PROGRAM euclidean IMPLICIT NONE INTEGER :: a, b, euclid_sub, euclid_mod - a = 24 - b = 27 - WRITE(*,*) 'Subtraction method: GCD is: ', euclid_sub(a, b) + a = 64 * 67 + b = 64 * 81 - a = 24 - b = 27 - WRITE(*,*) 'Modulus method: GCD is: ', euclid_mod(a, b) + WRITE(*,'(a)') '[#]' + WRITE(*,'(a)') 'Modulus-based euclidean algorithm result:' + WRITE(*, '(g0)') euclid_mod(a, b) + + a = 128 * 12 + b = 128 * 77 + + WRITE(*,'(a)') '[#]' + WRITE(*,'(a)') 'Subtraction-based euclidean algorithm result:' + WRITE(*, '(g0)') euclid_sub(a, b) END PROGRAM euclidean diff --git a/contents/euclidean_algorithm/code/go/euclidean.go b/contents/euclidean_algorithm/code/go/euclidean.go index f457b1849..ea543fe75 100644 --- a/contents/euclidean_algorithm/code/go/euclidean.go +++ b/contents/euclidean_algorithm/code/go/euclidean.go @@ -41,6 +41,8 @@ func main() { check1 := euclidMod(64*67, 64*81) check2 := euclidSub(128*12, 128*77) + fmt.Println("[#]\nModulus-based euclidean algorithm result:") fmt.Println(check1) + fmt.Println("[#]\nSubtraction-based euclidean algorithm result:") fmt.Println(check2) } diff --git a/contents/euclidean_algorithm/code/haskell/euclidean_algorithm.hs b/contents/euclidean_algorithm/code/haskell/euclidean_algorithm.hs index 9227de4e9..917aef7df 100644 --- a/contents/euclidean_algorithm/code/haskell/euclidean_algorithm.hs +++ b/contents/euclidean_algorithm/code/haskell/euclidean_algorithm.hs @@ -31,5 +31,7 @@ main :: IO () main = do let chk1 = euclidMod (64 * 67) (64 * 81) chk2 = euclidSub (128 * 12) (128 * 77) + putStrLn "[#]\nModulus-based euclidean algorithm result:" print chk1 + putStrLn "[#]\nSubtraction-based euclidean algorithm result:" print chk2 diff --git a/contents/euclidean_algorithm/code/java/EuclideanAlgo.java b/contents/euclidean_algorithm/code/java/EuclideanAlgo.java index 95d233f58..ac53379da 100644 --- a/contents/euclidean_algorithm/code/java/EuclideanAlgo.java +++ b/contents/euclidean_algorithm/code/java/EuclideanAlgo.java @@ -26,7 +26,9 @@ public static int euclidMod(int a, int b) { } public static void main(String[] args) { + System.out.println("[#]\nModulus-based euclidean algorithm result:"); System.out.println(euclidMod(64 * 67, 64 * 81)); + System.out.println("[#]\nSubtraction-based euclidean algorithm result:"); System.out.println(euclidSub(128 * 12, 128 * 77)); } } diff --git a/contents/euclidean_algorithm/code/javascript/euclidean_example.js b/contents/euclidean_algorithm/code/javascript/euclidean_example.js index fbaf4bfcc..2199c37dc 100644 --- a/contents/euclidean_algorithm/code/javascript/euclidean_example.js +++ b/contents/euclidean_algorithm/code/javascript/euclidean_example.js @@ -18,14 +18,16 @@ function euclidSub(a, b) { while (a !== b) { if (a > b) { - a -= a - b; + a -= b; } else { - b = b - a; + b -= a; } } return a; } +console.log('[#]\nModulus-based euclidean algorithm result:') console.log(euclidMod(64 * 67, 64 * 81)); +console.log('[#]\nSubtraction-based euclidean algorithm result:') console.log(euclidSub(128 * 12, 128 * 77)); diff --git a/contents/euclidean_algorithm/code/julia/euclidean.jl b/contents/euclidean_algorithm/code/julia/euclidean.jl index 744ae2187..a85f931ae 100644 --- a/contents/euclidean_algorithm/code/julia/euclidean.jl +++ b/contents/euclidean_algorithm/code/julia/euclidean.jl @@ -28,8 +28,8 @@ function main() check1 = euclid_mod(64 * 67, 64 * 81); check2 = euclid_sub(128 * 12, 128 * 77); - println("Modulus-based euclidean algorithm result: $(check1)") - println("subtraction-based euclidean algorithm result: $(check2)") + println("[#]\nModulus-based euclidean algorithm result:\n$(check1)") + println("[#]\nSubtraction-based euclidean algorithm result:\n$(check2)") end diff --git a/contents/euclidean_algorithm/code/kotlin/Euclidean.kt b/contents/euclidean_algorithm/code/kotlin/Euclidean.kt index 9e14c7463..855afcd59 100644 --- a/contents/euclidean_algorithm/code/kotlin/Euclidean.kt +++ b/contents/euclidean_algorithm/code/kotlin/Euclidean.kt @@ -26,6 +26,8 @@ fun euclidMod(a: Int, b: Int): Int { } fun main(args: Array) { - println(euclidSub(128 * 12, 128 * 77)) + println("[#]\nModulus-based euclidean algorithm result:") println(euclidMod(64 * 67, 64 * 81)) -} + println("[#]\nSubtraction-based euclidean algorithm result:") + println(euclidSub(128 * 12, 128 * 77)) +} \ No newline at end of file diff --git a/contents/euclidean_algorithm/code/lua/euclidean.lua b/contents/euclidean_algorithm/code/lua/euclidean.lua index 6b2aa8faa..0149ffd34 100644 --- a/contents/euclidean_algorithm/code/lua/euclidean.lua +++ b/contents/euclidean_algorithm/code/lua/euclidean.lua @@ -25,8 +25,10 @@ local function euclid_mod(a, b) end local function main() - print(euclid_sub(128 * 12, 128 * 77)) + print("[#]\nModulus-based euclidean algorithm result:") print(euclid_mod(64 * 67, 64 * 81)) + print("[#]\nSubtraction-based euclidean algorithm result:") + print(euclid_sub(128 * 12, 128 * 77)) end main() diff --git a/contents/euclidean_algorithm/code/matlab/euclidean.m b/contents/euclidean_algorithm/code/matlab/euclidean.m index de2a63dec..7a9b317f3 100644 --- a/contents/euclidean_algorithm/code/matlab/euclidean.m +++ b/contents/euclidean_algorithm/code/matlab/euclidean.m @@ -31,6 +31,7 @@ end function euclid() - ['gcd(520,420) via euclidSub: ',num2str(euclidSub(520,420))] - ['gcd(183,244) via euclidMod: ',num2str(euclidMod(183,244))] + ['[#] Modulus-based euclidean algorithm result: ',num2str(euclidMod(64 * 67, 64 * 81))] + + ['[#] Subtraction-based euclidean algorithm result: ',num2str(euclidSub(128 * 12, 128 * 77))] end \ No newline at end of file diff --git a/contents/euclidean_algorithm/code/nim/euclid_algorithm.nim b/contents/euclidean_algorithm/code/nim/euclid_algorithm.nim index e74855592..d52b8062a 100644 --- a/contents/euclidean_algorithm/code/nim/euclid_algorithm.nim +++ b/contents/euclidean_algorithm/code/nim/euclid_algorithm.nim @@ -24,5 +24,7 @@ func euclid_sub(in1, in2: int): int = result = a when isMainModule: + echo "[#]\nModulus-based euclidean algorithm result:" echo euclid_sub(64 * 67, 64 * 81) + echo "[#]\nSubtraction-based euclidean algorithm result:" echo euclid_mod(128 * 12, 128 * 77) diff --git a/contents/euclidean_algorithm/code/ocaml/euclidean_example.ml b/contents/euclidean_algorithm/code/ocaml/euclidean_example.ml index 27e3ab166..c363e5e4f 100644 --- a/contents/euclidean_algorithm/code/ocaml/euclidean_example.ml +++ b/contents/euclidean_algorithm/code/ocaml/euclidean_example.ml @@ -19,6 +19,7 @@ let euclid_sub a b = let chk1 = euclid_mod (64 * 67) (64 * 81) let chk2 = euclid_sub (128 * 12) (128 * 77) let () = + Printf.printf "[#]\nModulus-based euclidean algorithm result:\n"; chk1 |> print_int |> print_newline; - chk2 |> print_int |> print_newline - + Printf.printf "[#]\nSubtraction-based euclidean algorithm result:\n"; + chk2 |> print_int |> print_newline \ No newline at end of file diff --git a/contents/euclidean_algorithm/code/php/euclidean.php b/contents/euclidean_algorithm/code/php/euclidean.php index 52aac08c9..cd13e9d74 100644 --- a/contents/euclidean_algorithm/code/php/euclidean.php +++ b/contents/euclidean_algorithm/code/php/euclidean.php @@ -29,7 +29,7 @@ function euclid_mod(int $a, int $b): int return $a; } -printf('Euclidean mod: %s', euclid_mod(64 * 67, 64 * 81)); +printf('[#]'.PHP_EOL.'Modulus-based euclidean algorithm result:'.PHP_EOL.'%s', euclid_mod(64 * 67, 64 * 81)); echo PHP_EOL; -printf('Euclidean sub: %s', euclid_sub(128 * 12, 128 * 77)); +printf('[#]'.PHP_EOL.'Subtraction-based euclidean algorithm result:'.PHP_EOL.'%s', euclid_sub(128 * 12, 128 * 77)); echo PHP_EOL; diff --git a/contents/euclidean_algorithm/code/powershell/euclidean_algorithm.ps1 b/contents/euclidean_algorithm/code/powershell/euclidean_algorithm.ps1 index d717320c8..3e3925ed7 100644 --- a/contents/euclidean_algorithm/code/powershell/euclidean_algorithm.ps1 +++ b/contents/euclidean_algorithm/code/powershell/euclidean_algorithm.ps1 @@ -26,5 +26,5 @@ function Mod-Euclid($a, $b) { return $a } -Write-Host "Subtraction-based euclidean algorithm result: $(Mod-Euclid $(64 * 67) $(64 * 81))" -Write-Host "Modulus-based euclidean algorithm result: $(Sub-Euclid $(128 * 12) $(128 * 77))" +Write-Host "[#]`nSubtraction-based euclidean algorithm result:`n$(Mod-Euclid $(64 * 67) $(64 * 81))" +Write-Host "[#]`nModulus-based euclidean algorithm result:`n$(Sub-Euclid $(128 * 12) $(128 * 77))" diff --git a/contents/euclidean_algorithm/code/python/euclidean_example.py b/contents/euclidean_algorithm/code/python/euclidean_example.py index 0badc2ca8..03d51aa4b 100644 --- a/contents/euclidean_algorithm/code/python/euclidean_example.py +++ b/contents/euclidean_algorithm/code/python/euclidean_example.py @@ -27,5 +27,7 @@ def euclid_sub(a, b): return a if __name__=="__main__": - print('Euclidean mod: ', euclid_mod(64 * 67, 64 * 81)) - print('Euclidean sub: ', euclid_sub(128 * 12, 128 * 77)) + print('[#]\nModulus-based euclidean algorithm result:'), + print(euclid_mod(64 * 67, 64 * 81)) + print('[#]\nSubtraction-based euclidean algorithm result:') + print(euclid_sub(128 * 12, 128 * 77)) diff --git a/contents/euclidean_algorithm/code/racket/euclidean_algorithm.rkt b/contents/euclidean_algorithm/code/racket/euclidean_algorithm.rkt index f170d8e17..8d19eab86 100755 --- a/contents/euclidean_algorithm/code/racket/euclidean_algorithm.rkt +++ b/contents/euclidean_algorithm/code/racket/euclidean_algorithm.rkt @@ -23,5 +23,7 @@ ) ) +(displayln "[#]\nModulus-based euclidean algorithm result:") (displayln (euclid_sub (* 64 67) (* 64 81))) +(displayln "[#]\nSubtraction-based euclidean algorithm result:") (displayln (euclid_mod (* 128 12) (* 128 77))) diff --git a/contents/euclidean_algorithm/code/ruby/euclidean.rb b/contents/euclidean_algorithm/code/ruby/euclidean.rb index b8667ad71..b55bbd728 100644 --- a/contents/euclidean_algorithm/code/ruby/euclidean.rb +++ b/contents/euclidean_algorithm/code/ruby/euclidean.rb @@ -17,9 +17,8 @@ def gcd_minus(a, b) end a end - -p gcd_mod(12 * 6, 12 * 4) #=> 12 -p gcd_mod(9 * 667, 9 * 104) #=> 9 -p gcd_minus(12 * 6, 12 * 4) #=> 12 -p gcd_minus(9 * 667, 9 * 104) #=> 9 +print "[#]\nModulus-based euclidean algorithm result:\n" +p gcd_mod(64 * 67, 64 * 81) +print "[#]\nSubtraction-based euclidean algorithm result:\n" +p gcd_minus(128 * 12, 128 * 77) diff --git a/contents/euclidean_algorithm/code/rust/euclidean_example.rs b/contents/euclidean_algorithm/code/rust/euclidean_example.rs index 89b55ba22..1c9fb55f7 100644 --- a/contents/euclidean_algorithm/code/rust/euclidean_example.rs +++ b/contents/euclidean_algorithm/code/rust/euclidean_example.rs @@ -29,6 +29,6 @@ fn euclid_rem(mut a: i64, mut b: i64) -> i64 { fn main() { let chk1 = euclid_rem(64 * 67, 64 * 81); let chk2 = euclid_sub(128 * 12, 128 * 77); - println!("{}", chk1); - println!("{}", chk2); + println!("[#]\nModulus-based euclidean algorithm result:\n{}", chk1); + println!("[#]\nSubtraction-based euclidean algorithm result:\n{}", chk2); } diff --git a/contents/euclidean_algorithm/code/scala/euclidean.scala b/contents/euclidean_algorithm/code/scala/euclidean.scala index bc3fe103a..25079e603 100644 --- a/contents/euclidean_algorithm/code/scala/euclidean.scala +++ b/contents/euclidean_algorithm/code/scala/euclidean.scala @@ -3,8 +3,8 @@ object Euclid { def euclid_sub(a: Int, b: Int): Int = (Math.abs(a), Math.abs(b)) match { case (0, _) | (_, 0) => 0 - case (x, y) if x < y => euclid(x, y - x) - case (x, y) if x > y => euclid(x - y, y) + case (x, y) if x < y => euclid_sub(x, y - x) + case (x, y) if x > y => euclid_sub(x - y, y) case _ => a } @@ -15,8 +15,10 @@ object Euclid { } def main(args: Array[String]): Unit = { - println(euclid_sub(151 * 899, 151 * 182)) - println(euclid_mod(151 * 899, 151 * 182)) + println("[#]\nModulus-based euclidean algorithm result:") + println(euclid_mod(64 * 67, 64 * 81)) + println("[#]\nSubtraction-based euclidean algorithm result:") + println(euclid_sub(128 * 12, 128 * 77)) } } diff --git a/contents/euclidean_algorithm/code/scheme/euclidalg.ss b/contents/euclidean_algorithm/code/scheme/euclidalg.ss index 959ebdeca..2cda992d8 100644 --- a/contents/euclidean_algorithm/code/scheme/euclidalg.ss +++ b/contents/euclidean_algorithm/code/scheme/euclidalg.ss @@ -11,6 +11,8 @@ a (euclid-mod b (modulo a b)))) +(display "[#]\nModulus-based euclidean algorithm result:") (newline) (display (euclid-mod (* 64 67) (* 64 81))) (newline) -(display (euclid-sub (* 128 12) (* 128 77))) (newline) +(display "[#]\nSubtraction-based euclidean algorithm result:") (newline) +(display (euclid-sub (* 128 12) (* 128 77))) (newline) diff --git a/contents/euclidean_algorithm/code/swift/euclidean_algorithm.swift b/contents/euclidean_algorithm/code/swift/euclidean_algorithm.swift index 7b43959ad..9c2c71448 100644 --- a/contents/euclidean_algorithm/code/swift/euclidean_algorithm.swift +++ b/contents/euclidean_algorithm/code/swift/euclidean_algorithm.swift @@ -27,7 +27,9 @@ func euclidMod(a: Int, b: Int) -> Int { } func main() { + print("[#]\nModulus-based euclidean algorithm result:") print(euclidMod(a: 64 * 67, b: 64 * 81)) + print("[#]\nSubtraction-based euclidean algorithm result:") print(euclidSub(a: 128 * 12, b: 128 * 77)) } diff --git a/contents/euclidean_algorithm/euclidean_algorithm.md b/contents/euclidean_algorithm/euclidean_algorithm.md index cebf550ba..f75f6651f 100644 --- a/contents/euclidean_algorithm/euclidean_algorithm.md +++ b/contents/euclidean_algorithm/euclidean_algorithm.md @@ -189,7 +189,7 @@ The Euclidean Algorithm is truly fundamental to many other algorithms throughout Here's a video on the Euclidean algorithm:
- +
## Example Code diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 3fb4f239c..ed84683a6 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -244,7 +244,7 @@ These will all be covered in subsequent chapters which will come out somewhat re Here is a video describing tree traversal:
- +
## Example Code diff --git a/contents/forward_euler_method/code/asm-x64/SConscript b/contents/forward_euler_method/code/asm-x64/SConscript index 9322fd10c..2a10fbc14 100644 --- a/contents/forward_euler_method/code/asm-x64/SConscript +++ b/contents/forward_euler_method/code/asm-x64/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.X64(f'#/build/asm-x64/{dirname}', Glob('*.s'), LIBS='m', LINKFLAGS='-no-pie') +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.X64(build_target, str(file_info.path), LIBS='m', LINKFLAGS='-no-pie') + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/contents/forward_euler_method/code/c/SConscript b/contents/forward_euler_method/code/c/SConscript index 34a951e7f..b81220a0e 100644 --- a/contents/forward_euler_method/code/c/SConscript +++ b/contents/forward_euler_method/code/c/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.C(f'#/build/c/{dirname}', Glob('*.c'), LIBS='m') +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.C(build_target, str(file_info.path), LIBS='m') + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/contents/forward_euler_method/code/c/euler.c b/contents/forward_euler_method/code/c/euler.c index 9ce095930..211e2f614 100644 --- a/contents/forward_euler_method/code/c/euler.c +++ b/contents/forward_euler_method/code/c/euler.c @@ -13,7 +13,7 @@ void solve_euler(double timestep, double *result, size_t n) { int check_result(double *result, size_t n, double threshold, double timestep) { int is_approx = 1; for (size_t i = 0; i < n; ++i) { - double solution = exp(-3.0 * i * timestep); + double solution = exp(-3.0 * (double)i * timestep); if (fabs(result[i] - solution) > threshold) { printf("%f %f\n", result[i], solution); is_approx = 0; diff --git a/contents/forward_euler_method/code/coconut/euler.coco b/contents/forward_euler_method/code/coconut/euler.coco new file mode 100644 index 000000000..7297e9c51 --- /dev/null +++ b/contents/forward_euler_method/code/coconut/euler.coco @@ -0,0 +1,28 @@ +import math + +def forward_euler(time_step, n): + factors = [1] + [1 - 3 * time_step] * (n-1) + # We want all the cumulative values, thus the use of scan + return scan((*), factors) + + + +def check(result, threshold, time_step): + approx = True + # A scan object has a len if the underlying iterable has a len + solution = range(len(result)) |> map$(i -> math.exp(-3*i*time_step)) + for y, sol in zip(result, solution): + if not math.isclose(y, sol, abs_tol=threshold): + print(y, sol) + approx = False + return approx + + +if __name__ == '__main__': + time_step = 0.01 + n = 100 + threshold = 0.01 + + result = forward_euler(time_step, n) + approx = check(result, threshold, time_step) + print("All values within threshold") if approx else print("Value(s) not in threshold") diff --git a/contents/forward_euler_method/code/cpp/euler.cpp b/contents/forward_euler_method/code/cpp/euler.cpp index a341655f4..0fbeb8426 100644 --- a/contents/forward_euler_method/code/cpp/euler.cpp +++ b/contents/forward_euler_method/code/cpp/euler.cpp @@ -27,7 +27,7 @@ template bool check_result(Iter first, Iter last, double threshold, double timestep) { auto it = first; for (size_t idx = 0; it != last; ++idx, ++it) { - double solution = std::exp(-3.0 * idx * timestep); + double solution = std::exp(-3.0 * static_cast(idx) * timestep); if (std::abs(*it - solution) > threshold) { std::cout << "We found a value outside the threshold; the " << idx << "-th value was " << *it << ", but the expected solution was " diff --git a/contents/forward_euler_method/forward_euler_method.md b/contents/forward_euler_method/forward_euler_method.md index dfb185431..f2f21c8b9 100644 --- a/contents/forward_euler_method/forward_euler_method.md +++ b/contents/forward_euler_method/forward_euler_method.md @@ -96,7 +96,7 @@ That said, variations of this method *are* certainly used (for example Crank-Nic Here is a video describing the forward Euler method:
- +
## Example Code @@ -146,6 +146,8 @@ Full code for the visualization follows: [import, lang:"nim"](code/nim/forwardeuler.nim) {% sample lang="lisp" %} [import, lang="lisp"](code/clisp/euler.lisp) +{%sample lang="coco" %} +[import, lang:"coconut"](code/coconut/euler.coco) {% endmethod %} + +### Bibliography + +{% references %} {% endreferences %} + +## License + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). + + +##### Images/Graphics +- The animation "[Animated Random Walk](res/animated_random_walk.gif)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +- The animation "[Animated Metropolis](res/animated_metropolis.gif)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +- The image "[Plot of P(x)](res/plot_of_P.png)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +- The image "[Multiple Histograms](res/multiple_histograms.png)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +##### Text + +The text of this chapter was written by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + +##### Pull Requests + +After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: +- none diff --git a/contents/metropolis/res/1D_particles.png b/contents/metropolis/res/1D_particles.png new file mode 100644 index 000000000..3d8ab7d99 Binary files /dev/null and b/contents/metropolis/res/1D_particles.png differ diff --git a/contents/metropolis/res/animated_metropolis.gif b/contents/metropolis/res/animated_metropolis.gif new file mode 100644 index 000000000..93356bb7e Binary files /dev/null and b/contents/metropolis/res/animated_metropolis.gif differ diff --git a/contents/metropolis/res/animated_metropolis.mp4 b/contents/metropolis/res/animated_metropolis.mp4 new file mode 100644 index 000000000..68bfc36ec Binary files /dev/null and b/contents/metropolis/res/animated_metropolis.mp4 differ diff --git a/contents/metropolis/res/animated_random_walk.gif b/contents/metropolis/res/animated_random_walk.gif new file mode 100644 index 000000000..f80bc11b0 Binary files /dev/null and b/contents/metropolis/res/animated_random_walk.gif differ diff --git a/contents/metropolis/res/animated_random_walk.mp4 b/contents/metropolis/res/animated_random_walk.mp4 new file mode 100644 index 000000000..d7825ec3c Binary files /dev/null and b/contents/metropolis/res/animated_random_walk.mp4 differ diff --git a/contents/metropolis/res/multiple_histograms.png b/contents/metropolis/res/multiple_histograms.png new file mode 100644 index 000000000..9180253bc Binary files /dev/null and b/contents/metropolis/res/multiple_histograms.png differ diff --git a/contents/metropolis/res/plot_of_P.png b/contents/metropolis/res/plot_of_P.png new file mode 100644 index 000000000..3e4f3d10b Binary files /dev/null and b/contents/metropolis/res/plot_of_P.png differ diff --git a/contents/monte_carlo_integration/code/c/monte_carlo.c b/contents/monte_carlo_integration/code/c/monte_carlo.c index 9920ff55c..14f823fe4 100644 --- a/contents/monte_carlo_integration/code/c/monte_carlo.c +++ b/contents/monte_carlo_integration/code/c/monte_carlo.c @@ -24,7 +24,7 @@ double monte_carlo(unsigned int samples) { } int main() { - srand(time(NULL)); + srand((unsigned int)time(NULL)); double estimate = monte_carlo(1000000); diff --git a/contents/monte_carlo_integration/code/cpp/monte_carlo.cpp b/contents/monte_carlo_integration/code/cpp/monte_carlo.cpp index f38d83f45..4a600d72e 100644 --- a/contents/monte_carlo_integration/code/cpp/monte_carlo.cpp +++ b/contents/monte_carlo_integration/code/cpp/monte_carlo.cpp @@ -37,8 +37,6 @@ double monte_carlo_pi(unsigned samples) { } int main() { - unsigned samples; - double pi_estimate = monte_carlo_pi(10000000); std::cout << "Pi = " << pi_estimate << '\n'; std::cout << "Percent error is: " << 100 * std::abs(pi_estimate - PI) / PI << " %\n"; diff --git a/contents/monte_carlo_integration/monte_carlo_integration.md b/contents/monte_carlo_integration/monte_carlo_integration.md index c01a6cbbd..5a96fc1ef 100644 --- a/contents/monte_carlo_integration/monte_carlo_integration.md +++ b/contents/monte_carlo_integration/monte_carlo_integration.md @@ -125,7 +125,7 @@ I can guarantee that we will see similar methods crop up all over the place in t Here is a video describing Monte Carlo integration:
- +
## Example Code diff --git a/contents/notation/notation.md b/contents/notation/notation.md index f8242ddde..b8155bcb6 100644 --- a/contents/notation/notation.md +++ b/contents/notation/notation.md @@ -21,10 +21,28 @@ In addition, there are many different notations depending on who you ask, but fo Big $$O$$ assumes the worst, which is the often the most useful description of an algorithm. On the other hand, $$\Omega$$ assumes the best and $$\Theta$$ is used when the best and worst cases are the same. +It *may* seems like strange that an algorithm can run in different time, but let me explain a while: +```julia +function constant(a::UInt64, b::UInt64) + println(b) + for i=0:18446744073709551615 + if(a < b) + b = a - b + println(b) + end + end +end +``` +If we calculates the big 3 in b, it will be $$\Omega(1)$$ and $$O(b)$$, Obviously not the same. +The best-case runtime will be $$1$$ `println` statement if a > b, the worst-case runtime will be $$b$$ `println` statement if a = 1. +So that's the explanation, and let's move on. + Of the three Big $$O$$ is used the most, and is used in conversation to mean that the algorithm will take "on the order of" $$n$$ operations. Unfortunately, at this point, these notations might be a little vague. In fact, it was incredibly vague for me for a long time, and it wasn't until I saw the notations in action that it all started to make sense, so that's what this section is about: providing concrete examples to better understand computational complexity notation. +######In algorithms below, let us consider that the *slowest* statement is `println`, and we always talk about all the `println` in the function. + ## Constant Time Let's write some code that reads in an array of length `n` and runs with constant time: @@ -137,9 +155,10 @@ Here is a simple example of a function with exponential runtime: # Here, n is the number of iterations function exponential(value::Int64, n::Int64) println(value) - value += 1 - exponential(value, n-1) - exponential(value, n-1) + if(n >= 0) + value += 1 + exponential(value, n-1) + exponential(value, n-1) end ``` @@ -152,7 +171,6 @@ Instead of taking a value and computing more and more values each time, a good e # Here, cutoff is an arbitrary variable to know when to stop recursing function logarithmic(a::Array{Float64}, cutoff::Int64) if (length(a) > cutoff) - logarithmic(a[1:length(a)/2], cutoff) logarithmic(a[length(a)/2+1:end], cutoff) end println(length(a)) @@ -160,8 +178,8 @@ end ``` To be honest, it is not obvious that the provided `logarithmic` function should operate in $$\Theta(\log(n))$$ time, where $$n$$ is the size of `a`. That said, I encourage you to think about an array of size 8. -First, we split it in half, creating 2 arrays of 4 elements each. -If we split these new arrays, we have 4 arrays of 2, and if we split these by two we have 8 arrays of 1 element each. +First, we split it in half and run the algorithm on one of them, creating an array of 4 elements. +If we split the new array and run it on 1 of them, we have an array of 2, and if we split it by two and run on 1 we have an array of 1 element each. This is as far as we can go, and we ended up dividing the array 3 times to get to this point. $$3 = \log_2(8)$$, so this function runs with a logarithmic number of operations. diff --git a/contents/probability_distributions/distributions.md b/contents/probability_distributions/distributions.md new file mode 100644 index 000000000..aba490e42 --- /dev/null +++ b/contents/probability_distributions/distributions.md @@ -0,0 +1,214 @@ +# What's a probability distribution? + +Probability distributions are mathematical functions that give the probabilities of a range or set of outcomes. +These outcomes can be the result of an experiment or procedure, such as tossing a coin or rolling dice. +They can also be the result of a physical measurement, such as measuring the temperature of an object, counting how many electrons are spin up, etc. +Broadly speaking, we can classify probability distributions into two categories - __discrete probability distributions__ and __continuous probability distributions__. + +## Discrete Probability Distributions + +It's intuitive for us to understand what a __discrete__ probability distribution is. +For example, we understand the outcomes of a coin toss very well, and also that of a dice roll. +For a single coin toss, we know that the probability of getting heads $$(H)$$ is half, or $$P(H) = \frac{1}{2}$$. +Similarly, the probability of getting tails $$(T)$$ is $$P(T) = \frac{1}{2}$$. +Formally, we can write the probability distribution for such a coin toss as, + +$$ +P(n) = \begin{matrix} + \displaystyle \frac 1 2 &;& n \in \left\{H,T\right\}. + \end{matrix} +$$ + +Here, $$n$$ denotes the outcome, and we used the "set notation", $$n \in\left\{H,T\right\}$$, which means "$$n$$ belongs to a set containing $$H$$ and $$T$$". +From the above equation, we can also assume that any other outcome for $$n$$ (such as landing on an edge) is incredibly unlikely, impossible, or simply "not allowed" (for example, just toss again if it _does_ land on its edge!). + +For a probability distribution, it's important to take note of the set of possibilities, or the __domain__ of the distribution. +Here, $$\left\{H,T\right\}$$ is the domain of $$P(n)$$, telling us that $$n$$ can only be either $$H$$ or $$T$$. + +If we use a different system, the outcome $$n$$ could mean other things. +For example, it could be a number like the outcome of a __die roll__ which has the probability distribution, + + +$$ +P(n) = \begin{matrix} + \displaystyle\frac 1 6 &;& n \in [\![1,6]\!] + \end{matrix}. +$$ +This is saying that the probability of $$n$$ being a whole number between $$1$$ and $$6$$ is $$1/6$$, and we assume that the probability of getting any other $$n$$ is $$0$$. +This is a discrete probability function because $$n$$ is an integer, and thus only takes discrete values. + +Both of the above examples are rather boring, because the value of $$P(n)$$ is the same for all $$n$$. +An example of a discrete probability function where the probability actually depends on $$n$$, is when $$n$$ is the sum of numbers on a __roll of two dice__. +In this case, $$P(n)$$ is different for each $$n$$ as some possibilities like $$n=2$$ can happen in only one possible way (by getting a $$1$$ on both dice), whereas $$n=4$$ can happen in $$3$$ ways ($$1$$ and $$3$$; or $$2$$ and $$2$$; or $$3$$ and $$1$$). + +The example of rolling two dice is a great case study for how we can construct a probability distribution, since the probability varies and it is not immediately obvious how it varies. +So let's go ahead and construct it! + +Let's first define the domain of our target $$P(n)$$. +We know that the lowest sum of two dice is $$2$$ (a $$1$$ on both dice), so $$n \geq 2$$ for sure. Similarly, the maximum is the sum of two sixes, or $$12$$, so $$n \leq 12$$ also. + +So now we know the domain of possibilities, i.e., $$n \in [\![2,12]\!]$$. +Next, we take a very common approach - for each outcome $$n$$, we count up the number of different ways it can occur. +Let's call this number the __frequency of__ $$n$$, $$f(n)$$. +We already mentioned that there is only one way to get $$n=2$$, by getting a pair of $$1$$s. +By our definition of the function $$f$$, this means that $$f(2)=1$$. +For $$n=3$$, we see that there are two possible ways of getting this outcome: the first die shows a $$1$$ and the second a $$2$$, or the first die shows a $$2$$ and the second a $$1$$. +Thus, $$f(3)=2$$. +If you continue doing this for all $$n$$, you may see a pattern (homework for the reader!). +Once you have all the $$f(n)$$, we can visualize it by plotting $$f(n)$$ vs $$n$$, as shown below. + +

+ <FIG> Die Roll +

+ +We can see from the plot that the most common outcome for the sum of two dice is a $$n=7$$, and the further away from $$n=7$$ you get, the less likely the outcome. +Good to know, for a prospective gambler! + +### Normalization + +The $$f(n)$$ plotted above is technically NOT the probability $$P(n)$$ – because we know that the sum of all probabilities should be $$1$$, which clearly isn't the case for $$f(n)$$. +But we can get the probability by dividing $$f(n)$$ by the _total_ number of possibilities, $$N$$. +For two dice, that is $$N = 6 \times 6 = 36$$, but we could also express it as the _sum of all frequencies_, + +$$ +N = \sum_n f(n), +$$ + +which would also equal to $$36$$ in this case. +So, by dividing $$f(n)$$ by $$\sum_n f(n)$$ we get our target probability distribution, $$P(n)$$. +This process is called __normalization__ and is crucial for determining almost any probability distribution. +So in general, if we have the function $$f(n)$$, we can get the probability as + +$$ +P(n) = \frac{f(n)}{\displaystyle\sum_{n} f(n)}. +$$ + +Note that $$f(n)$$ does not necessarily have to be the frequency of $$n$$ – it could be any function which is _proportional_ to $$P(n)$$, and the above definition of $$P(n)$$ would still hold. +It's easy to check that the sum is now equal to $$1$$, since + +$$ +\sum_n P(n) = \frac{\displaystyle\sum_{n}f(n)}{\displaystyle\sum_{n} f(n)} = 1. +$$ + +Once we have the probability function $$P(n)$$, we can calculate all sorts of probabilites. +For example, let's say we want to find the probability that $$n$$ will be between two integers $$a$$ and $$b$$, inclusively (also including $$a$$ and $$b$$). +For brevity, we will use the notation $$\mathbb{P}(a \leq n \leq b)$$ to denote this probability. +And to calculate it, we simply have to sum up all the probabilities for each value of $$n$$ in that range, i.e., + +$$ +\mathbb{P}(a \leq n \leq b) = \sum_{n=a}^{b} P(n). +$$ + +## Probability Density Functions + +What if instead of a discrete variable $$n$$, we had a continuous variable $$x$$, like temperature or weight? +In that case, it doesn't make sense to ask what the probability is of $$x$$ being _exactly_ a particular number – there are infinite possible real numbers, after all, so the probability of $$x$$ being exactly any one of them is essentially zero! +But it _does_ make sense to ask what the probability is that $$x$$ will be _between_ a certain range of values. +For example, one might say that there is $$50\%$$ chance that the temperature tomorrow at noon will be between $$5$$ and $$15$$, or $$5\%$$ chance that it will be between $$16$$ and $$16.5$$. +But how do we put all that information, for every possible range, in a single function? +The answer is to use a __probability density function__. + + What does that mean? +Well, suppose $$x$$ is a continous quantity, and we have a probability density function, $$P(x)$$ which looks like + +

+ <FIG> probability density +

+ +Now, if we are interested in the probability of the range of values that lie between $$x_0$$ and $$x_0 + dx$$, all we have to do is calculate the _area_ of the green sliver above. +This is the defining feature of a probability density function: + + __the probability of a range of values is the _area_ of the region under the probability density curve which is within that range.__ + + +So if $$dx$$ is infinitesimally small, then the area of the green sliver becomes $$P(x)dx$$, and hence, + +$$ +\mathbb{P}(x_0 \leq x \leq x_0 + dx) = P(x)dx. +$$ + +So strictly speaking, $$P(x)$$ itself is NOT a probability, but rather the probability is the quantity $$P(x)dx$$, or any area under the curve. +That is why we call $$P(x)$$ the probability _density_ at $$x$$, while the actual probability is only defined for ranges of $$x$$. + +Thus, to obtain the probability of $$x$$ lying within a range, we simply integrate $$P(x)$$ between that range, i.e., + +$$ +\mathbb{P}(a \leq x \leq b ) = \int_a^b P(x)dx. +$$ + +This is analagous to finding the probability of a range of discrete values from the previous section: + +$$ +\mathbb{P}(a \leq n \leq b) = \sum_{n=a}^{b} P(n). +$$ + +The fact that all probabilities must sum to $$1$$ translates to + +$$ +\int_D P(x)dx = 1. +$$ + +where $$D$$ denotes the __domain__ of $$P(x)$$, i.e., the entire range of possible values of $$x$$ for which $$P(x)$$ is defined. + +### Normalization of a Density Function + +Just like in the discrete case, we often first calculate some density or frequency function $$f(x)$$, which is NOT $$P(x)$$, but proportional to it. +We can get the probability density function by normalizing it in a similar way, except that we integrate instead of sum: + +$$ +P(\mathbf{x}) = \frac{f(\mathbf{x})}{\int_D f(\mathbf{x})d\mathbf{x}}. +$$ + +For example, consider the following __Gaussian function__ (popularly used in __normal distributions__), + +$$ +f(x) = e^{-x^2}, +$$ + +which is defined for all real numbers $$x$$. +We first integrate it (or do a quick google search, as it is rather tricky) to get + +$$ +N = \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}. +$$ + +Now we have a Gaussian probability distribution, + +$$ +P(x) = \frac{1}{N} e^{-x^2} = \frac{1}{\sqrt{\pi}} e^{-x^2}. +$$ + +In general, normalization can allow us to create a probability distribution out of almost any function $$f(\mathbf{x})$$. +There are really only two rules that $$f(\mathbf{x})$$ must satisfy to be a candidate for a probability density distribution: +1. The integral of $$f(\mathbf{x})$$ over any subset of $$D$$ (denoted by $$S$$) has to be non-negative (it can be zero): +$$ +\int_{S}f(\mathbf{x})d\mathbf{x} \geq 0. +$$ +2. The following integral must be finite: +$$ +\int_{D} f(\mathbf{x})d\mathbf{x}. +$$ + + + +## License + +##### Images/Graphics + +- The image "[Frequency distribution of a double die roll](res/double_die_frequencies.png)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +- The image "[Probability Density](res/normal_distribution.png)" was created by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +##### Text + +The text of this chapter was written by [K. Shudipto Amin](https://github.com/shudipto-amin) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + +##### Pull Requests + +After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: +- none + diff --git a/contents/probability_distributions/res/double_die_frequencies.png b/contents/probability_distributions/res/double_die_frequencies.png new file mode 100644 index 000000000..874633331 Binary files /dev/null and b/contents/probability_distributions/res/double_die_frequencies.png differ diff --git a/contents/probability_distributions/res/normal_distribution.png b/contents/probability_distributions/res/normal_distribution.png new file mode 100644 index 000000000..36ea67790 Binary files /dev/null and b/contents/probability_distributions/res/normal_distribution.png differ diff --git a/contents/split-operator_method/code/c/SConscript b/contents/split-operator_method/code/c/SConscript index 2cd13de37..bb40f4a85 100644 --- a/contents/split-operator_method/code/c/SConscript +++ b/contents/split-operator_method/code/c/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.C(f'#/build/c/{dirname}', Glob('*.c'), LIBS=['m', 'fftw3']) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.C(build_target, str(file_info.path), LIBS=['m', 'fftw3']) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/contents/split-operator_method/code/c/split_op.c b/contents/split-operator_method/code/c/split_op.c index ecf48e027..fd5a84001 100644 --- a/contents/split-operator_method/code/c/split_op.c +++ b/contents/split-operator_method/code/c/split_op.c @@ -34,10 +34,10 @@ void fft(double complex *x, size_t n, bool inverse) { fftw_plan p; if (inverse) { - p = fftw_plan_dft_1d(n, (fftw_complex*)x, (fftw_complex*)y, + p = fftw_plan_dft_1d((int)n, (fftw_complex*)x, (fftw_complex*)y, FFTW_BACKWARD, FFTW_ESTIMATE); } else { - p = fftw_plan_dft_1d(n, (fftw_complex*)x, (fftw_complex*)y, + p = fftw_plan_dft_1d((int)n, (fftw_complex*)x, (fftw_complex*)y, FFTW_FORWARD, FFTW_ESTIMATE); } @@ -63,9 +63,9 @@ void init_params(struct params *par, double xmax, unsigned int res, double dt, par->im_time = im; for (size_t i = 0; i < res; ++i) { - par->x[i] = xmax / res - xmax + i * (2.0 * xmax / res); + par->x[i] = xmax / res - xmax + (double)i * (2.0 * xmax / res); if (i < res / 2) { - par->k[i] = i * M_PI / xmax; + par->k[i] = (double)i * M_PI / xmax; } else { par->k[i] = ((double)i - res) * M_PI / xmax; } diff --git a/contents/split-operator_method/code/cpp/SConscript b/contents/split-operator_method/code/cpp/SConscript index a25ed8c91..f9ec1b545 100644 --- a/contents/split-operator_method/code/cpp/SConscript +++ b/contents/split-operator_method/code/cpp/SConscript @@ -1,6 +1,6 @@ -Import('*') -from pathlib import Path +Import('files_to_compile env') -dirname = Path.cwd().parents[1].stem - -env.CPlusPlus(f'#/build/cpp/{dirname}', Glob('*.cpp'), LIBS=['m', 'fftw3']) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.CPlusPlus(build_target, str(file_info.path), LIBS=['m', 'fftw3']) + env.Alias(str(file_info.chapter), build_result) diff --git a/contents/split-operator_method/code/cpp/split_op.cpp b/contents/split-operator_method/code/cpp/split_op.cpp index 74f8df2b7..bb160c901 100644 --- a/contents/split-operator_method/code/cpp/split_op.cpp +++ b/contents/split-operator_method/code/cpp/split_op.cpp @@ -28,9 +28,9 @@ struct Params { im_time = im; for (size_t i = 0; i < res; ++i) { - x.emplace_back(xmax / res - xmax + i * (2.0 * xmax / res)); + x.emplace_back(xmax / res - xmax + static_cast(i) * (2.0 * xmax / res)); if (i < res / 2) { - k.push_back(i * M_PI / xmax); + k.push_back(static_cast(i) * M_PI / xmax); } else { k.push_back((static_cast(i) - res) * M_PI / xmax); } @@ -85,7 +85,7 @@ void fft(vector_complex &x, bool inverse) { fftw_complex *in = reinterpret_cast(x.data()); fftw_complex *out = reinterpret_cast(y.data()); - p = fftw_plan_dft_1d(x.size(), in, out, + p = fftw_plan_dft_1d(static_cast(x.size()), in, out, (inverse ? FFTW_BACKWARD : FFTW_FORWARD), FFTW_ESTIMATE); fftw_execute(p); @@ -97,7 +97,7 @@ void fft(vector_complex &x, bool inverse) { } void split_op(Params &par, Operators &opr) { - double density[opr.size]; + auto density = std::vector(opr.size, 0); for (size_t i = 0; i < par.timesteps; ++i) { for (size_t j = 0; j < opr.size; ++j) { @@ -142,7 +142,7 @@ void split_op(Params &par, Operators &opr) { std::ofstream fstream = std::ofstream(filename_stream.str()); if (fstream) { - for (int i = 0; i < opr.size; ++i) { + for (std::size_t i = 0; i < opr.size; ++i) { std::stringstream data_stream; data_stream << i << "\t" << density[i] << "\t" << real(opr.v[i]) << "\n"; diff --git a/contents/split-operator_method/split-operator_method.md b/contents/split-operator_method/split-operator_method.md index 1af23e331..08fc3d33d 100644 --- a/contents/split-operator_method/split-operator_method.md +++ b/contents/split-operator_method/split-operator_method.md @@ -172,7 +172,7 @@ The Split-Operator method is one of the most commonly used quantum simulation al Here is a video describing the split-operator method:
- +
## Example Code diff --git a/contents/stable_marriage_problem/code/c/stable_marriage.c b/contents/stable_marriage_problem/code/c/stable_marriage.c index e4f4ad2fd..8537e82f2 100644 --- a/contents/stable_marriage_problem/code/c/stable_marriage.c +++ b/contents/stable_marriage_problem/code/c/stable_marriage.c @@ -5,7 +5,7 @@ #include struct person { - int id; + size_t id; struct person *partner; size_t *prefers; size_t index; @@ -13,7 +13,7 @@ struct person { void shuffle(size_t *array, size_t size) { for (size_t i = size - 1; i > 0; --i) { - size_t j = rand() % (i + 1); + size_t j = (size_t)rand() % (i + 1); size_t tmp = array[i]; array[i] = array[j]; array[j] = tmp; @@ -82,7 +82,7 @@ void free_group(struct person *group, size_t size) { } int main() { - srand(time(NULL)); + srand((unsigned int)time(NULL)); struct person men[5], women[5]; @@ -114,7 +114,7 @@ int main() { printf("\n"); for (size_t i = 0; i < 5; ++i) { - printf("the partner of man %zu is woman %d\n", i, men[i].partner->id); + printf("the partner of man %zu is woman %ld\n", i, men[i].partner->id); } free_group(men, 5); diff --git a/contents/stable_marriage_problem/stable_marriage_problem.md b/contents/stable_marriage_problem/stable_marriage_problem.md index dc7f031ab..f6a3e5b2a 100644 --- a/contents/stable_marriage_problem/stable_marriage_problem.md +++ b/contents/stable_marriage_problem/stable_marriage_problem.md @@ -23,7 +23,7 @@ I am incredibly interested to see what you guys do and how you implement the alg Here is a video describing the stable marriage problem:
- +
## Example Code diff --git a/contents/stacks_and_queues/code/cpp/queue.cpp b/contents/stacks_and_queues/code/cpp/queue.cpp new file mode 100644 index 000000000..009abbd06 --- /dev/null +++ b/contents/stacks_and_queues/code/cpp/queue.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +namespace my { + /** + * implementation using linked list + * [value][next] -> [value][next] -> ... -> [value][next] + * (front Node) (intermediat Nodes) (rear Node) + */ + template + struct Node { + /** + * next: will store right Node address + */ + T value; + std::shared_ptr> next; + Node(const T& V) : value(V) { } + }; + + template + class queue { + private: + /** + * front_pointer: points to left most node + * count: keeps track of current number of elements present in queue + * rear_pointer: points to most recent Node added into the queue, which is right most Node + */ + std::shared_ptr> front_pointer; + std::shared_ptr> rear_pointer; + size_t count; + public: + queue() : count(0ULL) { } + + void enqueue(const T& element) { + auto new_node = std::make_shared>(element); + if (count > 0) { + rear_pointer->next = new_node; + rear_pointer = new_node; + } else { + rear_pointer = front_pointer = new_node; + } + count = count + 1; + } + + void dequeue() { + if (count > 1) { + front_pointer = front_pointer->next; + count = count - 1; + } else if (count == 1) { + front_pointer.reset(); + rear_pointer.reset(); + count = count - 1; + } + } + + T& front() { + assert(count > 0 && "calling front on an empty queue"); + return front_pointer->value; + } + + T const& front() const { + assert(count > 0 && "calling front on an empty queue"); + return front_pointer->value; + } + + size_t size() const { return count; } + + bool empty() const { return count == 0; } + + ~queue() { + while (front_pointer.get() != nullptr) { + front_pointer = front_pointer->next; + } + } + }; +} + +int main() { + my::queue intQueue; + intQueue.enqueue(4); + intQueue.enqueue(5); + intQueue.enqueue(9); + + int frontElement = intQueue.front(); + intQueue.dequeue(); + std::cout << frontElement << '\n'; + std::cout << intQueue.size() << '\n'; + std::cout << intQueue.front() << '\n'; + return 0; +} diff --git a/contents/stacks_and_queues/code/cpp/stack.cpp b/contents/stacks_and_queues/code/cpp/stack.cpp new file mode 100644 index 000000000..0d13beda3 --- /dev/null +++ b/contents/stacks_and_queues/code/cpp/stack.cpp @@ -0,0 +1,84 @@ +#include +#include +#include + +namespace my { + /** + * implementation using linked list + * [value][next] -> [value][next] -> ... -> [value][next] + * (top Node) (intermediat Nodes) + * left most Node represents top element of stack + */ + template + struct Node { + /** + * next: will store right Node address + */ + T value; + std::unique_ptr> next; + Node(const T& V) : value(V) { } + }; + + template + class stack { + private: + /** + * top_pointer: points to left most node + * count: keeps track of current number of elements present in stack + */ + std::unique_ptr> top_pointer; + size_t count; + public: + stack() : count(0ULL) { } + + void push(const T& element) { + auto new_node = std::make_unique>(element); + new_node->next = std::move(top_pointer); + top_pointer = std::move(new_node); + count = count + 1; + } + + void pop() { + if (count > 0) { + top_pointer = std::move(top_pointer->next); + count = count - 1; + } + } + + T& top() { + assert(count > 0 and "calling top() on an empty stack"); + return top_pointer->value; + } + // returning mutable reference can very be usefull if someone wants to modify top element + + T const& top() const { + assert(count > 0 and "calling top() on an empty stack"); + return top_pointer->value; + } + + size_t size() const { return count; } + + bool empty() const { return count == 0; } + + ~stack() { + while (top_pointer.get() != nullptr) { + top_pointer = std::move(top_pointer->next); + } + } + }; +} + +int main() { + my::stack intStack; + + intStack.push(4); + intStack.push(5); + intStack.push(9); + + int topElement = intStack.top(); + intStack.pop(); + std::cout << topElement << '\n'; + std::cout << intStack.size() << '\n'; + std::cout << intStack.top() << '\n'; + return 0; +} diff --git a/contents/stacks_and_queues/code/java/Queue.java b/contents/stacks_and_queues/code/java/QueueTest.java similarity index 96% rename from contents/stacks_and_queues/code/java/Queue.java rename to contents/stacks_and_queues/code/java/QueueTest.java index bb349ec6d..2bf8bbe33 100644 --- a/contents/stacks_and_queues/code/java/Queue.java +++ b/contents/stacks_and_queues/code/java/QueueTest.java @@ -43,7 +43,7 @@ interface IQueue { } -public class Queue implements IQueue { +class Queue implements IQueue { private List list; diff --git a/contents/stacks_and_queues/code/java/Stack.java b/contents/stacks_and_queues/code/java/StackTest.java similarity index 96% rename from contents/stacks_and_queues/code/java/Stack.java rename to contents/stacks_and_queues/code/java/StackTest.java index 2d65a0e59..07c96d066 100644 --- a/contents/stacks_and_queues/code/java/Stack.java +++ b/contents/stacks_and_queues/code/java/StackTest.java @@ -42,7 +42,7 @@ interface IStack { } -public class Stack implements IStack { +class Stack implements IStack { private List list; diff --git a/contents/stacks_and_queues/code/rust/SConscript b/contents/stacks_and_queues/code/rust/SConscript index 0c3f15913..343755fa8 100644 --- a/contents/stacks_and_queues/code/rust/SConscript +++ b/contents/stacks_and_queues/code/rust/SConscript @@ -3,8 +3,6 @@ from pathlib import Path dirname = Path.cwd().parents[1].stem -env.rustc(f'#/build/rust/stack', '#/contents/stacks_and_queues/code/rust/Stack.rs') -env.Clean('rust', f'#/build/rust/stack.pdb') +env.rustc(f'#/build/rust/{dirname}/stack', '#/contents/stacks_and_queues/code/rust/Stack.rs') -env.rustc(f'#/build/rust/queue', '#/contents/stacks_and_queues/code/rust/Queue.rs') -env.Clean('rust', f'#/build/rust/queue.pdb') \ No newline at end of file +env.rustc(f'#/build/rust/{dirname}/queue', '#/contents/stacks_and_queues/code/rust/Queue.rs') diff --git a/contents/stacks_and_queues/stacks_and_queues.md b/contents/stacks_and_queues/stacks_and_queues.md index a3c46b478..b695f53bd 100644 --- a/contents/stacks_and_queues/stacks_and_queues.md +++ b/contents/stacks_and_queues/stacks_and_queues.md @@ -19,7 +19,9 @@ Here is a simple implementation of a stack: {% sample lang="ts" %} [import, lang:"typescript"](code/typescript/stack.ts) {% sample lang="java" %} -[import, lang:"java"](code/java/Stack.java) +[import, lang:"java"](code/java/StackTest.java) +{% sample lang = "cpp"%} +[import, lang:"cpp"](code/cpp/stack.cpp) {% sample lang="rust" %} [import, lang:"rust"](code/rust/Stack.rs) {% endmethod %} @@ -29,7 +31,9 @@ Here is a simple implementation of a queue: {% sample lang="ts" %} [import, lang:"typescript"](code/typescript/queue.ts) {% sample lang="java" %} -[import, lang:"java" ](code/java/Queue.java) +[import, lang:"java" ](code/java/QueueTest.java) +{% sample lang = "cpp"%} +[import, lang:"cpp"](code/cpp/queue.cpp) {% sample lang="rust" %} [import, lang:"rust" ](code/rust/Queue.rs) {% endmethod %} diff --git a/contents/thomas_algorithm/code/c/thomas.c b/contents/thomas_algorithm/code/c/thomas.c index 3aed94e8e..415d31d97 100644 --- a/contents/thomas_algorithm/code/c/thomas.c +++ b/contents/thomas_algorithm/code/c/thomas.c @@ -16,7 +16,7 @@ void thomas(double * const a, double * const b, double * const c, x[i] = (x[i] - a[i] * x[i - 1]) * scale; } - for (int i = size - 2; i >= 0; --i) { + for (size_t i = size - 2; i < size - 1; --i) { x[i] -= y[i] * x[i + 1]; } } diff --git a/contents/thomas_algorithm/code/cpp/thomas.cpp b/contents/thomas_algorithm/code/cpp/thomas.cpp index 72ddd70db..a96bf2f8b 100644 --- a/contents/thomas_algorithm/code/cpp/thomas.cpp +++ b/contents/thomas_algorithm/code/cpp/thomas.cpp @@ -1,43 +1,45 @@ +#include #include #include -#include -void thomas(std::vector const a, std::vector const b, std::vector const c, std::vector& x) { - int size = a.size(); - double y[size]; - memset(y, 0, size * sizeof(double)); - - y[0] = c[0] / b[0]; - x[0] = x[0] / b[0]; - - for (size_t i = 1; i < size; ++i) { - double scale = 1.0 / (b[i] - a[i] * y[i - 1]); - y[i] = c[i] * scale; - x[i] = (x[i] - a[i] * x[i - 1]) * scale; - } - - for (int i = size - 2; i >= 0; --i) { - x[i] -= y[i] * x[i + 1]; - } +void thomas( + std::vector const& a, + std::vector const& b, + std::vector const& c, + std::vector& x) { + auto y = std::vector(a.size(), 0.0); + + y[0] = c[0] / b[0]; + x[0] = x[0] / b[0]; + + for (std::size_t i = 1; i < a.size(); ++i) { + const auto scale = 1.0 / (b[i] - a[i] * y[i - 1]); + y[i] = c[i] * scale; + x[i] = (x[i] - a[i] * x[i - 1]) * scale; + } + + for (std::size_t i = a.size() - 2; i < a.size(); --i) { + x[i] -= y[i] * x[i + 1]; + } } int main() { - std::vector a = {0.0, 2.0, 3.0}; - std::vector b = {1.0, 3.0, 6.0}; - std::vector c = {4.0, 5.0, 0.0}; - std::vector x = {7.0, 5.0, 3.0}; + const std::vector a = {0.0, 2.0, 3.0}; + const std::vector b = {1.0, 3.0, 6.0}; + const std::vector c = {4.0, 5.0, 0.0}; + std::vector x = {7.0, 5.0, 3.0}; - std::cout << "The system" << std::endl; - std::cout << "[1.0 4.0 0.0][x] = [7.0]" << std::endl; - std::cout << "[2.0 3.0 5.0][y] = [5.0]" << std::endl; - std::cout << "[0.0 3.0 6.0][z] = [3.0]" << std::endl; - std::cout << "has the solution" << std::endl; + std::cout << "The system\n"; + std::cout << "[1.0 4.0 0.0][x] = [7.0]\n"; + std::cout << "[2.0 3.0 5.0][y] = [5.0]\n"; + std::cout << "[0.0 3.0 6.0][z] = [3.0]\n"; + std::cout << "has the solution:\n"; - thomas(a, b, c, x); + thomas(a, b, c, x); - for (size_t i = 0; i < 3; ++i) { - std::cout << "[" << x[i] << "]" << std::endl; - } + for (auto const& val : x) { + std::cout << "[" << val << "]\n"; + } - return 0; + return 0; } diff --git a/contents/tree_traversal/code/python/tree_traversal.py b/contents/tree_traversal/code/python/tree_traversal.py index 735837bdd..1c15eae23 100644 --- a/contents/tree_traversal/code/python/tree_traversal.py +++ b/contents/tree_traversal/code/python/tree_traversal.py @@ -46,31 +46,18 @@ def dfs_recursive_inorder_btree(node): def dfs_stack(node): - stack = [] - stack.append(node) - - temp = None - - while len(stack) > 0: - print(stack[-1].data, end=' ') - temp = stack.pop() - - for child in temp.children: - stack.append(child) - + stack = [node] + while stack: + node = stack.pop() + stack.extend(node.children) + print(node.data, end=' ') def bfs_queue(node): - queue = [] - queue.append(node) - - temp = None - - while len(queue) > 0: - print(queue[0].data, end=' ') - temp = queue.pop(0) - - for child in temp.children: - queue.append(child) + queue = [node] + while queue: + node = queue.pop(0) + queue.extend(node.children) + print(node.data) def main(): diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 566aca469..b0e829b1b 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -231,7 +231,7 @@ In code, it looks like this: {% sample lang="js" %} [import:53-60, lang:"javascript"](code/javascript/tree.js) {% sample lang="py" %} -[import:48-59, lang:"python"](code/python/tree_traversal.py) +[import:48-53, lang:"python"](code/python/tree_traversal.py) {% sample lang="scratch" %}

@@ -284,7 +284,7 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can {% sample lang="js" %} [import:62-69, lang:"javascript"](code/javascript/tree.js) {% sample lang="py" %} -[import:62-72, lang:"python"](code/python/tree_traversal.py) +[import:55-60, lang:"python"](code/python/tree_traversal.py) {% sample lang="scratch" %}

@@ -320,7 +320,7 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can Here is a video describing tree traversal:

- +
## Example Code diff --git a/contents/verlet_integration/verlet_integration.md b/contents/verlet_integration/verlet_integration.md index 5d993b4f4..ad75814d4 100644 --- a/contents/verlet_integration/verlet_integration.md +++ b/contents/verlet_integration/verlet_integration.md @@ -64,13 +64,24 @@ Here is what it looks like in code: [import:3-13, lang:"lisp"](code/clisp/verlet.lisp) {% endmethod %} -Now, obviously this poses a problem; what if we want to calculate a term that requires velocity, like the kinetic energy, $$\frac{1}{2}mv^2$$? In this case, we certainly cannot get rid of the velocity! Well, we can find the velocity to $$\mathcal{O}(\Delta t^2)$$ accuracy by using the Stormer-Verlet method, which is the same as before, but we calculate velocity like so +Now, obviously this poses a problem; what if we want to calculate a term that requires velocity, like the kinetic energy, $$\frac{1}{2}mv^2$$? In this case, we certainly cannot get rid of the velocity! Well, we can find the velocity to $$\mathcal{O}(\Delta t^2)$$ accuracy by using the Stormer-Verlet method. +We have the equations for $$x(t+\Delta t)$$ and $$x(t-\Delta t)$$ above, so let's start there. +If we subtract the latter from the former, we get the following: $$ -v(t) = \frac{x(t+\Delta t) - x(t-\Delta t)}{2\Delta t} + \mathcal{O}(\Delta t^2) +x(t+\Delta t) - x(t - \Delta t) = 2v(t)\Delta t + \frac{1}{3}b(t)\Delta t^3. $$ -Note that the 2 in the denominator appears because we are going over 2 timesteps. It's essentially solving $$v=\frac{\Delta x}{\Delta t}$$. In addition, we can calculate the velocity of the next timestep like so +When we solve for $$v(t)$$, we get + +$$ +\begin{align} +v(t) &= \frac{x(t+\Delta t) - x(t-\Delta t)}{2\Delta t} + \frac{b(t) \Delta t^3}{3 \Delta t} \\ +v(t) &= \frac{x(t+\Delta t) - x(t-\Delta t)}{2\Delta t} + \mathcal{O}(\Delta t^2). +\end{align} +$$ + +Note that the 2 in the denominator makes sense because we are going over 2 timesteps. It's essentially solving $$v=\frac{\Delta x}{\Delta t}$$. In addition, we can calculate the velocity of the next timestep like so $$ v(t+\Delta t) = \frac{x(t+\Delta t) - x(t)}{\Delta t} + \mathcal{O}(\Delta t) @@ -176,14 +187,14 @@ Here is the velocity Verlet method in code: [import:28-35, lang:"lisp"](code/clisp/verlet.lisp) {% endmethod %} -Even though this method is more widely used than the simple Verlet method mentioned above, it unforunately has an error term of $$\mathcal{O}(\Delta t^2)$$, which is two orders of magnitude worse. That said, if you want to have a simulaton with many objects that depend on one another --- like a gravity simulation --- the Velocity Verlet algorithm is a handy choice; however, you may have to play further tricks to allow everything to scale appropriately. These types of simulatons are sometimes called *n-body* simulations and one such trick is the Barnes-Hut algorithm, which cuts the complexity of n-body simulations from $$\sim \mathcal{O}(n^2)$$ to $$\sim \mathcal{O}(n\log(n))$$. +Even though this method is more widely used than the simple Verlet method mentioned above, it unfortunately has an error term of $$\mathcal{O}(\Delta t^2)$$, which is two orders of magnitude worse. That said, if you want to have a simulation with many objects that depend on one another --- like a gravity simulation --- the Velocity Verlet algorithm is a handy choice; however, you may have to play further tricks to allow everything to scale appropriately. These types of simulations are sometimes called *n-body* simulations and one such trick is the Barnes-Hut algorithm, which cuts the complexity of n-body simulations from $$\sim \mathcal{O}(n^2)$$ to $$\sim \mathcal{O}(n\log(n))$$. ## Video Explanation Here is a video describing Verlet integration:
- +
## Example Code diff --git a/literature.bib b/literature.bib index 2f22c0dc8..80e55f787 100644 --- a/literature.bib +++ b/literature.bib @@ -346,3 +346,198 @@ @misc{3b1b_finger_count url={https://youtu.be/1SMmc9gQmHQ}, year={2015} } + +#------------------------------------------------------------------------------# +# Metropolis # +#------------------------------------------------------------------------------# + +@article{metropolis1953equation, +author = {Metropolis,Nicholas and Rosenbluth,Arianna W. and Rosenbluth,Marshall N. and Teller,Augusta H. and Teller,Edward }, +title = {Equation of State Calculations by Fast Computing Machines}, +journal = {The Journal of Chemical Physics}, +volume = {21}, +number = {6}, +pages = {1087-1092}, +year = {1953}, +doi = {10.1063/1.1699114}, + +URL = { + https://doi.org/10.1063/1.1699114 + +}, +eprint = { + https://doi.org/10.1063/1.1699114 + +} + +} + +@article{rosenthal2011optimal, + title={Optimal proposal distributions and adaptive MCMC}, + author={Rosenthal, Jeffrey S and others}, + journal={Handbook of Markov Chain Monte Carlo}, + volume={4}, + number={10.1201}, + year={2011}, + publisher={Chapman & Hall/CRC Boca Raton, FL} +} + +@article{gareth2001optimal, +author = {Gareth O. Roberts and Jeffrey S. Rosenthal}, +title = {Optimal scaling for various Metropolis-Hastings algorithms}, +volume = {16}, +journal = {Statistical Science}, +number = {4}, +publisher = {Institute of Mathematical Statistics}, +pages = {351 -- 367}, +year = {2001}, +doi = {10.1214/ss/1015346320}, +URL = {https://doi.org/10.1214/ss/1015346320} +} + +@misc{potential_energy_wiki, + title={Wikipedia: Potential Energy}, + url={https://en.wikipedia.org/wiki/Potential_energy}, + year={2021} +} + +@misc{ludwig_boltzmann_wiki, + title={Wikipedia: Ludwig Boltzmann}, + url={https://en.wikipedia.org/wiki/Ludwig_Boltzmann}, + year={2021} +} + +@misc{boltzmann_distribution_wiki, + title={Wikipedia: Boltzmann distribution}, + url={https://en.wikipedia.org/wiki/Boltzmann_distribution}, + year={2021} +} + +@article{hastings1970monte, + author = {Hastings, W. K.}, + title = "Monte Carlo sampling methods using Markov chains and their applications", + journal = {Biometrika}, + volume = {57}, + number = {1}, + pages = {97-109}, + year = {1970}, + month = {04}, + abstract = "{A generalization of the sampling method introduced by Metropolis et al. (1953) is presented along with an exposition of the relevant theory, techniques of application and methods and difficulties of assessing the error in Monte Carlo estimates. Examples of the methods, including the generation of random orthogonal matrices and potential applications of the methods to numerical problems arising in statistics, are discussed.}", + issn = {0006-3444}, + doi = {10.1093/biomet/57.1.97}, + url = {https://doi.org/10.1093/biomet/57.1.97}, + eprint = {https://academic.oup.com/biomet/article-pdf/57/1/97/23940249/57-1-97.pdf}, +} + +@misc{mala_wiki, + title={Wikipedia: Metropolis-adjusted Langevin Algorithm}, + url={https://en.wikipedia.org/wiki/Metropolis-adjusted_Langevin_algorithm}, + year={2021} +} + +@misc{hmc_wiki, + title={Wikipedia: Hamiltonian Monte Carlo}, + url={https://en.wikipedia.org/wiki/Hamiltonian_Monte_Carlo}, + year={2021} +} + +@misc{rmsd_wiki, + title={Wikipedia: Root Mean Square Deviation}, + url={https://en.wikipedia.org/wiki/Root-mean-square_deviation}, + year={2021} +} + +#------------------------------------------------------------------------------# +# Cryptography +#------------------------------------------------------------------------------# + +@misc{ceasar_cipher_wiki, + title={Wikipedia: Ceasar Cipher}, + url={https://en.wikipedia.org/wiki/Caesar_cipher}, + year={2022} +} + +@misc{post-quantum_crypto_wiki, + title={Wikipedia: Post-quantum Cryptography}, + url={https://en.wikipedia.org/wiki/Post-quantum_cryptography}, + year={2022} +} + +@misc{Kerckhoffs_principle_wiki, + title={Wikipedia: Kerckhoffs's principle}, + url={https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle}, + year={2022} +} + +@misc{rot13_wiki, + title={Wikipedia: ROT13}, + url={https://en.wikipedia.org/wiki/ROT13}, + year={2022} +} + +@misc{CC_permutation, + title={Crypto Corner: Permutation Cipher}, + url={https://crypto.interactive-maths.com/permutation-cipher.html}, + year={2022} +} + +@misc{xor_cipher_wiki, + title={Wikipedia: XOR cipher}, + url={https://en.wikipedia.org/wiki/XOR_cipher}, + year={2022} +} + +@misc{DES_wiki, + title={Wikipedia: Data Encryption Standard}, + url={https://en.wikipedia.org/wiki/Data_Encryption_Standard}, + year={2022} +} + +@misc{AES_wiki, + title={Wikipedia: Advanced Encryption Standard}, + url={https://en.wikipedia.org/wiki/Advanced_Encryption_Standard}, + year={2022} +} + +@misc{blowfish_cipher_wiki, + title={Wikipedia: Blowfish (cipher)}, + url={https://en.wikipedia.org/wiki/Blowfish_(cipher)}, + year={2022} +} + +@misc{RSA_wiki, + title={Wikipedia: RSA (cryptosystem)}, + url={https://en.wikipedia.org/wiki/RSA_(cryptosystem)}, + year={2022} +} + +@misc{ECC_crypto_wiki, + title={Wikipedia: Elliptic-curve cryptography}, + url={https://en.wikipedia.org/wiki/Elliptic-curve_cryptography}, + year={2022} +} + +#------------------------------------------------------------------------------# +# BOX--MULLER +#------------------------------------------------------------------------------# + +@article{box1958, + title={A note on the generation of random normal deviates}, + author={Box, George EP}, + journal={Ann. Math. Statist.}, + volume={29}, + pages={610--611}, + year={1958} +} + +@misc{marsaglia_wiki, + title={Wikipedia: Marsaglia Transform}, + url={https://en.wikipedia.org/wiki/Marsaglia_polar_method}, + year={2022} +} + +@misc{box_muller_wiki, + title={Wikipedia: Box-Muller Transform}, + url={https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform}, + year={2022} +} diff --git a/redirects.json b/redirects.json index 2e2858568..0415c8b20 100644 --- a/redirects.json +++ b/redirects.json @@ -139,6 +139,10 @@ { "from": "chapters/QI/QI.html", "to": "contents/quantum_information/quantum_information.html" + }, + { + "from": "chapters/cryptography/cryptography.html", + "to": "contents/cryptography/cryptography.html" } ] } diff --git a/sconscripts/asm-x64_SConscript b/sconscripts/asm-x64_SConscript index caabf226f..ba90ee330 100644 --- a/sconscripts/asm-x64_SConscript +++ b/sconscripts/asm-x64_SConscript @@ -1,6 +1,6 @@ -Import('files_to_compile language env') -from pathlib import Path +Import('files_to_compile env') -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - env.X64(f'#/build/{language}/{chapter_name}', str(file), LINKFLAGS='-no-pie') +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.X64(build_target, str(file_info.path), LINKFLAGS='-no-pie') + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/bash_SConscript b/sconscripts/bash_SConscript new file mode 100644 index 000000000..0b3c17dc0 --- /dev/null +++ b/sconscripts/bash_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.bash' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/c_SConscript b/sconscripts/c_SConscript index a0cbffd95..ebc6aeead 100644 --- a/sconscripts/c_SConscript +++ b/sconscripts/c_SConscript @@ -1,6 +1,6 @@ Import('files_to_compile env') -from pathlib import Path -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - env.C(f'#/build/c/{chapter_name}', str(file)) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.C(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/coconut_SConscript b/sconscripts/coconut_SConscript new file mode 100644 index 000000000..9325cfe24 --- /dev/null +++ b/sconscripts/coconut_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.py' + build_result = env.Coconut(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/cpp_SConscript b/sconscripts/cpp_SConscript index a30e08652..f1ae9f974 100644 --- a/sconscripts/cpp_SConscript +++ b/sconscripts/cpp_SConscript @@ -1,6 +1,6 @@ Import('files_to_compile env') -from pathlib import Path -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - env.CPlusPlus(f'#/build/cpp/{chapter_name}', str(file)) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.CPlusPlus(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/fortran_SConscript b/sconscripts/fortran_SConscript index df3e7fc27..ab85e9f06 100644 --- a/sconscripts/fortran_SConscript +++ b/sconscripts/fortran_SConscript @@ -1,6 +1,6 @@ Import('files_to_compile env') -from pathlib import Path -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - env.Fortran(f'#/build/fortran/{chapter_name}', str(file)) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.Fortran(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/go_SConscript b/sconscripts/go_SConscript index 795be1d53..d521574a9 100644 --- a/sconscripts/go_SConscript +++ b/sconscripts/go_SConscript @@ -1,6 +1,6 @@ Import('files_to_compile env') -from pathlib import Path -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - env.Go(f'#/build/go/{chapter_name}', str(file)) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.Go(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/java_SConscript b/sconscripts/java_SConscript new file mode 100644 index 000000000..78f87d8a4 --- /dev/null +++ b/sconscripts/java_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}' + build_result = env.Java(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/javascript_SConscript b/sconscripts/javascript_SConscript new file mode 100644 index 000000000..767f5cd4a --- /dev/null +++ b/sconscripts/javascript_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.js' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/julia_SConscript b/sconscripts/julia_SConscript new file mode 100644 index 000000000..84de28959 --- /dev/null +++ b/sconscripts/julia_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.jl' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/kotlin_SConscript b/sconscripts/kotlin_SConscript new file mode 100644 index 000000000..81748b367 --- /dev/null +++ b/sconscripts/kotlin_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + build_result = env.KotlinJar(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/lolcode_SConscript b/sconscripts/lolcode_SConscript new file mode 100644 index 000000000..241565f53 --- /dev/null +++ b/sconscripts/lolcode_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.lol' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/lua_SConscript b/sconscripts/lua_SConscript new file mode 100644 index 000000000..a64415d74 --- /dev/null +++ b/sconscripts/lua_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.lua' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/php_SConscript b/sconscripts/php_SConscript new file mode 100644 index 000000000..9748a1a5a --- /dev/null +++ b/sconscripts/php_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.php' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/powershell_SConscript b/sconscripts/powershell_SConscript new file mode 100644 index 000000000..bbb4a9b1a --- /dev/null +++ b/sconscripts/powershell_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.ps1' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/python_SConscript b/sconscripts/python_SConscript new file mode 100644 index 000000000..0d26a9620 --- /dev/null +++ b/sconscripts/python_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.py' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/ruby_SConscript b/sconscripts/ruby_SConscript new file mode 100644 index 000000000..ad58ed64a --- /dev/null +++ b/sconscripts/ruby_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.rb' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file diff --git a/sconscripts/rust_SConscript b/sconscripts/rust_SConscript index b9cf669c7..893877a67 100644 --- a/sconscripts/rust_SConscript +++ b/sconscripts/rust_SConscript @@ -1,14 +1,15 @@ Import('files_to_compile env') -from pathlib import Path -for file in files_to_compile: - chapter_name = file.parent.parent.parent.stem - if (file.parent / 'Cargo.toml').exists(): - env.cargo(target=f'#/build/rust/{chapter_name}', - source=str(file), - MANIFEST=str(file.parent / 'Cargo.toml'), - SOURCE_DIR=str(file.parent)) - env.Clean('rust', str(file.parent / 'target')) +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}' + if (file_info.path.parent / 'Cargo.toml').exists(): + build_result = env.cargo(target=build_target, + source=str(file_info.path), + MANIFEST=str(file_info.path.parent / 'Cargo.toml'), + SOURCE_DIR=str(file_info.path.parent)) + env.Clean('rust', str(file_info.path.parent / 'target')) else: - env.rustc(f'#/build/rust/{chapter_name}', str(file)) - env.Clean('rust', f'#/build/rust/{chapter_name}.pdb') + build_result = env.rustc(build_target, str(file_info.path)) + env.Clean('rust', f'{build_target}.pdb') + + env.Alias(str(file_info.chapter), build_result) diff --git a/sconscripts/viml_SConscript b/sconscripts/viml_SConscript new file mode 100644 index 000000000..bef5029f4 --- /dev/null +++ b/sconscripts/viml_SConscript @@ -0,0 +1,6 @@ +Import('files_to_compile env') + +for file_info in files_to_compile: + build_target = f'#/build/{file_info.language}/{file_info.chapter}/{file_info.path.stem}.vim' + build_result = env.Copier(build_target, str(file_info.path)) + env.Alias(str(file_info.chapter), build_result) \ No newline at end of file