subtree(users/wpcarro): docking briefcase at '24f5a642'
git-subtree-dir: users/wpcarro git-subtree-mainline:464bbcb15cgit-subtree-split:24f5a642afChange-Id: I6105b3762b79126b3488359c95978cadb3efa789
This commit is contained in:
commit
019f8fd211
766 changed files with 175420 additions and 0 deletions
8
users/wpcarro/assessments/semiprimes/server/lib/app.ex
Normal file
8
users/wpcarro/assessments/semiprimes/server/lib/app.ex
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
defmodule App do
|
||||
use Application
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
Sup.start_link()
|
||||
end
|
||||
end
|
||||
41
users/wpcarro/assessments/semiprimes/server/lib/cache.ex
Normal file
41
users/wpcarro/assessments/semiprimes/server/lib/cache.ex
Normal 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
|
||||
22
users/wpcarro/assessments/semiprimes/server/lib/extras.ex
Normal file
22
users/wpcarro/assessments/semiprimes/server/lib/extras.ex
Normal 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
|
||||
26
users/wpcarro/assessments/semiprimes/server/lib/math.ex
Normal file
26
users/wpcarro/assessments/semiprimes/server/lib/math.ex
Normal 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
|
||||
86
users/wpcarro/assessments/semiprimes/server/lib/router.ex
Normal file
86
users/wpcarro/assessments/semiprimes/server/lib/router.ex
Normal 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
|
||||
33
users/wpcarro/assessments/semiprimes/server/lib/server.ex
Normal file
33
users/wpcarro/assessments/semiprimes/server/lib/server.ex
Normal 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
|
||||
23
users/wpcarro/assessments/semiprimes/server/lib/sup.ex
Normal file
23
users/wpcarro/assessments/semiprimes/server/lib/sup.ex
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue