diff --git a/labs/02/cipher.txt b/labs/02/cipher.txt new file mode 100644 index 0000000..d4be357 --- /dev/null +++ b/labs/02/cipher.txt @@ -0,0 +1,30 @@ +Given: +- public key = {e, n} where + e = 65537 + n = 150833184288091770161966794058583103894140817040870314600690471524168617075425648362232214626866783147628121644765079260823839601102585268527908198147644311732189788842930506819720606279195272120981495283244202074795161751033872021233632806698896506286786027999600824135350742013291456231834593908211807306581 +- ciphertext (in file "ciphertext.data") + +To decrypt, you need to find 2 primes whose product is n. This is the +difficult part of breaking RSA encryption. + +However, in this case, you are given: n = p * q where +p = 12945845173329038168894514532852761087184861453819103626524746458007544079436193033296354255392833659165781825320450882527971501424543610656635242495207243 +q = 11651088227042719432197879712448441990940913399774434709130882531920046670720814447752206963062868458438310623799329703497031760471331490496164292198579167 + +The private key is then {d, n}, where d is the multiplicative inverse of e +modulo (p - 1) * (q - 1). (You can use a function in lab1 to calculate this.) + +Then to decrypt the content of "ciphertext.data", +1) you first have to convert the content (a binary) into an integer. Let's + call it c. +2) calculate c ^ d (mod n) (Here c ^ d means c raised to the d-th power) + (You can use a function in lab1 to calculate this). Assume this gives us + m, an integer. +3) convert m to a binary to get the message + +To convert a binary to an integer, think of each octet in the binary as +a digit in base 256, e.g., +<<92, 14, 93>> gives 92 * 256^2 + 14 * 256 + 93 +Converting an integer to binary requires the inverse operation (basically +keep on dividing by 256 and taking remainders) + diff --git a/labs/02/ciphertext.data b/labs/02/ciphertext.data new file mode 100644 index 0000000..4a57129 --- /dev/null +++ b/labs/02/ciphertext.data @@ -0,0 +1 @@ +\]\j@¸=Qû06JÛ£Rü²Ë�–Éœ][yí ™‘‰’©¸¤t poÁ ?ß¿x”æß”™.ɇ|lº^ 7†‡óœPHoóÌnià}2:¢4jh$¿®›%“?œÚa=øŽäörWuëBà1èW éÚž9à§Ò8½ \ No newline at end of file diff --git a/lectures/03/arithmetic.ex b/lectures/03/arithmetic.ex new file mode 100644 index 0000000..10c8655 --- /dev/null +++ b/lectures/03/arithmetic.ex @@ -0,0 +1,32 @@ +defmodule Arithmetic.Worker do + use GenServer + + ### Client API ### + def start() do + GenServer.start(__MODULE__, nil) + end + + def square(pid, x) do + GenServer.call(pid, {:square, x}) + end + + def sqrt(pid, x) do + GenServer.call(pid, {:sqrt, x}) + end + + ### Server Impl ### + @impl true + def init(_arg) do + {:ok, nil} + end + + @impl true + def handle_call({:square, x}, _from, state) do + {:reply, x * x, state} + end + + @impl true + def handle_call({:sqrt, x}, _from, state) do + {:reply, (if x < 0, do: :error, else: :math.sqrt(x)), state} + end +end diff --git a/lectures/03/counter/.formatter.exs b/lectures/03/counter/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/lectures/03/counter/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lectures/03/counter/.gitignore b/lectures/03/counter/.gitignore new file mode 100644 index 0000000..ccae38f --- /dev/null +++ b/lectures/03/counter/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +counter-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/lectures/03/counter/README.md b/lectures/03/counter/README.md new file mode 100644 index 0000000..f15a4ad --- /dev/null +++ b/lectures/03/counter/README.md @@ -0,0 +1,21 @@ +# Counter + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `counter` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:counter, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/lectures/03/counter/lib/counter.ex b/lectures/03/counter/lib/counter.ex new file mode 100644 index 0000000..2e364fe --- /dev/null +++ b/lectures/03/counter/lib/counter.ex @@ -0,0 +1,18 @@ +defmodule Counter do + @moduledoc """ + Documentation for `Counter`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Counter.hello() + :world + + """ + def hello do + :world + end +end diff --git a/lectures/03/counter/lib/counter/worker.ex b/lectures/03/counter/lib/counter/worker.ex new file mode 100644 index 0000000..0fdff84 --- /dev/null +++ b/lectures/03/counter/lib/counter/worker.ex @@ -0,0 +1,41 @@ +defmodule Counter.Worker do + use GenServer # use builtint Elixir Generic Server + + ### Client API ### + def start(n \\ 0) do + GenServer.start(__MODULE__, n) + end + + def inc(pid, amt \\ 1) do + GenServer.cast(pid, {:inc, amt}) + end + + def dec(pid, amt \\ 1) do + GenServer.cast(pid, {:dec, amt}) + end + + def value(pid) do + GenServer.call(pid, :value) + end + + ### Server Impl ### + @impl true # annotation that signature agrees with callback + def init(arg) do # arg comes from 2nd arg of GenServer.start + {:ok, arg} + end + + @impl true + def handle_call(:value, _from, state) do + {:reply, state, state} # {:reply, actual_reply, new_state} + end + + @impl true + def handle_cast({:inc, amt}, state) do + {:noreply, state + amt} # {:noreply, new_state} + end + + @impl true + def handle_cast({:dec, amt}, state) do + {:noreply, state - amt} # {:noreply, new_state} + end +end diff --git a/lectures/03/counter/mix.exs b/lectures/03/counter/mix.exs new file mode 100644 index 0000000..bab6814 --- /dev/null +++ b/lectures/03/counter/mix.exs @@ -0,0 +1,28 @@ +defmodule Counter.MixProject do + use Mix.Project + + def project do + [ + app: :counter, + version: "0.1.0", + elixir: "~> 1.17", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/lectures/03/counter/test/counter_test.exs b/lectures/03/counter/test/counter_test.exs new file mode 100644 index 0000000..99884d6 --- /dev/null +++ b/lectures/03/counter/test/counter_test.exs @@ -0,0 +1,8 @@ +defmodule CounterTest do + use ExUnit.Case + doctest Counter + + test "greets the world" do + assert Counter.hello() == :world + end +end diff --git a/lectures/03/counter/test/test_helper.exs b/lectures/03/counter/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/lectures/03/counter/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lectures/03/data b/lectures/03/data new file mode 100644 index 0000000..1279d49 --- /dev/null +++ b/lectures/03/data @@ -0,0 +1,2 @@ +a12345678 homer simpson comp4958 100 comp4959 49 +a22222222 bart simpson comp2510 99 diff --git a/lectures/03/lec3.ex b/lectures/03/lec3.ex new file mode 100644 index 0000000..9617869 --- /dev/null +++ b/lectures/03/lec3.ex @@ -0,0 +1,76 @@ +# how to implement a generic server +# motivation for GenServer +defmodule GenericServer do + ### Server Implementation ### + + # pass in a module 'm', since module names are just atoms + # more generic, take in starting args and pass to modules + # init function to do whatever is required beforehand + def start(m, arg) do + state = m.init(arg) + spawn(fn -> loop(m, state) end) + end + + defp loop(m, state) do + receive do + {:call, msg, from} -> + {reply, new_state} = m.handle_call(msg, from, state) + send(from, {self(), reply}) + loop(m, new_state) + + {:cast, msg} -> + new_state = m.handle_cast(msg, state) + loop(m, new_state) + end + end + + ### Client API ### + def call(pid, msg, timeout \\ 5000) do + send(pid, {:call, msg, self()}) + + receive do + # pin operator, reply must have same pid as call argument pid + {^pid, x} -> x + after + timeout -> + :timeout + end + end + + def cast(pid, msg) do + send(pid, {:cast, msg}) + end +end + +defmodule Counter do + ### Server Implementation ### + def start(file) do + GenericServer.start(__MODULE__, file) + end + + def init(file) do + with {:ok, content} <- File.read(file), + {n, _} <- Integer.parse(content) do + n + end + if is_integer(val), do: val, else: 0 + end + + def handle_call(:value, _from, state) do + # -> {reply, new_state} in GenericServer + {state, state} + end + + def handle_cast({:inc, amt}, state) do + state + amt + end + + ### Client API ### + def value(pid) do + GenericServer.call(pid, :value) + end + + def inc(pid, amt \\ 1) do + GenericServer.cast(pid, {:inc, amt}) + end +end diff --git a/lectures/03/notes.ex b/lectures/03/notes.ex new file mode 100644 index 0000000..0537825 --- /dev/null +++ b/lectures/03/notes.ex @@ -0,0 +1,25 @@ +### Streams + +0..1_000_000 |> Enum.map(& &1 * &1) |> Enum.filter(& rem(&1, 8) == 0) |> Enum.sum +# & &1 * &1 is equivalent to +# fn x -> x * x end + +0..1_000_000 |> Steam.map(& &1 * &1) |> Steam.filter(& rem(&1, 8) == 0) |> Enum.sum + +fibs = Stream.unfold({0, 1}, fn {a, b} -> {a, {b, a+b}} end) +fibs |> Enum.take(20); + +# fizzbuzz infstream +nats = Stream.iterate(1, & &1 + 1) +# Stream.iterate(1, fn x -> x + 1) is an inf stream because we can take up to n with no limit +nats |> Enum.take(10) + +fizz = Stream.cycle(["", "", "fizz"]) +fizz |> Enum.take(10) +buzz = Stream.cycle(["", "", "", "", "buzz"]) +buzz |> Enum.take(10) + +fizzbuzz = Stream.zip_with([nats, fizz, buzz], fn [n, f, b] -> + if f == "" && b == "", do: n, + else: f <> b # concat f with b +end) diff --git a/lectures/03/rcounter/.formatter.exs b/lectures/03/rcounter/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/lectures/03/rcounter/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lectures/03/rcounter/.gitignore b/lectures/03/rcounter/.gitignore new file mode 100644 index 0000000..7e1be71 --- /dev/null +++ b/lectures/03/rcounter/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +rcounter-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/lectures/03/rcounter/README.md b/lectures/03/rcounter/README.md new file mode 100644 index 0000000..19a53c7 --- /dev/null +++ b/lectures/03/rcounter/README.md @@ -0,0 +1,21 @@ +# Rcounter + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `rcounter` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:rcounter, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/lectures/03/rcounter/lib/counter/worker.ex b/lectures/03/rcounter/lib/counter/worker.ex new file mode 100644 index 0000000..523cdca --- /dev/null +++ b/lectures/03/rcounter/lib/counter/worker.ex @@ -0,0 +1,43 @@ +defmodule Counter.Worker do + use GenServer # use builtint Elixir Generic Server + + ### Client API ### + def inc(amt \\ 1) do + GenServer.cast(__MODULE__, {:inc, amt}) + end + + def dec(amt \\ 1) do + GenServer.cast(__MODULE__, {:dec, amt}) + end + + def value() do + GenServer.call(__MODULE__, :value) + end + + ### Server Impl ### + def start(n \\ 0) do + # use module name as name for registered server + # now inc, dec, and value dont need pid, and can use __MODULE__ + GenServer.start(__MODULE__, n, name: __MODULE__) + end + + @impl true # annotation that signature agrees with callback + def init(arg) do # arg comes from 2nd arg of GenServer.start + {:ok, arg} + end + + @impl true + def handle_call(:value, _from, state) do + {:reply, state, state} # {:reply, actual_reply, new_state} + end + + @impl true + def handle_cast({:inc, amt}, state) do + {:noreply, state + amt} # {:noreply, new_state} + end + + @impl true + def handle_cast({:dec, amt}, state) do + {:noreply, state - amt} # {:noreply, new_state} + end +end diff --git a/lectures/03/rcounter/mix.exs b/lectures/03/rcounter/mix.exs new file mode 100644 index 0000000..2e45316 --- /dev/null +++ b/lectures/03/rcounter/mix.exs @@ -0,0 +1,28 @@ +defmodule Rcounter.MixProject do + use Mix.Project + + def project do + [ + app: :rcounter, + version: "0.1.0", + elixir: "~> 1.17", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/lectures/03/rcounter/test/rcounter_test.exs b/lectures/03/rcounter/test/rcounter_test.exs new file mode 100644 index 0000000..7541bb8 --- /dev/null +++ b/lectures/03/rcounter/test/rcounter_test.exs @@ -0,0 +1,8 @@ +defmodule RcounterTest do + use ExUnit.Case + doctest Rcounter + + test "greets the world" do + assert Rcounter.hello() == :world + end +end diff --git a/lectures/03/rcounter/test/test_helper.exs b/lectures/03/rcounter/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/lectures/03/rcounter/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lectures/03/student.ex b/lectures/03/student.ex new file mode 100644 index 0000000..ca957cc --- /dev/null +++ b/lectures/03/student.ex @@ -0,0 +1,54 @@ +### Structs + +defmodule Name do + # defaults to john doe + # or without defaults + # defstruct [:first, :last] + defstruct [first: "john", last: "doe"] + + def new(firstname, lastname) do + %Name{first: firstname, last: lastname} + end +end + +defmodule Student do + # default empty id, default name, default empty score map + defstruct [id: "", name: %Name{}, scores: %{}] + + # default scores to empty map + def new(id, firstname, lastname, scores \\ %{}) do + %Student{id: id, name: Name.new(firstname, lastname), scores: scores} + end + + # returns a new map + def add_score(%Student{} = s, course, score) do + %Student{s | scores: Map.put(s.scores, course, score)} + end + + def parse(line) do + case String.split(line) do + [id, firstname, lastname | rest] -> + # chunk_every returns a list of lists + scores = for [c, s] <- Enum.chunk_every(rest, 2), into: %{}, + do: {c, String.to_integer(s)} + Student.new(id, firstname, lastname, scores) + + _ -> :error + end + end + + def read_data(file) do + {:ok, content} = File.read(file) + content |> String.split("\n") |> Enum.map(&parse/1) |> Enum.filter(& &1 != :error) + end +end + +# s = Student.new("a22222222", "homer", "simpson") +# # struct pattern matching +# %Student{id: x} = s +# x # -> a22222222 + +# %Student{} = s # doesnt match anything +# # but ensures that s is a Student + +# Student.add_score(s, "comp4958", 55) diff --git a/note.md b/note.md index ce8456e..7fb0cac 100644 --- a/note.md +++ b/note.md @@ -6,3 +6,7 @@ `.exs` is for interpreted code `iex file.ex` + +# projects +`mix new ` creates a prject dir with name +`iex -S mix` runs mix script