lec03 generic servers

This commit is contained in:
SowinskiBraeden committed 2026-09-22 12:21:27 -07:00
1 parent a579cf11a1
commit 4abfd6b848
23 files changed
+502

No files matched your search

+30
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
\]\j@╦=QШ06JшёRЭ╡к█√и°][yМ ≥▒┴▓╘╦╓t poа ?ъ©x■Фъ■≥.и┤|l╨^ 7├┤С°PHoСлniЮ}2:╒4jh$©╝⌡%⌠?°зa=Ь▌ДЖrWuКBЮ1ХW Из·9Ю╖р8╫
+32
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
+26
View File
@@ -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/
+21
View File
@@ -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 <https://hexdocs.pm/counter>.
+18
View File
@@ -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
+41
View File
@@ -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
+28
View File
@@ -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
@@ -0,0 +1,8 @@
defmodule CounterTest do
use ExUnit.Case
doctest Counter
test "greets the world" do
assert Counter.hello() == :world
end
end
+1
View File
@@ -0,0 +1 @@
ExUnit.start()
+2
View File
@@ -0,0 +1,2 @@
a12345678 homer simpson comp4958 100 comp4959 49
a22222222 bart simpson comp2510 99
+76
View File
@@ -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
+25
View File
@@ -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)
+4
View File
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
+26
View File
@@ -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/
+21
View File
@@ -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 <https://hexdocs.pm/rcounter>.
@@ -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
+28
View File
@@ -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
@@ -0,0 +1,8 @@
defmodule RcounterTest do
use ExUnit.Case
doctest Rcounter
test "greets the world" do
assert Rcounter.hello() == :world
end
end
@@ -0,0 +1 @@
ExUnit.start()
+54
View File
@@ -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)
+4
View File
@@ -6,3 +6,7 @@
`.exs` is for interpreted code
`iex file.ex`
# projects
`mix new <name>` creates a prject dir with name
`iex -S mix` runs mix script