subtree(users/wpcarro): docking briefcase at '24f5a642'

git-subtree-dir: users/wpcarro
git-subtree-mainline: 464bbcb15c
git-subtree-split: 24f5a642af
Change-Id: I6105b3762b79126b3488359c95978cadb3efa789
This commit is contained in:
Vincent Ambo 2021-12-14 01:51:19 +03:00
commit 019f8fd211
766 changed files with 175420 additions and 0 deletions

View file

@ -0,0 +1,8 @@
defmodule App do
use Application
@impl true
def start(_type, _args) do
Sup.start_link()
end
end

View file

@ -0,0 +1,41 @@
defmodule Cache do
@moduledoc """
Cache is an in-memory key-value store.
"""
use Agent
@doc """
Inititalize the key-value store.
"""
def start_link(_) do
Agent.start_link(fn -> %{} end, name: __MODULE__)
end
@doc """
Attempt to return the value stored at `key`
"""
def get(key) do
Agent.get(__MODULE__, &Map.get(&1, key))
end
@doc """
Write the `value` under the `key`. Last writer wins.
"""
def put(key, value) do
Agent.update(__MODULE__, &Map.put(&1, key, value))
end
@doc """
List the contents of the cache. Useful for debugging purposes.
"""
def list() do
Agent.get(__MODULE__, & &1)
end
@doc """
Invalidate the entire cache.
"""
def clear() do
Agent.update(__MODULE__, fn _ -> %{} end)
end
end

View file

@ -0,0 +1,22 @@
defmodule Extras do
@moduledoc """
Hosts utility functions intended to supplement the standard library.
"""
@doc """
Return an ascending range starting at `a` and ending at `b` (exclusive).
## Examples
iex> Extras.range(2, 5)
[2, 3, 4]
"""
def range(a, b) do
if b <= a do
[]
else
[a] ++ range(a + 1, b)
end
end
end

View file

@ -0,0 +1,26 @@
defmodule Math do
@moduledoc """
Math utilities.
"""
alias Extras
@doc """
Returns the prime factors for `n`.
## Examples
iex> Math.factor(15)
[3, 5]
"""
def factor(1), do: []
def factor(n) do
Extras.range(2, n - 1)
|> Enum.find(&(rem(n, &1) == 0))
|> case do
nil -> [n]
x -> [x | factor(div(n, x))]
end
end
end

View file

@ -0,0 +1,86 @@
defmodule Router do
use Plug.Router
use Plug.Debugger
require Logger
plug(Plug.Logger, log: :debug)
plug(Plug.Parsers, parsers: [:urlencoded])
plug(:match)
plug(:dispatch)
@usage """
Usage: Try querying some of the following endpoints...
GET /
GET /help
GET /semiprime?number=<integer>
GET /semiprimes?numbers=<comma-separated-integers>
"""
get "/" do
send_resp(conn, 200, "Welcome to Semiprimes Service!\n\n#{@usage}")
end
get "/help" do
send_resp(conn, 200, @usage)
end
get "/semiprime" do
case conn |> Map.get(:query_params) |> Map.get("number") do
nil ->
send_resp(conn, 400, "You must pass an integer as a query parameter. #{@usage}")
val ->
case Integer.parse(val) do
{n, ""} ->
send_resp(conn, 200, semiprime_response(n))
_ ->
send_resp(conn, 400, "We could not parse the number you provided.\n\n#{@usage}")
end
end
end
get "/semiprimes" do
case conn |> Map.get(:query_params) |> Map.get("numbers") do
nil ->
send_resp(
conn,
400,
"You must pass a comma-separated list of integers as a query parameter.\n\n#{@usage}"
)
xs ->
response =
xs
|> String.split(",")
|> Stream.map(&Integer.parse/1)
|> Stream.filter(fn
{n, ""} -> true
_ -> false
end)
|> Stream.map(fn {n, ""} -> semiprime_response(n) end)
|> Enum.join("\n")
send_resp(conn, 200, response)
end
end
match _ do
send_resp(conn, 404, "Not found.")
end
################################################################################
# Utils
################################################################################
defp semiprime_response(n) do
case Server.semiprime(n) do
nil ->
"#{n} is not a semiprime. Try another number!"
{hit_or_miss, factors} ->
response = "#{n} is a semiprime! Its factors are #{Enum.join(factors, " and ")}."
"Cache #{Atom.to_string(hit_or_miss)} - #{response}"
end
end
end

View file

@ -0,0 +1,33 @@
defmodule Server do
@moduledoc """
Documentation for `Server`.
"""
@doc """
If `n` contains exactly two prime factors, return those prime factors;
otherwise, return nothing.
"""
def semiprime(n) do
case Cache.get(n) do
nil ->
case do_semiprime(n) do
nil ->
nil
res ->
Cache.put(n, res)
{:miss, res}
end
hit ->
{:hit, hit}
end
end
defp do_semiprime(n) do
case Math.factor(n) do
[_, _] = res -> res
_ -> nil
end
end
end

View file

@ -0,0 +1,23 @@
defmodule Sup do
@moduledoc """
Top-level supervisor for our OTP application. For now, this supervisor starts
and monitors our cache.
"""
use Supervisor
alias Plug.Adapters.Cowboy
def start_link(opts \\ []) do
Supervisor.start_link(__MODULE__, :ok, opts)
end
@impl true
def init(:ok) do
children = [
Cache,
Cowboy.child_spec(scheme: :http, plug: Router, options: [port: 8000])
]
Supervisor.init(children, strategy: :one_for_one)
end
end