diff --git a/.gitignore b/.gitignore index 2e22839..d4ee5df 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ venv __pycache__ *.ignore .mypy_cache +.DS_Store # Output files /output @@ -10,7 +11,7 @@ __pycache__ *.pdf # TeX build files -*.synctex.gz* +*.synctex* *.latexmk *.aux *.out diff --git a/lib/tex/ormc_handout.cls b/lib/tex/ormc_handout.cls index 98da07c..832a81b 100755 --- a/lib/tex/ormc_handout.cls +++ b/lib/tex/ormc_handout.cls @@ -1,18 +1,3 @@ -% Copyright (C) 2023 Mark (mark@betalupi.com) -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with this program. If not, see . - \NeedsTeXFormat{LaTeX2e} \ProvidesClass{../../../lib/tex/ormc_handout}[2023/05/29 2.0.2 ORMC Handout] diff --git a/lib/typst/local/handout/0.1.0/header.typ b/lib/typst/local/handout/0.1.0/header.typ index a46cc67..21f2b78 100644 --- a/lib/typst/local/handout/0.1.0/header.typ +++ b/lib/typst/local/handout/0.1.0/header.typ @@ -44,6 +44,14 @@ by = text(size: 10pt, [Prepared by #by on #date]) } + let sub = () + if (by != none) { + sub.push(by) + } + if (subtitle != none) { + sub.push(subtitle) + } + // Main title align( center, @@ -61,8 +69,7 @@ // Title text(size: 20pt, title), // Subtitle - if (by != none) { text(size: 10pt, by) }, - if (subtitle != none) { text(size: 10pt, subtitle) }, + ..sub, line(length: 100%, stroke: 0.2mm), ), ), diff --git a/lib/typst/local/handout/0.1.0/lib.typ b/lib/typst/local/handout/0.1.0/lib.typ index 06eddc9..43e503e 100755 --- a/lib/typst/local/handout/0.1.0/lib.typ +++ b/lib/typst/local/handout/0.1.0/lib.typ @@ -3,8 +3,14 @@ // Re-exports // All functions that maybe used by client code are listed here #import "misc.typ": * -#import "object.typ": problem, definition, theorem -#import "solution.typ": if_solutions, if_no_solutions, solution +#import "object.typ": problem, definition, theorem, example, remark, generic +#import "solution.typ": ( + if_solutions, + if_no_solutions, + if_solutions_else, + solution, + instructornote, +) /// Main handout wrapper. diff --git a/lib/typst/local/handout/0.1.0/object.typ b/lib/typst/local/handout/0.1.0/object.typ index 7d4d09b..ed38683 100644 --- a/lib/typst/local/handout/0.1.0/object.typ +++ b/lib/typst/local/handout/0.1.0/object.typ @@ -98,3 +98,11 @@ #let theorem = _mkobj("Theorem") #let example = _mkobj("Example") #let remark = _mkobj("Remark") + +#let generic(obj_content) = { + block( + above: 8mm, + below: 2mm, + text(weight: "bold", obj_content), + ) +} diff --git a/lib/typst/local/handout/0.1.0/solution.typ b/lib/typst/local/handout/0.1.0/solution.typ index abb54b2..2af06c2 100755 --- a/lib/typst/local/handout/0.1.0/solution.typ +++ b/lib/typst/local/handout/0.1.0/solution.typ @@ -1,4 +1,4 @@ -#import "misc.typ": ored +#import "misc.typ": ored, oblue /// If false, hide instructor info. /// @@ -27,6 +27,10 @@ if not show_solutions { content } } +#let if_solutions_else(if_yes, if_no) = { + if show_solutions { if_yes } else { if_no } +} + #let solution(content) = { if_solutions( align( @@ -54,3 +58,31 @@ ), ) } + +#let instructornote(content) = { + if_solutions( + align( + center, + stack( + block( + width: 100%, + breakable: false, + fill: oblue, + stroke: oblue + 2pt, + inset: 1.5mm, + align(left, text(fill: white, weight: "bold", [Instructor note:])), + ), + + block( + width: 100%, + height: auto, + breakable: false, + fill: oblue.lighten(80%).desaturate(10%), + stroke: oblue + 2pt, + inset: 3mm, + align(left, content), + ), + ), + ), + ) +} diff --git a/src/Advanced/Fast Inverse Root/main.typ b/src/Advanced/Fast Inverse Root/main.typ new file mode 100644 index 0000000..e36d184 --- /dev/null +++ b/src/Advanced/Fast Inverse Root/main.typ @@ -0,0 +1,31 @@ +#import "@local/handout:0.1.0": * + +// Bonus: +// - Floats vs fixed point +// - Float density +// - Find non-floatable rational numbers +// - What if we use `n`-bit floats? + +#show: doc => handout( + doc, + group: "Advanced 2", + title: [Fast Inverse Square Root], + by: "Mark", +) + +#include "parts/00 intro.typ" +#pagebreak() + +#include "parts/01 int.typ" +#pagebreak() + +#include "parts/02 float.typ" +#pagebreak() + +#include "parts/03 approx.typ" +#pagebreak() + +#include "parts/04 quake.typ" +#pagebreak() + +#include "parts/05 bonus.typ" diff --git a/src/Advanced/Fast Inverse Root/meta.toml b/src/Advanced/Fast Inverse Root/meta.toml new file mode 100644 index 0000000..5a1bdbd --- /dev/null +++ b/src/Advanced/Fast Inverse Root/meta.toml @@ -0,0 +1,7 @@ +[metadata] +title = "Fast Inverse Square Root" + + +[publish] +handout = true +solutions = true diff --git a/src/Advanced/Fast Inverse Root/parts/00 intro.typ b/src/Advanced/Fast Inverse Root/parts/00 intro.typ new file mode 100644 index 0000000..d0ca1b5 --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/00 intro.typ @@ -0,0 +1,45 @@ +#import "@local/handout:0.1.0": * + += Introduction + +In 2005, ID Software published the source code of _Quake III Arena_, a popular game released in 1999. \ +This caused quite a stir: ID Software was responsible for many games popular among old-school engineers (most notably _Doom_, which has a place in programmer humor even today). + +#v(2mm) + +Naturally, this community immediately began dissecting _Quake_'s source. \ +One particularly interesting function is reproduced below, with original comments: \ + +#v(3mm) + +```c +float Q_rsqrt( float number ) { + long i; + float x2, y; + const float threehalfs = 1.5F; + + x2 = number * 0.5F; + y = number; + i = * ( long * ) &y; // evil floating point bit level hacking + i = 0x5f3759df - ( i >> 1 ); // [redacted] + y = * ( float * ) &i; + y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration +// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed + + return y; +} +``` + +#v(3mm) + +This code defines a function `Q_sqrt`, which was used as a fast approximation of the inverse square root in graphics routines. (in other words, `Q_sqrt` efficiently approximates $1 div sqrt(x)$) + +#v(3mm) + +The key word here is "fast": _Quake_ ran on very limited hardware, and traditional approximation techniques (like Taylor series)#footnote[Taylor series aren't used today, and for the same reason. There are better ways.] were too computationally expensive to be viable. + +#v(3mm) + +Our goal today is to understand how `Q_sqrt` works. \ +To do that, we'll first need to understand how computers represent numbers. \ +We'll start with simple binary integers---turn the page. diff --git a/src/Advanced/Fast Inverse Root/parts/01 int.typ b/src/Advanced/Fast Inverse Root/parts/01 int.typ new file mode 100644 index 0000000..0bc07bc --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/01 int.typ @@ -0,0 +1,102 @@ +#import "@local/handout:0.1.0": * + += Integers + +#definition() +A _bit string_ is a string of binary digits. \ +In this handout, we'll denote bit strings with the prefix `0b`. \ +#note[This prefix is only notation---it is _not_ part of the string itself.] \ +For example, $1001$ is the number "one thousand and one," while $#text([`0b1001`])$ is the string of bits "1 0 0 1". + +#v(2mm) +We will separate long bit strings with underscores for readability. \ +Underscores have no meaning: $#text([`0b1111_0000`]) = #text([`0b11110000`])$. + +#problem() +What is the value of the following bit strings, if we interpret them as integers in base 2? +- `0b0001_1010` +- `0b0110_0001` + +#solution([ + - $#text([`0b0001_1010`]) = 2 + 8 + 16 = 26$ + - $#text([`0b0110_0001`]) = 1 + 32 + 64 = 95$ +]) + +#v(1fr) + +#definition() +We can interpret a bit string in any number of ways. \ +One such interpretation is the _unsigned integer_, or `uint` for short. \ +`uint`s allow us to represent positive (hence "unsigned") integers using 32-bit strings. + +#v(2mm) + +The value of a `uint` is simply its value as a binary number: +- $#text([`0b00000000_00000000_00000000_00000000`]) = 0$ +- $#text([`0b00000000_00000000_00000000_00000011`]) = 3$ +- $#text([`0b00000000_00000000_00000000_00100000`]) = 32$ +- $#text([`0b00000000_00000000_00000000_10000010`]) = 130$ + +#problem() +What is the largest number we can represent with a 32-bit `uint`? + +#solution([ + $#text([`0b11111111_11111111_11111111_11111111`]) = 2^(32)-1$ +]) + +#v(1fr) +#pagebreak() + +#problem() +Find the value of each of the following 32-bit unsigned integers: +- `0b00000000_00000000_00000101_00111001` +- `0b00000000_00000000_00000001_00101100` +- `0b00000000_00000000_00000100_10110000` +#hint([The third conversion is easy---look carefully at the second.]) + +#instructornote[ + Consider making a list of the powers of two $>= 1024$ on the board. +] + +#solution([ + - $#text([`0b00000000_00000000_00000101_00111001`]) = 1337$ + - $#text([`0b00000000_00000000_00000001_00101100`]) = 300$ + - $#text([`0b00000000_00000000_00000010_01011000`]) = 1200$ + Notice that the third int is the second shifted left twice (i.e, multiplied by 4) +]) + +#v(1fr) + + +#definition() +In general, fast division of `uints` is difficult#footnote([One may use repeated subtraction, but this isn't efficient.]). \ +Division by powers of two, however, is incredibly easy: \ +To divide by two, all we need to do is shift the bits of our integer right. + +#v(2mm) + +For example, consider $#text[`0b0000_0110`] = 6$. \ +If we insert a zero at the left end of this string and delete the zero at the right \ +(thus "shifting" each bit right), we get `0b0000_0011`, which is 3. \ + +#v(2mm) + +Of course, we lose the remainder when we right-shift an odd number: \ +$9$ shifted right is $4$, since `0b0000_1001` shifted right is `0b0000_0100`. + +#problem() +Right shifts are denoted by the `>>` symbol: \ +$#text[`00110`] #text[`>>`] n$ means "shift `0b0110` right $n$ times." \ +Find the value of the following: +- $12 #text[`>>`] 1$ +- $27 #text[`>>`] 3$ +- $16 #text[`>>`] 8$ +#note[Naturally, you'll have to convert these integers to binary first.] + +#solution[ + - $12 #text[`>>`] 1 = 6$ + - $27 #text[`>>`] 3 = 3$ + - $16 #text[`>>`] 8 = 0$ +] + +#v(1fr) diff --git a/src/Advanced/Fast Inverse Root/parts/02 float.typ b/src/Advanced/Fast Inverse Root/parts/02 float.typ new file mode 100644 index 0000000..db056ff --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/02 float.typ @@ -0,0 +1,207 @@ +#import "@local/handout:0.1.0": * +#import "@preview/cetz:0.3.1" + += Floats +#definition() +_Binary decimals_#footnote([Note that "binary decimal" is a misnomer---"deci" means "ten"!]) are very similar to base-10 decimals.\ +In base 10, we interpret place value as follows: +- $0.1 = 10^(-1)$ +- $0.03 = 3 times 10^(-2)$ +- $0.0208 = 2 times 10^(-2) + 8 times 10^(-4)$ + +#v(5mm) + +We can do the same in base 2: +- $#text([`0.1`]) = 2^(-1) = 0.5$ +- $#text([`0.011`]) = 2^(-2) + 2^(-3) = 0.375$ +- $#text([`101.01`]) = 5.125$ + +#v(5mm) + +#problem() +Rewrite the following binary decimals in base 10: \ +#note([You may leave your answer as a fraction.]) +- `1011.101` +- `110.1101` + + +#v(1fr) +#pagebreak() + +#definition() +Another way we can interpret a bit string is as a _signed floating-point decimal_, or a `float` for short. \ +Floats represent a subset of the real numbers, and are interpreted as follows: \ +#note([The following only applies to floats that consist of 32 bits. We won't encounter any others today.]) + +#align( + center, + box( + inset: 2mm, + cetz.canvas({ + import cetz.draw: * + + let chars = ( + `0`, + `b`, + `0`, + `_`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `_`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `_`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `_`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + `0`, + ) + + let x = 0 + for c in chars { + content((x, 0), c) + x += 0.25 + } + + let y = -0.4 + line((0.3, y), (0.65, y)) + content((0.45, y - 0.2), [s]) + + line((0.85, y), (2.9, y)) + content((1.9, y - 0.2), [exponent]) + + line((3.10, y), (9.4, y)) + content((6.3, y - 0.2), [fraction]) + }), + ), +) + +- The first bit denotes the sign of the float's value + We'll label it $s$. \ + If $s = #text([`1`])$, this float is negative; if $s = #text([`0`])$, it is positive. + +- The next eight bits represent the _exponent_ of this float. + #note([(we'll see what that means soon)]) \ + We'll call the value of this eight-bit binary integer $E$. \ + Naturally, $0 <= E <= 255$ #note([(since $E$ consist of eight bits)]) + +- The remaining 23 bits represent the _fraction_ of this float. \ + They are interpreted as the fractional part of a binary decimal. \ + For example, the bits `0b10100000_00000000_00000000` represent $0.5 + 0.125 = 0.625$. \ + We'll call the value of these bits as a binary integer $F$. \ + Their value as a binary decimal is then $F div 2^23$. #note([(convince yourself of this)]) + + +#problem(label: "floata") +Consider `0b01000001_10101000_00000000_00000000`. \ +Find the $s$, $E$, and $F$ we get if we interpret this bit string as a `float`. \ +#note([Leave $F$ as a sum of powers of two.]) + +#solution([ + $s = 0$ \ + $E = 258$ \ + $F = 2^31+2^19 = 2,621,440$ +]) + +#v(1fr) + + +#definition(label: "floatdef") +The final value of a float with sign $s$, exponent $E$, and fraction $F$ is + +$ + (-1)^s times 2^(E - 127) times (1 + F / (2^(23))) +$ + +Notice that this is very similar to base-10 scientific notation, which is written as + +$ + (-1)^s times 10^(e) times (f) +$ + +#note[ + We subtract 127 from $E$ so we can represent positive and negative numbers. \ + $E$ is an eight bit binary integer, so $0 <= E <= 255$ and thus $-127 <= (E - 127) <= 127$. +] + +#problem() +Consider `0b01000001_10101000_00000000_00000000`. \ +This is the same bit string we used in @floata. \ + +#v(2mm) + +What value do we get if we interpret this bit string as a float? \ +#hint([$21 div 16 = 1.3125$]) + +#solution([ + This is 21: + $ + 2^(131) times (1 + (2^(21) + 2^(19)) / (2^(23))) + = 2^(4) times (1 + 0.25 + 0.0625) + = 16 times (1.3125) + = 21 + $ +]) + +#v(1fr) +#pagebreak() + +#problem() +Encode $12.5$ as a float. \ +#hint([$12.5 div 8 = 1.5625$]) + +#solution([ + $ + 12.5 + = 8 times 1.5625 + = 2^(3) times (1 + (0.5 + 0.0625)) + = 2^(130) times (1 + (2^(22) + 2^(19)) / (2^(23))) + $ + + which is `0b01000001_01001000_00000000_00000000`. \ +]) + + +#v(1fr) + +#definition() +Say we have a bit string $x$. \ +We'll let $x_f$ denote the value we get if we interpret $x$ as a float, \ +and we'll let $x_i$ denote the value we get if we interpret $x$ an integer. + +#problem() +Let $x = #text[`0b01000001_01001000_00000000_00000000`]$. \ +What are $x_f$ and $x_i$? #note([As always, you may leave big numbers as powers of two.]) +#solution([ + $x_f = 12.5$ + + #v(2mm) + + $x_i = 2^30 + 2^24 + 2^22 + 2^19 = 11,095,237,632$ +]) + +#v(1fr) diff --git a/src/Advanced/Fast Inverse Root/parts/03 approx.typ b/src/Advanced/Fast Inverse Root/parts/03 approx.typ new file mode 100644 index 0000000..5934628 --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/03 approx.typ @@ -0,0 +1,173 @@ +#import "@local/handout:0.1.0": * +#import "@preview/cetz:0.3.1" +#import "@preview/cetz-plot:0.1.0": plot, chart + += Integers and Floats + +#generic("Observation:") +If $x$ is smaller than 1, $log_2(1 + x)$ is approximately equal to $x$. \ +Note that this equality is exact for $x = 0$ and $x = 1$, since $log_2(1) = 0$ and $log_2(2) = 1$. + +#v(5mm) + +We'll add the _correction term_ $epsilon$ to our approximation: $log_2(1 + a) approx a + epsilon$. \ +This allows us to improve the average error of our linear approximation: + +#table( + stroke: none, + align: center, + columns: (1fr, 1fr), + inset: 5mm, + [$log_2(1+x)$ and $x + 0$] + + cetz.canvas({ + import cetz.draw: * + + let f1(x) = calc.log(calc.abs(x + 1), base: 2) + let f2(x) = x + + // Set-up a thin axis style + set-style(axes: (stroke: .5pt, tick: (stroke: .5pt))) + + + plot.plot( + size: (7, 7), + x-tick-step: 0.2, + y-tick-step: 0.2, + y-min: 0, + y-max: 1, + x-min: 0, + x-max: 1, + legend: none, + axis-style: "scientific-auto", + x-label: none, + y-label: none, + { + let domain = (0, 1) + + plot.add( + f1, + domain: domain, + label: $log(1+x)$, + style: (stroke: ogrape), + ) + + plot.add( + f2, + domain: domain, + label: $x$, + style: (stroke: oblue), + ) + }, + ) + }) + + [ + Max error: 0.086 \ + Average error: 0.0573 + ], + [$log_2(1+x)$ and $x + 0.045$] + + cetz.canvas({ + import cetz.draw: * + + let f1(x) = calc.log(calc.abs(x + 1), base: 2) + let f2(x) = x + 0.0450466 + + // Set-up a thin axis style + set-style(axes: (stroke: .5pt, tick: (stroke: .5pt))) + + + plot.plot( + size: (7, 7), + x-tick-step: 0.2, + y-tick-step: 0.2, + y-min: 0, + y-max: 1, + x-min: 0, + x-max: 1, + legend: none, + axis-style: "scientific-auto", + x-label: none, + y-label: none, + { + let domain = (0, 1) + + plot.add( + f1, + domain: domain, + label: $log(1+x)$, + style: (stroke: ogrape), + ) + + plot.add( + f2, + domain: domain, + label: $x$, + style: (stroke: oblue), + ) + }, + ) + }) + + [ + Max error: 0.041 \ + Average error: 0.0254 + ], +) + + +A suitiable value of $epsilon$ can be found using calculus or with computational trial-and-error. \ +We won't bother with this---we'll simply leave the correction term as an opaque constant $epsilon$. + + + +#v(1fr) + +#note( + type: "Note", + [ + "Average error" above is simply the area of the region between the two graphs: + $ + integral_0^1 abs( #v(1mm) log(1+x)_2 - (x+epsilon) #v(1mm)) + $ + Feel free to ignore this note, it isn't a critical part of this handout. + ], +) + + +#pagebreak() + +#problem(label: "convert") +Use the fact that $log_2(1 + a) approx a + epsilon$ to approximate $log_2(x_f)$ in terms of $x_i$. \ +Namely, show that +$ + log_2(x_f) = (x_i) / (2^23) - 127 + epsilon +$ +#note([ + In other words, we're finding an expression for $x$ as a float + in terms of $x$ as an int. +]) + +#solution([ + Let $E$ and $F$ be the exponent and float bits of $x_f$. \ + We then have: + $ + log_2(x_f) + &= log_2 ( 2^(E-127) times (1 + (F) / (2^23)) ) \ + &= E - 127 + log_2(1 + F / (2^23)) \ + & approx E-127 + F / (2^23) + epsilon \ + &= 1 / (2^23)(2^23 E + F) - 127 + epsilon \ + &= 1 / (2^23)(x_i) - 127 + epsilon + $ +]) + +#v(1fr) + + +#problem() +Using basic log rules, rewrite $log_2(1 / sqrt(x))$ in terms of $log_2(x)$. + +#solution([ + $ + log_2(1 / sqrt(x)) = (-1) / (2)log_2(x) + $ +]) + +#v(1fr) diff --git a/src/Advanced/Fast Inverse Root/parts/04 quake.typ b/src/Advanced/Fast Inverse Root/parts/04 quake.typ new file mode 100644 index 0000000..5e2d57f --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/04 quake.typ @@ -0,0 +1,210 @@ +#import "@local/handout:0.1.0": * + += The Fast Inverse Square Root + +A simplified version of the _Quake_ routine we are studying is reproduced below. + +#v(2mm) + +```c +float Q_rsqrt( float number ) { + long i = * ( long * ) &number; + i = 0x5f3759df - ( i >> 1 ); + return * ( float * ) &i; +} +``` + +#v(2mm) + +This code defines a function `Q_rsqrt` that consumes a float `number` and approximates its inverse square root. +If we rewrite this using notation we're familiar with, we get the following: +$ + #text[`Q_sqrt`] (n_f) = + 6240089 - (n_i div 2) + #h(10mm) + approx 1 / sqrt(n_f) +$ + +#note[ + `0x5f3759df` is $6240089$ in hexadecimal. \ + Ask an instructor to explain if you don't know what this means. \ + It is a magic number hard-coded into `Q_sqrt`. +] + +#v(2mm) + +Our goal in this section is to understand why this works: +- How does Quake approximate $1 / sqrt(x)$ by simply subtracting and dividing by two? +- What's special about $6240089$? + + + +#v(1fr) + +#remark() +For those that are interested, here are the details of the "code-to-math" translation: + +- "`long i = * (long *) &number`" is C magic that tells the compiler \ + to set `i` to the `uint` value of the bits of `number`. \ + #note[ + "long" refers to a "long integer", which has 32 bits. \ + Normal `int`s have 16 bits, `short int`s have 8. + ] \ + In other words, `number` is $n_f$ and `i` is $n_i$. +#v(2mm) + + +- Notice the right-shift in the second line of the function. \ + We translated `(i >> 1)` into $(n_i div 2)$. +#v(2mm) + +- "`return * (float *) &i`" is again C magic. \ + Much like before, it tells us to return the value of the bits of `i` as a float. +#pagebreak() + +#generic("Setup:") +We are now ready to show that $#text[`Q_sqrt`] (x)$ effectively approximates $1/sqrt(x)$. \ +For convenience, let's call the bit string of the inverse square root $r$. \ +In other words, +$ + r_f := 1 / (sqrt(n_f)) +$ +This is the value we want to approximate. \ + +#problem(label: "finala") +Find an approximation for $log_2(r_f)$ in terms of $n_i$ and $epsilon$ \ +#note[Remember, $epsilon$ is the correction constant in our approximation of $log_2(1 + x)$.] + +#solution[ + $ + log_2(r_f) + = log_2(1 / sqrt(n_f)) + = (-1) / 2 log_2(n_f) + approx (-1) / 2 ( (n_i) / (2^23) + epsilon - 127 ) + $ +] + +#v(1fr) + +#problem(label: "finalb") +Let's call the "magic number" in the code above $kappa$, so that +$ + #text[`Q_sqrt`] (n_f) = kappa - (n_i div 2) +$ +Use @convert and @finala to show that $#text[`Q_sqrt`] (n_f) approx r_i$ \ +#note(type: "Note")[ + If we know $r_i$, we know $r_f$. \ + We don't even need to convert between the two---the underlying bits are the same! +] + +#solution[ + From @convert, we know that + $ + log_2(r_f) approx (r_i) / (2^23) + epsilon - 127 + $ + + Combining this with the result from @finala, we get: + $ + (r_i) / (2^23) + epsilon - 127 + &approx (-1) / (2) ( (n_i) / (2^23) + epsilon - 127) \ + (r_i) / (2^23) + &approx (-1) / (2) ( (n_i) / (2^23)) + 3 / 2 (127 - epsilon) \ + r_i + &approx (-1) / 2 (n_i) + 2^23 3 / 2(127 - epsilon) + = 2^23 3 / 2 (127 - epsilon) - (n_i) / 2 + $ + + #v(2mm) + + This is exactly what we need! If we set $kappa$ to $(3 times 2^22) (127-epsilon)$, then + $ + r_i approx kappa - (n_i div 2) = #text[`Q_sqrt`] (n_f) + $ +] + +#v(1fr) + +#problem(label: "finalc") +What is the exact value of $kappa$ in terms of $epsilon$? \ +#hint[Look at @finalb. We already found it!] + +#solution[ + This problem makes sure our students see that + $kappa = (3 times 2^22) (127 - epsilon)$. \ + See the solution to @finalb. +] + +#v(2cm) + +#pagebreak() + +#remark() +In @finalc we saw that $kappa = (3 times 2^22) (127 - epsilon)$. \ +Looking at the code again, we see that $kappa = #text[`0x5f3759df`]$ in _Quake_: + +#v(2mm) + +```c +float Q_rsqrt( float number ) { + long i = * ( long * ) &number; + i = 0x5f3759df - ( i >> 1 ); + return * ( float * ) &i; +} +``` + +#v(2mm) +Using a calculator and some basic algebra, we can find the $epsilon$ this code uses: \ +#note[Remember, #text[`0x5f3759df`] is $6240089$ in hexadecimal.] +$ + (3 times 2^22) (127 - epsilon) &= 6240089 \ + (127 - epsilon) &= 126.955 \ + epsilon &= 0.0450466 +$ + +So, $0.045$ is the $epsilon$ used by Quake. \ +Online sources state that this constant was generated by trial-and-error, \ +though it is fairly close to the ideal $epsilon$. + +#remark() +And now, we're done! \ +We've shown that `Q_sqrt(x)` approximates $1/sqrt(x)$ fairly well. \ + +#v(2mm) + +Notably, `Q_sqrt` uses _zero_ divisions or multiplications (`>>` doesn't count). \ +This makes it _very_ fast when compared to more traditional approximation techniques (i.e, Taylor series). + +#v(2mm) + +In the case of _Quake_, this is very important. 3D graphics require thousands of inverse-square-root calculations to render a single frame#footnote[e.g, to generate normal vectors], which is not an easy task for a Playstation running at 300MHz. + +#instructornote[ + Let $x$ be a bit string. If we assume $x_f$ is positive and $E$ is even, then + $ + (x #text[`>>`] 1)_f = 2^((E div 2) - 127) times (1 + (F div 2) / (2^(23))) + $ + Notably: a right-shift divides the exponent of $x_f$ by two, \ + which is, of course, a square root! + + #v(2mm) + + This intuition is hand-wavy, though: \ + If $E$ is odd, its lowest-order bit becomes the highest-order bit of $F$ when we shift $x$ right. \ + Also, a right shift doesn't divide the _entire_ exponent, skipping the $-127$ offset. \ + + #v(2mm) + + Remarkably, this intuition is still somewhat correct. \ + The bits align _just so_, and our approximation still works. + + #v(8mm) + + One can think of the fast inverse root as a "digital slide rule": \ + The integer representation of $x_f$ already contains $log_2(x_f)$, offset and scaled. \ + By subtracting and dividing in "log space", we effectively invert and root $x_f$! + + After all, + $ + - 1 / 2 log_2(n_f) = 1 / sqrt(n_f) + $ +] diff --git a/src/Advanced/Fast Inverse Root/parts/05 bonus.typ b/src/Advanced/Fast Inverse Root/parts/05 bonus.typ new file mode 100644 index 0000000..3cf5177 --- /dev/null +++ b/src/Advanced/Fast Inverse Root/parts/05 bonus.typ @@ -0,0 +1,36 @@ +#import "@local/handout:0.1.0": * + += Bonus -- More about Floats + +#problem() +Convince yourself that all numbers that can be represented as a float are rational. + +#problem() +Find a rational number that cannot be represented as a float. + +#v(1fr) + +#problem() +What is the smallest positive 32-bit float? + +#v(1fr) + +#problem() +What is the largest positive 32-bit float? + +#v(1fr) + +#problem() +How many floats are between $-1$ and $1$? + +#v(1fr) + +#problem() +How many floats are between $1$ and $2$? + +#v(1fr) + +#problem() +How many floats are between $1$ and $128$? + +#v(1fr) diff --git a/src/Advanced/Introduction to Quantum/src/main.tex b/src/Advanced/Introduction to Quantum/src/main.tex index 4708319..d8ddfab 100755 --- a/src/Advanced/Introduction to Quantum/src/main.tex +++ b/src/Advanced/Introduction to Quantum/src/main.tex @@ -1,18 +1,3 @@ -% Copyright (C) 2023 -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% You may have received a copy of the GNU General Public License -% along with this program. If not, see . -% -% -% -% If you edit this, please give credit! -% Quality handouts take time to make. - % use the [nosolutions] flag to hide solutions, % use the [solutions] flag to show solutions. \documentclass[ diff --git a/src/Advanced/Nonstandard Analysis/main.tex b/src/Advanced/Nonstandard Analysis/main.tex index c0cc0e9..4daa657 100755 --- a/src/Advanced/Nonstandard Analysis/main.tex +++ b/src/Advanced/Nonstandard Analysis/main.tex @@ -1,19 +1,3 @@ -% Copyright (C) 2023 -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% You may have received a copy of the GNU General Public License -% along with this program. If not, see . -% -% -% -% If you edit this, please give credit! -% Quality handouts take time to make. - - % use [nosolutions] flag to hide solutions. % use [solutions] flag to show solutions. \documentclass[ diff --git a/src/Advanced/Nonstandard Analysis/parts/dual.tex b/src/Advanced/Nonstandard Analysis/parts/dual.tex index b7eb55a..344447d 100644 --- a/src/Advanced/Nonstandard Analysis/parts/dual.tex +++ b/src/Advanced/Nonstandard Analysis/parts/dual.tex @@ -1,19 +1,3 @@ -% Copyright (C) 2023 -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% You may have received a copy of the GNU General Public License -% along with this program. If not, see . -% -% -% -% If you edit this, please give credit! -% Quality handouts take time to make. - - \section{Dual Numbers} \definition{} diff --git a/src/Advanced/Nonstandard Analysis/parts/extensions.tex b/src/Advanced/Nonstandard Analysis/parts/extensions.tex index e0cb5d7..96165a3 100644 --- a/src/Advanced/Nonstandard Analysis/parts/extensions.tex +++ b/src/Advanced/Nonstandard Analysis/parts/extensions.tex @@ -1,18 +1,3 @@ -% Copyright (C) 2023 -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% You may have received a copy of the GNU General Public License -% along with this program. If not, see . -% -% -% -% If you edit this, please give credit! -% Quality handouts take time to make. - \section{Extensions of $\mathbb{R}$} \definition{} diff --git a/src/Advanced/Nonstandard Analysis/parts/supremum.tex b/src/Advanced/Nonstandard Analysis/parts/supremum.tex index bd0dbf7..d4fb16f 100644 --- a/src/Advanced/Nonstandard Analysis/parts/supremum.tex +++ b/src/Advanced/Nonstandard Analysis/parts/supremum.tex @@ -1,19 +1,3 @@ -% Copyright (C) 2023 -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% You may have received a copy of the GNU General Public License -% along with this program. If not, see . -% -% -% -% If you edit this, please give credit! -% Quality handouts take time to make. - - \section*{The supremum \& the infimum} \definition{} diff --git a/src/Warm-Ups/Slide Rules/main.tex b/src/Warm-Ups/Slide Rules/main.tex new file mode 100755 index 0000000..f2f5471 --- /dev/null +++ b/src/Warm-Ups/Slide Rules/main.tex @@ -0,0 +1,78 @@ +% use [nosolutions] flag to hide solutions. +% use [solutions] flag to show solutions. +\documentclass[ + solutions, + shortwarning +]{../../../lib/tex/ormc_handout} +\usepackage{../../../lib/tex/macros} + + +\usepackage{pdfpages} +\usepackage{sliderule} +\usepackage{changepage} + +% Args: +% x, top scale y, label +\newcommand{\slideruleind}[3]{ + \draw[ + line width=1mm, + draw=black, + opacity=0.3, + text opacity=1 + ] + ({#1}, {#2 + 1}) + -- + ({#1}, {#2 - 1.1}) + node [below] {#3}; +} + + +\uptitlel{Advanced} +\uptitler{\smallurl{}} +\title{Warm-Up: Slide Rules} +\subtitle{Prepared by Mark on \today} + +\begin{document} + + \maketitle + + + \begin{center} + \begin{minipage}{6cm} + Dad says that anyone who can't use + a slide rule is a cultural illiterate + and should not be allowed to vote. + + \vspace{1ex} + + \textit{Have Space Suit --- Will Travel, 1958} + \end{minipage} + \end{center} + \hfill + + \input{parts/0 logarithms.tex} + \input{parts/1 intro.tex} + \input{parts/2 multiplication.tex} + + % Make sure the slide rule is on an odd page, + % so that double-sided printing won't require + % students to tear off problems. + \checkoddpage + \ifoddpage\else + \vspace*{\fill} + \begin{center} + { + \Large + \textbf{This page unintentionally left blank.} + } + \end{center} + \vspace{\fill} + \pagebreak + \fi + + \includepdf[ + pages=1, + fitpaper=true + ]{resources/rule.pdf} + +\end{document} \ No newline at end of file diff --git a/src/Warm-Ups/Slide Rules/meta.toml b/src/Warm-Ups/Slide Rules/meta.toml new file mode 100644 index 0000000..59669b6 --- /dev/null +++ b/src/Warm-Ups/Slide Rules/meta.toml @@ -0,0 +1,6 @@ +[metadata] +title = "Slide Rules" + +[publish] +handout = false +solutions = true diff --git a/src/Warm-Ups/Slide Rules/parts/0 logarithms.tex b/src/Warm-Ups/Slide Rules/parts/0 logarithms.tex new file mode 100644 index 0000000..172fade --- /dev/null +++ b/src/Warm-Ups/Slide Rules/parts/0 logarithms.tex @@ -0,0 +1,63 @@ +\section{Logarithms} + +\definition{} +The \textit{logarithm} is the inverse of the exponent. That is, if $b^p = c$, then $\log_b{c} = p$. \\ +In other words, $\log_b{c}$ asks the question ``what power do I need to raise $b$ to to get $c$?'' \\ + +\problem{} +Evaluate the following by hand: + +\begin{enumerate} + \item $\log_{10}{(1000)}$ + \vfill + \item $\log_2{(64)}$ + \vfill + \item $\log_2{(\frac{1}{4})}$ + \vfill + \item $\log_x{(x)}$ for any $x$ + \vfill + \item $log_x{(1)}$ for any $x$ + \vfill +\end{enumerate} + +\problem{} +Prove the following: + +\begin{enumerate}[itemsep=2mm] + \item $\log_b{(b^x)} = x$ + \vfill + \item $b^{\log_b{x}} = x$ + \vfill + \item $\log_b{(xy)} = \log_b{(x)} + \log_b{(y)}$ + \vfill + \item $\log_b{(\frac{x}{y})} = \log_b{(x)} - \log_b{(y)}$ + \vfill + \item $\log_b{(x^y)} = y \log_b{(x)}$ + \vfill +\end{enumerate} + + +\begin{instructornote} + A good intro to the following sections is the linear slide rule: + \note{(note that these rules start at 0)} + \begin{center} + \begin{tikzpicture}[scale=0.5] + \linearscale{2}{1}{} + \linearscale{0}{0}{} + + \slideruleind + {5} + {1} + {2 + 3 = 5} + \end{tikzpicture} + \end{center} + + Take two linear rules, offset one, and you add. + Do the same with a log scale, and you multiply! \\ + + \linehack{} + + After assembling the paper slide rule, you can make a visor with some transparent tape. +\end{instructornote} + +\pagebreak \ No newline at end of file diff --git a/src/Warm-Ups/Slide Rules/parts/1 intro.tex b/src/Warm-Ups/Slide Rules/parts/1 intro.tex new file mode 100644 index 0000000..8d839c8 --- /dev/null +++ b/src/Warm-Ups/Slide Rules/parts/1 intro.tex @@ -0,0 +1,43 @@ +\section{Introduction} + +Mathematicians, physicists, and engineers needed to quickly compute products long before computers conquered the world. + +\medskip + +The \textit{slide rule} is an instrument that uses the logarithm to solve this problem. Before you continue, cut out and assemble your slide rule. + +\medskip + +There are four scales on your slide rule, each labeled with a letter on the left side: + +\def\sliderulewidth{13} +\begin{center} +\begin{tikzpicture}[scale=1] + \tscale{0}{9}{T} + \kscale{0}{8}{K} + \abscale{0}{7}{A} + + \abscale{0}{5.5}{B} + \ciscale{0}{4.5}{CI} + \cdscale{0}{3.5}{C} + + \cdscale{0}{2}{D} + \lscale{0}{1}{L} + \sscale{0}{0}{S} +\end{tikzpicture} +\end{center} + +Each scale's ``generating function'' is on the right: +\begin{itemize} + \item T: $\tan$ + \item K: $x^3$ + \item A,B: $x^2$ + \item CI: $\frac{1}{x}$ + \item C, D: $x$ + \item L: $\log_{10}(x)$ + \item S: $\sin$ +\end{itemize} + +Once you understand the layout of your slide rule, move on to the next page. + +\pagebreak diff --git a/src/Warm-Ups/Slide Rules/parts/2 multiplication.tex b/src/Warm-Ups/Slide Rules/parts/2 multiplication.tex new file mode 100644 index 0000000..fee1493 --- /dev/null +++ b/src/Warm-Ups/Slide Rules/parts/2 multiplication.tex @@ -0,0 +1,299 @@ +\section{Multiplication} + +We'll use the C and D scales of your slide rule to multiply. \\ + +Say we want to multiply $2 \times 3$. First, move the \textit{left-hand index} of the C scale over the smaller number, $2$: + +\def\sliderulewidth{10} +\begin{center} +\begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(2)}{1}{C} + \cdscale{0}{0}{D} +\end{tikzpicture} +\end{center} + +Then we'll find the second number, $3$ on the C scale, and read the D scale under it: + +\begin{center} +\begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(2)}{1}{C} + \cdscale{0}{0}{D} + + \slideruleind + {\cdscalefn(6)} + {1} + {6} + +\end{tikzpicture} +\end{center} + +Of course, our answer is 6. + +\problem{} +What is $1.15 \times 2.1$? \\ +Use your slide rule. + +\begin{solution} + \begin{center} + \begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(1.15)}{1}{C} + \cdscale{0}{0}{D} + + \slideruleind + {\cdscalefn(1.15)} + {1} + {1.15} + + \slideruleind + {\cdscalefn(1.15) + \cdscalefn(2.1)} + {1} + {2.415} + + \end{tikzpicture} + \end{center} +\end{solution} + +\vfill + +Note that your answer isn't exact. $1.15 \times 2.1 = 2.415$, but an answer accurate within two decimal places is close enough for most practical applications. \\ + +\pagebreak + +Look at your C and D scales again. They contain every number between 1 and 10, but no more than that. +What should we do if we want to calculate $32 \times 210$? \\ + +\problem{} +Using your slide rule, calculate $32 \times 210$. \\ +%\hint{$32 = 3.2 \times 10^1$} + +\begin{solution} + \begin{center} + \begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(2.1)}{1}{C} + \cdscale{0}{0}{D} + + \slideruleind + {\cdscalefn(2.1)} + {1} + {2.1} + + \slideruleind + {\cdscalefn(2.1) + \cdscalefn(3.2)} + {1} + {6.72} + + \end{tikzpicture} + \end{center} + + Placing the decimal point correctly is your job. \\ + $10^1 \times 10^2 = 10^3$, so our final answer is $6.72 \times 10^3 = 672$. +\end{solution} + +\vfill + +%This method of writing numbers is called \textit{scientific notation}. In the form $a \times 10^b$, $a$ is called the \textit{mantissa}, and $b$, the \textit{exponent}. \\ + +%You may also see expressions like $4.3\text{e}2$. This is equivalent to $4.3 \times 10^2$, but is more compact. + + +\problem{} +Compute the following: +\begin{enumerate} + \item $1.44 \times 52$ + \item $0.38 \times 1.24$ + \item $\pi \times 2.35$ +\end{enumerate} + +\begin{solution} + \begin{enumerate} + \item $1.44 \times 52 = 74.88$ + \item $0.38 \times 1.24 = 0.4712$ + \item $\pi \times 2.35 = 7.382$ + \end{enumerate} +\end{solution} + +\vfill +\pagebreak + +\problem{} +Note that the numbers on your C and D scales are logarithmically spaced. + +\def\sliderulewidth{13} +\begin{center} +\begin{tikzpicture}[scale=1] + \cdscale{0}{1}{C} + \cdscale{0}{0}{D} +\end{tikzpicture} +\end{center} + +Why does our multiplication procedure work? \\ +%\hint{See \ref{logids}} + +\vfill +\pagebreak + +Now we want to compute $7.2 \times 5.5$: + +\def\sliderulewidth{10} +\begin{center} +\begin{tikzpicture}[scale=0.8] + \cdscale{\cdscalefn(5.5)}{1}{C} + \cdscale{0}{0}{D} + + \slideruleind + {\cdscalefn(5.5)} + {1} + {5.5} + + \slideruleind + {\cdscalefn(5.5) + \cdscalefn(7.2)} + {1} + {???} + +\end{tikzpicture} +\end{center} + +No matter what order we go in, the answer ends up off the scale. There must be another way. \\ + +\medskip + +Look at the far right of your C scale. There's an arrow pointing to the $10$ tick, labeled \textit{right-hand index}. Move it over the \textit{larger} number, $7.2$: + +\begin{center} +\begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C} + \cdscale{0}{0}{D} + + \slideruleind + {\cdscalefn(7.2)} + {1} + {7.2} + +\end{tikzpicture} +\end{center} + +Now find the smaller number, $5.5$, on the C scale, and read the D scale under it: + +\begin{center} +\begin{tikzpicture}[scale=1] + \cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C} + \cdscale{0}{0}{D} + + + \slideruleind + {\cdscalefn(7.2)} + {1} + {7.2} + + \slideruleind + {\cdscalefn(3.96)} + {1} + {3.96} + +\end{tikzpicture} +\end{center} + +Our answer should be about $7 \times 5 = 35$, so let's move the decimal point: $5.5 \times 7.2 = 39.6$. We can do this by hand to verify our answer. \\ + +\medskip + +\problem{} +Why does this work? \par +\hint{Add a second $D$ scale.} + +\begin{solution} + Consider the following picture, where I've put two D scales next to each other: + + \begin{center} + \begin{tikzpicture}[scale=0.7] + \cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C} + \cdscale{0}{0}{} + \cdscale{-10}{0}{} + + \draw[ + draw=black, + ] + (0, 0) + -- + (0, -0.3) + node [below] {D}; + + \draw[ + draw=black, + ] + (-10, 0) + -- + (-10, -0.3) + node [below] {D}; + + \slideruleind + {-10 + \cdscalefn(7.2)} + {1} + {7.2} + + \slideruleind + {\cdscalefn(7.2)} + {1} + {7.2} + + \slideruleind + {\cdscalefn(3.96)} + {1} + {3.96} + + \end{tikzpicture} + \end{center} + + \medskip + + The second D scale has been moved to the right by $(\log{10})$, so every value on it is $(\log{10})$ smaller than it should be. + + \medskip + + \medskip + In other words, the answer we get from reverse multiplication is the following: $\log{a} + \log{b} - \log{10}$. \\ + This reduces to $\log{(\frac{a \times b}{10})}$, which explains the misplaced decimal point in $7.2 \times 5.5$. +\end{solution} + +\vfill +\pagebreak + +\problem{} +Compute the following using your slide rule: +\begin{enumerate} + \item $9 \times 8$ + \item $15 \times 35$ + \item $42.1 \times 7.65$ + \item $6.5^2$ +\end{enumerate} + +\begin{solution} + \begin{enumerate} + \item $9 \times 8 = 72$ + \item $15 \times 35 = 525$ + \item $42.1 \times 7.65 = 322.065$ + \item $6.5^2 = 42.25$ + \end{enumerate} +\end{solution} + +\vfill + +\problem{} +Compute the following using your slide rule. \\ + +\begin{enumerate} + \item $135 \div 15$ + \item $68.2 \div 0.575$ + \item $(118 \times 0.51) \div 6.6$ +\end{enumerate} + +\begin{solution} + \begin{enumerate} + \item $135 \div 15 = 9$ + \item $68.2 \div 0.575 = 118.609$ + \item $(118 \times 0.51) \div 6.6 = 9.118$ + \end{enumerate} +\end{solution} + +\vfill +\pagebreak \ No newline at end of file diff --git a/src/Warm-Ups/Slide Rules/resources/rule.pdf b/src/Warm-Ups/Slide Rules/resources/rule.pdf new file mode 100755 index 0000000..b8fc33b Binary files /dev/null and b/src/Warm-Ups/Slide Rules/resources/rule.pdf differ diff --git a/src/Warm-Ups/Slide Rules/resources/rule.svg b/src/Warm-Ups/Slide Rules/resources/rule.svg new file mode 100755 index 0000000..9506a6d --- /dev/null +++ b/src/Warm-Ups/Slide Rules/resources/rule.svg @@ -0,0 +1,24144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Warm-Ups/Slide Rules/sliderule.sty b/src/Warm-Ups/Slide Rules/sliderule.sty new file mode 100755 index 0000000..13e3995 --- /dev/null +++ b/src/Warm-Ups/Slide Rules/sliderule.sty @@ -0,0 +1,534 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{sliderule}[2022/08/22 Slide rule tools] + +\RequirePackage{tikz} +\RequirePackage{ifthen} + + +% Scale functions: +% See https://sliderulemuseum.com/SR_Scales.htm +% +% l: length of the rule +% n: the number on the rule +% +% A/B: (l/2) * log(n) +% C/D: l / log(n) +% CI: abs(l * log(10 / n) - l) +% K: (l/3) * log(n) +% +% L: n * l +% T: l * log(10 * tan(n)) +% S: l * log(10 * sin(n)) + +\def\sliderulewidth{10} + +\def\abscalefn(#1){(\sliderulewidth/2) * log10(#1)} +\def\cdscalefn(#1){(\sliderulewidth * log10(#1))} +\def\ciscalefn(#1){(\sliderulewidth - \cdscalefn(#1))} +\def\kscalefn(#1){(\sliderulewidth/3) * log10(#1)} +\def\lscalefn(#1){(\sliderulewidth * #1)} +\def\tscalefn(#1){(\sliderulewidth * log10(10 * tan(#1)))} +\def\sscalefn(#1){(\sliderulewidth * log10(10 * sin(#1)))} + + +% Arguments: +% Label +% x of start +% y of start +\newcommand{\linearscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks + \foreach \i in {0,..., 10}{ + \draw[black] + ({#1 + (\sliderulewidth / 10) * \i}, #2) -- + ({#1 + (\sliderulewidth / 10) * \i}, #2 + 0.3) + node[above] {\i}; + } + + % Submarks + \foreach \n in {0, ..., 9} { + \foreach \i in {1,..., 9} { + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2) -- + ({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2 + 0.2); + } { + \draw[black] + ({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2) -- + ({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2 + 0.1); + } + } + } +} + + +% Arguments: +% Label +% x of start +% y of start +\newcommand{\abscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks 1 - 9 + \foreach \i in {1,..., 9}{ + \draw[black] + ({#1 + \abscalefn(\i)}, #2) -- + ({#1 + \abscalefn(\i)}, #2 + 0.3) + node[above] {\i}; + } + % Numbers and marks 10 - 100 + \foreach \i in {1,..., 10}{ + \draw[black] + ({#1 + \abscalefn(10 * \i)}, #2) -- + ({#1 + \abscalefn(10 * \i)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\i}}; + } + + % Submarks 1 - 9 + \foreach \n in {1, ..., 9} { + \ifthenelse{\n<5}{ + \foreach \i in {1,..., 9} + } { + \foreach \i in {2,4,6,8} + } + { + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \abscalefn(\n + \i / 10)}, #2) -- + ({#1 + \abscalefn(\n + \i / 10)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \abscalefn(\n + \i / 10)}, #2) -- + ({#1 + \abscalefn(\n + \i / 10)}, #2 + 0.1); + } + } + } + + % Submarks 10 - 100 + \foreach \n in {10,20,...,90} { + \ifthenelse{\n<50}{ + \foreach \i in {1,..., 9} + } { + \foreach \i in {2,4,6,8} + } + { + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \abscalefn(\n + \i)}, #2) -- + ({#1 + \abscalefn(\n + \i)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \abscalefn(\n + \i)}, #2) -- + ({#1 + \abscalefn(\n + \i)}, #2 + 0.1); + } + } + } +} + +\newcommand{\cdscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks 1 - 10 + \foreach \i in {1,..., 10}{ + \draw[black] + ({#1 + \cdscalefn(\i)}, #2) -- + ({#1 + \cdscalefn(\i)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\i}}; + } + + % Submarks 1 - 9 + \foreach \n in {1, ..., 9} { + \ifthenelse{\n<3}{ + \foreach \i in {5,10,...,95} + } { + \foreach \i in {10,20,...,90} + } + { + \ifthenelse{\i=50}{ + \draw[black] + ({#1 + \cdscalefn(\n + \i / 100)}, #2) -- + ({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.2); + \ifthenelse{\n=1}{ + \draw + ({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.2) + node [above] {1.5}; + }{} + } { + \ifthenelse{ + \i=10 \OR \i=20 \OR \i=30 \OR \i=40 \OR + \i=60 \OR \i=70 \OR \i=80 \OR \i=90 + }{ + \draw[black] + ({#1 + \cdscalefn(\n + \i / 100)}, #2) -- + ({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.15); + } { + \draw[black] + ({#1 + \cdscalefn(\n + \i / 100)}, #2) -- + ({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.1); + } + } + } + } +} + +\newcommand{\ciscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks + \foreach \i in {1,...,10}{ + \draw[black] + ({#1 + \ciscalefn(\i)}, #2) -- + ({#1 + \ciscalefn(\i)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\ifthenelse{\i=0}{0}{.\i}}}; + } + + % Submarks 1 - 9 + \foreach \n in {1, ..., 9} { + \ifthenelse{\n<3}{ + \foreach \i in {5,10,...,95} + } { + \foreach \i in {10,20,...,90} + } + { + \ifthenelse{\i=50}{ + \draw[black] + ({#1 + \ciscalefn(\n + \i / 100)}, #2) -- + ({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.2); + \ifthenelse{\n=1}{ + \draw + ({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.2) + node [above] {1.5}; + }{} + } { + \ifthenelse{ + \i=10 \OR \i=20 \OR \i=30 \OR \i=40 \OR + \i=60 \OR \i=70 \OR \i=80 \OR \i=90 + }{ + \draw[black] + ({#1 + \ciscalefn(\n + \i / 100)}, #2) -- + ({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.15); + } { + \draw[black] + ({#1 + \ciscalefn(\n + \i / 100)}, #2) -- + ({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.1); + } + } + } + } +} + +\newcommand{\kscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks 1 - 9 + \foreach \i in {1,...,9}{ + \draw[black] + ({#1 + \kscalefn(\i)}, #2) -- + ({#1 + \kscalefn(\i)}, #2 + 0.3) + node[above] {\i}; + } + % Numbers and marks 10 - 90 + \foreach \i in {1,..., 9}{ + \draw[black] + ({#1 + \kscalefn(10 * \i)}, #2) -- + ({#1 + \kscalefn(10 * \i)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\i}}; + } + % Numbers and marks 100 - 1000 + \foreach \i in {1,..., 10}{ + \draw[black] + ({#1 + \kscalefn(100 * \i)}, #2) -- + ({#1 + \kscalefn(100 * \i)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\i}}; + } + + % Submarks 1 - 9 + \foreach \n in {1, ..., 9} { + \ifthenelse{\n<4}{ + \foreach \i in {1,..., 9} + } { + \foreach \i in {2,4,6,8} + } + { + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \kscalefn(\n + \i / 10)}, #2) -- + ({#1 + \kscalefn(\n + \i / 10)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \kscalefn(\n + \i / 10)}, #2) -- + ({#1 + \kscalefn(\n + \i / 10)}, #2 + 0.1); + } + } + } + + % Submarks 10 - 90 + \foreach \n in {10,20,...,90} { + \ifthenelse{\n<40}{ + \foreach \i in {1,..., 9} + } { + \foreach \i in {2,4,6,8} + } + { + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \kscalefn(\n + \i)}, #2) -- + ({#1 + \kscalefn(\n + \i)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \kscalefn(\n + \i)}, #2) -- + ({#1 + \kscalefn(\n + \i)}, #2 + 0.1); + } + } + } + + % Submarks 100 - 1000 + \foreach \n in {100,200,...,900} { + \ifthenelse{\n<400}{ + \foreach \i in {10,20,...,90} + } { + \foreach \i in {20,40,60,80} + } + { + \ifthenelse{\i=50}{ + \draw[black] + ({#1 + \kscalefn(\n + \i)}, #2) -- + ({#1 + \kscalefn(\n + \i)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \kscalefn(\n + \i)}, #2) -- + ({#1 + \kscalefn(\n + \i)}, #2 + 0.1); + } + } + } +} + +\newcommand{\lscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks + \foreach \i in {0,..., 10}{ + \draw[black] + ({#1 + \lscalefn(\i / 10)}, #2) -- + ({#1 + \lscalefn(\i / 10)}, #2 + 0.3) + node[above] {\ifthenelse{\i=10}{1}{\ifthenelse{\i=0}{0}{.\i}}}; + } + + % Submarks + \foreach \n in {0, ..., 9} { + \foreach \i in {1,...,19} { + \ifthenelse{\i=10}{ + \draw[black] + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) -- + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.2); + } { + \ifthenelse{ + \i=1 \OR \i=3 \OR \i=5 \OR \i=7 \OR + \i=9 \OR \i=11 \OR \i=13 \OR \i=15 \OR + \i=17 \OR \i=19 + }{ + \draw[black] + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) -- + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.1); + } { + \draw[black] + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) -- + ({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.15); + } + } + } + } +} + +\newcommand{\tscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + % First line + \draw[black] ({#1}, #2) -- ({#1}, #2 + 0.2); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks 6 - 10 + \foreach \i in {6,...,9,10,15,...,45}{ + \draw[black] + ({#1 + \tscalefn(\i)}, #2) -- + ({#1 + \tscalefn(\i)}, #2 + 0.3) + node[above] {\i}; + } + + % Submarks 6 - 10 + \foreach \n in {6, ..., 9} { + \foreach \i in {1,...,9}{ + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \tscalefn(\n + \i / 10)}, #2) -- + ({#1 + \tscalefn(\n + \i / 10)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \tscalefn(\n + \i / 10)}, #2) -- + ({#1 + \tscalefn(\n + \i / 10)}, #2 + 0.1); + } + } + } + + % Submarks 15 - 45 + \foreach \n in {10, 15, ..., 40} { + \foreach \i in {1,...,24}{ + \ifthenelse{ + \i=5 \OR \i=10 \OR \i=15 \OR \i=20 + } { + \draw[black] + ({#1 + \tscalefn(\n + \i / 5)}, #2) -- + ({#1 + \tscalefn(\n + \i / 5)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \tscalefn(\n + \i / 5)}, #2) -- + ({#1 + \tscalefn(\n + \i / 5)}, #2 + 0.1); + } + } + } +} + +\newcommand{\sscale}[3]{ + \draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2); + \draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9); + \draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7); + \draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7); + + % First line + \draw[black] ({#1}, #2) -- ({#1}, #2 + 0.2); + + + \draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3}; + + % Numbers and marks + \foreach \i in {6,...,9,10,15,...,30,40,50,...,60,90}{ + \draw[black] + ({#1 + \sscalefn(\i)}, #2) -- + ({#1 + \sscalefn(\i)}, #2 + 0.3) + node[above] {\i}; + } + + % Submarks 6 - 10 + \foreach \n in {6, ..., 9} { + \foreach \i in {1,...,9}{ + \ifthenelse{\i=5}{ + \draw[black] + ({#1 + \sscalefn(\n + \i / 10)}, #2) -- + ({#1 + \sscalefn(\n + \i / 10)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \sscalefn(\n + \i / 10)}, #2) -- + ({#1 + \sscalefn(\n + \i / 10)}, #2 + 0.1); + } + } + } + + % Submarks 15 - 30 + \foreach \n in {10, 15, ..., 25} { + \foreach \i in {1,...,24}{ + \ifthenelse{ + \i=5 \OR \i=10 \OR \i=15 \OR \i=20 + } { + \draw[black] + ({#1 + \sscalefn(\n + \i / 5)}, #2) -- + ({#1 + \sscalefn(\n + \i / 5)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \sscalefn(\n + \i / 5)}, #2) -- + ({#1 + \sscalefn(\n + \i / 5)}, #2 + 0.1); + } + } + } + + % Submarks 30 + \foreach \n in {30} { + \foreach \i in {1,...,19}{ + \ifthenelse{ + \i=2 \OR \i=4 \OR \i=6 \OR \i=8 \OR + \i=10 \OR \i=12 \OR \i=14 \OR \i=16 \OR + \i=18 + } { + \draw[black] + ({#1 + \sscalefn(\n + \i / 2)}, #2) -- + ({#1 + \sscalefn(\n + \i / 2)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \sscalefn(\n + \i / 2)}, #2) -- + ({#1 + \sscalefn(\n + \i / 2)}, #2 + 0.1); + } + } + } + + % Submarks 40 - 50 + \foreach \n in {40, 50} { + \foreach \i in {1,...,9}{ + \ifthenelse{ + \i=5 \OR \i=10 \OR \i=15 \OR \i=20 + } { + \draw[black] + ({#1 + \sscalefn(\n + \i)}, #2) -- + ({#1 + \sscalefn(\n + \i)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \sscalefn(\n + \i)}, #2) -- + ({#1 + \sscalefn(\n + \i)}, #2 + 0.1); + } + } + } + + % Submarks 60 + \foreach \i in {1,...,10}{ + \ifthenelse{ + \i=5 \OR \i=10 + } { + \draw[black] + ({#1 + \sscalefn(60 + \i * 2)}, #2) -- + ({#1 + \sscalefn(60 + \i * 2)}, #2 + 0.2); + } { + \draw[black] + ({#1 + \sscalefn(60 + \i * 2)}, #2) -- + ({#1 + \sscalefn(60 + \i * 2)}, #2 + 0.1); + } + } +}