Fix a few typos

Fix a few typos

defmodule Nerves.Env do
  @moduledoc """
  Contains package info for Nerves dependencies

  The Nerves Env is used to load information from dependencies that
  contain a nerves.exs config file in the root of the dependency
  path. Nerves loads this config because it needs access to information
  about Nerves compile time dependencies before any code is compiled.
  """

  alias Nerves.Package

  @doc """
  Starts the Nerves environment agent and loads package information.
  If the Nerves.Env is already started, the function returns
  `{:error, {:already_started, pid}}` with the pid of that process
  """
  @spec start() :: Agent.on_start
  def start do
    Agent.start_link(fn -> load_packages() end, name: __MODULE__)
  end

  @doc """
  Stop the Nerves environment agent.
  """
  @spec stop() :: :ok
  def stop do
    Agent.stop(__MODULE__)
  end

  @doc """
  Check if the env compilers are disabled
  """
  @spec enabled?() :: boolean
  def enabled?() do
    System.get_env("NERVES_ENV_DISABLED") == nil
  end

  @doc """
  Enable the Nerves Env compilers
  """
  @spec enable() :: :ok
  def enable() do
    System.delete_env("NERVES_ENV_DISABLED")
  end

  @doc """
  Disable the Nerves Env compilers
  """
  @spec disable() :: :ok
  def disable() do
    System.put_env("NERVES_ENV_DISABLED", "1")
  end

  @doc """
  Re evaluates the mix file under a different target.

  This allows you to start in one target, like host, but then
  switch to a different target.
  """
  @spec change_target(String.t) :: :no_return
  def change_target(target) do
    unless System.get_env("NERVES_TARGET_CHANGE") do
      System.put_env("MIX_TARGET", target)
      System.put_env("NERVES_TARGET_CHANGE", "1")
      :init.restart
      :timer.sleep(:infinity)
    end
  end

  @doc """
  Cleans the artifacts for the package providers of all specified packages.
  """
  @spec clean([Nerves.Package.t]) :: :ok | {:error, term}
  def clean(pkgs) do
    Enum.each(pkgs, &Package.clean/1)
  end

  @doc """
  Ensures that an application which contains a Nerves package config has
  been loaded into the environment agent.

  ## Options
    * `app` - The atom of the app to load
    * `path` - Optional path for the app
  """
  @spec ensure_loaded(app :: atom, path :: String.t) ::
    {:ok, Nerves.Package.t} | {:error, term}
  def ensure_loaded(app, path \\ nil) do
    path = path || File.cwd!
    if File.exists?(Package.config_path(path)) do
      packages = Agent.get(__MODULE__, &(&1))
      case Enum.find(packages, & &1.app == app) do
        nil ->
          case Package.load_config({app, path}) do
            %Package{} = package ->
              Agent.update(__MODULE__, fn(packages) ->
                [package | packages]
              end)
              {:ok, package}
            error ->
              error
          end
        package -> {:ok, package}
      end
    else
      {:error, "Nerves package config for #{inspect app} was not found at #{path}"}
    end
  end

  @doc """
  Returns the architecture for the host system.

  ## Example return values
    "x86_64"
    "arm"
  """
  @spec host_arch() :: String.t
  def host_arch() do
    :erlang.system_info(:system_architecture)
    |> to_string
    |> parse_arch
  end

  @doc false
  def parse_arch(arch) when is_binary(arch) do
    arch
    |> String.split("-")
    |> parse_arch
  end
  @doc false
  def parse_arch(arch) when is_list(arch) do
    arch = List.first(arch)
    case arch do
      <<"win", _tail :: binary>> -> "x86_64"
      arch ->
        if String.contains?(arch, "arm") do
          "arm"
        else
          "x86_64"
        end
    end
  end

  @doc """
  Returns the platform for the host system.

  ## Example return values
    "win"
    "linux"
    "darwin"
  """
  @spec host_platform() :: String.t
  def host_platform() do
    :erlang.system_info(:system_architecture)
    |> to_string
    |> parse_platform
  end
  @doc false
  def parse_platform(platform) when is_binary(platform) do
    platform
    |> String.split("-")
    |> parse_platform
  end
  @doc false
  def parse_platform(platform) when is_list(platform) do
    case platform do
      [<<"win", _tail :: binary>> | _] ->
        "win"
      [_ , _, "linux" | _] ->
        "linux"
      [_, _, <<"darwin", _tail :: binary>> | _] ->
        "darwin"
      _ ->
        Mix.raise "Could not determine your host platform from system: #{platform}"
    end
  end

  @doc """
  Lists all Nerves packages loaded in the Nerves environment.
  """
  @spec packages() :: [Nerves.Package.t]
  def packages do
    Agent.get(__MODULE__, &(&1)) || Mix.raise "Nerves packages are not loaded"
  end

  @doc """
  Gets a package by app name.
  """
  @spec package(name :: atom) ::
    Nerves.Package.t | nil
  def package(name) do
      packages()
      |> Enum.filter(& &1.app == name)
      |> List.first
  end

  @doc """
  Lists packages by package type.
  """
  @spec packages_by_type(type :: String.t) ::
    [Nerves.Package.t]
  def packages_by_type(type, packages \\ nil) do
    (packages || packages())
    |> Enum.filter(& &1.type === type)
  end

  @doc """
  Helper function for returning the system type package
  """
  @spec system() ::
    Nerves.Package.t
  def system do
    system =
      packages_by_type(:system)
      |> List.first
    system
  end

  @doc """
  Helper function for returning the system_platform type package
  """
  @spec system_platform() ::
    Nerves.Package.t
  def system_platform do
    system().platform
  end

  @doc """
  Helper function for returning the toolchain type package
  """
  @spec toolchain() ::
    Nerves.Package.t
  def toolchain do
    toolchain =
      packages_by_type(:toolchain)
      |> List.first
    toolchain
  end

  @doc """
  Helper function for returning the toolchain_platform type package
  """
  @spec toolchain_platform() ::
    Nerves.Package.t
  def toolchain_platform do
    toolchain().platform
  end

  @doc """
  Export environment variables used by Elixir, Erlang, C/C++ and other tools
  so that they use Nerves toolchain parameters and not the host's.

  For a comprehensive list of environment variables, see the documentation
  for the package defining system_platform.
  """
  @spec bootstrap() :: :ok
  def bootstrap do

    nerves_system_path = system_path()
    nerves_toolchain_path = toolchain_path()

    [{"NERVES_SYSTEM", nerves_system_path},
     {"NERVES_TOOLCHAIN", nerves_toolchain_path},
     {"NERVES_APP", File.cwd!}]
    |> Enum.each(fn({k, v}) ->
      cond do
        v == nil -> Mix.shell.info "#{k} is unset"
        File.dir?(v) != true ->
          Mix.shell.error """
          #{k} is set to a path which does not exist:
          #{v}
          """
          exit({:shutdown, 1})
        true -> System.put_env(k, v)
      end
    end)

    if nerves_system_path != nil and File.dir?(nerves_system_path) do
      # Bootstrap the build platform
      platform = Nerves.Env.system.platform
      # Pre 0.4.0 Legacy
      platform = platform || Nerves.Env.system.config[:build_platform]
      ## end re 0.4.0 Legacy
      pkg =
        Nerves.Env.packages_by_type(:system_platform)
        |> List.first
      platform.bootstrap(pkg)
    end
  end

  @doc false
  defp load_packages do
    Mix.Project.deps_paths
    |> Map.put(Mix.Project.config[:app], File.cwd!)
    |> Enum.filter(fn({_, path}) ->
      Package.config_path(path)
      |> File.exists?
    end)
    |> Enum.map(&Package.load_config/1)
    |> validate_packages
  end

  @doc false
  defp validate_packages(packages) do
    for type <- [:system, :toolchain] do
      packages_by_type(type, packages)
      |> validate_one(type)
    end
    packages
  end

  @doc false
  defp validate_one(packages, type) when length(packages) > 1 do
    packages = Enum.map(packages, &(Map.get(&1, :app)))
    Mix.raise """
    Your mix project cannot contain more than one #{type} for the target.
    Your dependancies for the target contian the following #{type}s:
    #{Enum.join(packages, ~s/ /)}
    """
  end
  @doc false
  defp validate_one(packages, _type), do: packages

  @doc false
  defp toolchain_path do
    case Nerves.Env.toolchain do
      nil -> nil
      toolchain ->
        Nerves.Package.Artifact.dir(toolchain, toolchain)
    end
  end
  @doc false
  defp system_path do
    case Nerves.Env.system do
      nil -> nil
      system ->
        toolchain = Nerves.Env.toolchain
        Nerves.Package.Artifact.dir(system, toolchain)
    end
  end

  # Pre 0.4.0 Legacy
  @doc false
  def initialize, do: start()
  @doc false
  def stale? do
    system_manifest =
      system_path()
      |> Path.join(".nerves.lock")
      |> Path.expand

    if stale_check_manifest(system_manifest) do
      false
    else
      true
    end
  end
  @doc false
  def stale_check_manifest(manifest) do
    case File.read(manifest) do
      {:ok, file} ->
        file
        |> :erlang.binary_to_term
        |> Keyword.equal?(packages())
      _ -> false
    end
  end
  @doc false
  def deps, do: packages()
  @doc false
  def deps_by_type(type), do: packages_by_type(type)
  ## End Pre 0.4.0 Legacy

end

defmodule Nerves.Env do
  @moduledoc """
  Contains package info for Nerves dependencies

  The Nerves Env is used to load information from dependencies that
  contain a nerves.exs config file in the root of the dependency
  path. Nerves loads this config because it needs access to information
  about Nerves compile time dependencies before any code is compiled.
  """

  alias Nerves.Package

  @doc """
  Starts the Nerves environment agent and loads package information.
  If the Nerves.Env is already started, the function returns
  `{:error, {:already_started, pid}}` with the pid of that process
  """
  @spec start() :: Agent.on_start
  def start do
    Agent.start_link(fn -> load_packages() end, name: __MODULE__)
  end

  @doc """
  Stop the Nerves environment agent.
  """
  @spec stop() :: :ok
  def stop do
    Agent.stop(__MODULE__)
  end

  @doc """
  Check if the env compilers are disabled
  """
  @spec enabled?() :: boolean
  def enabled?() do
    System.get_env("NERVES_ENV_DISABLED") == nil
  end

  @doc """
  Enable the Nerves Env compilers
  """
  @spec enable() :: :ok
  def enable() do
    System.delete_env("NERVES_ENV_DISABLED")
  end

  @doc """
  Disable the Nerves Env compilers
  """
  @spec disable() :: :ok
  def disable() do
    System.put_env("NERVES_ENV_DISABLED", "1")
  end

  @doc """
  Re evaluates the mix file under a different target.

  This allows you to start in one target, like host, but then
  switch to a different target.
  """
  @spec change_target(String.t) :: :no_return
  def change_target(target) do
    unless System.get_env("NERVES_TARGET_CHANGE") do
      System.put_env("MIX_TARGET", target)
      System.put_env("NERVES_TARGET_CHANGE", "1")
      :init.restart
      :timer.sleep(:infinity)
    end
  end

  @doc """
  Cleans the artifacts for the package providers of all specified packages.
  """
  @spec clean([Nerves.Package.t]) :: :ok | {:error, term}
  def clean(pkgs) do
    Enum.each(pkgs, &Package.clean/1)
  end

  @doc """
  Ensures that an application which contins a Nerves package config has
  been loaded into the environment agent.

  ## Options
    * `app` - The atom of the app to load
    * `path` - Optional path for the app
  """
  @spec ensure_loaded(app :: atom, path :: String.t) ::
    {:ok, Nerves.Package.t} | {:error, term}
  def ensure_loaded(app, path \\ nil) do
    path = path || File.cwd!
    if File.exists?(Package.config_path(path)) do
      packages = Agent.get(__MODULE__, &(&1))
      case Enum.find(packages, & &1.app == app) do
        nil ->
          case Package.load_config({app, path}) do
            %Package{} = package ->
              Agent.update(__MODULE__, fn(packages) ->
                [package | packages]
              end)
              {:ok, package}
            error ->
              error
          end
        package -> {:ok, package}
      end
    else
      {:error, "Nerves package config for #{inspect app} was not fount at #{path}"}
    end
  end

  @doc """
  Returns the archetecture for the host system.

  ## Example return values
    "x86_64"
    "arm"
  """
  @spec host_arch() :: String.t
  def host_arch() do
    :erlang.system_info(:system_architecture)
    |> to_string
    |> parse_arch
  end

  @doc false
  def parse_arch(arch) when is_binary(arch) do
    arch
    |> String.split("-")
    |> parse_arch
  end
  @doc false
  def parse_arch(arch) when is_list(arch) do
    arch = List.first(arch)
    case arch do
      <<"win", _tail :: binary>> -> "x86_64"
      arch ->
        if String.contains?(arch, "arm") do
          "arm"
        else
          "x86_64"
        end
    end
  end

  @doc """
  Returns the platform for the host system.

  ## Example return values
    "win"
    "linux"
    "darwin"
  """
  @spec host_platform() :: String.t
  def host_platform() do
    :erlang.system_info(:system_architecture)
    |> to_string
    |> parse_platform
  end
  @doc false
  def parse_platform(platform) when is_binary(platform) do
    platform
    |> String.split("-")
    |> parse_platform
  end
  @doc false
  def parse_platform(platform) when is_list(platform) do
    case platform do
      [<<"win", _tail :: binary>> | _] ->
        "win"
      [_ , _, "linux" | _] ->
        "linux"
      [_, _, <<"darwin", _tail :: binary>> | _] ->
        "darwin"
      _ ->
        Mix.raise "Could not determine your host platform from system: #{platform}"
    end
  end

  @doc """
  Lists all Nerves packages loaded in the Nerves environment.
  """
  @spec packages() :: [Nerves.Package.t]
  def packages do
    Agent.get(__MODULE__, &(&1)) || Mix.raise "Nerves packages are not loaded"
  end

  @doc """
  Gets a package by app name.
  """
  @spec package(name :: atom) ::
    Nerves.Package.t | nil
  def package(name) do
      packages()
      |> Enum.filter(& &1.app == name)
      |> List.first
  end

  @doc """
  Lists packages by package type.
  """
  @spec packages_by_type(type :: String.t) ::
    [Nerves.Package.t]
  def packages_by_type(type, packages \\ nil) do
    (packages || packages())
    |> Enum.filter(& &1.type === type)
  end

  @doc """
  Helper function for returning the system type package
  """
  @spec system() ::
    Nerves.Package.t
  def system do
    system =
      packages_by_type(:system)
      |> List.first
    system
  end

  @doc """
  Helper function for returning the system_platform type package
  """
  @spec system_platform() ::
    Nerves.Package.t
  def system_platform do
    system().platform
  end

  @doc """
  Helper function for returning the toolchain type package
  """
  @spec toolchain() ::
    Nerves.Package.t
  def toolchain do
    toolchain =
      packages_by_type(:toolchain)
      |> List.first
    toolchain
  end

  @doc """
  Helper function for returning the toolchain_platform type package
  """
  @spec toolchain_platform() ::
    Nerves.Package.t
  def toolchain_platform do
    toolchain().platform
  end

  @doc """
  Export environment variables used by Elixir, Erlang, C/C++ and other tools
  so that they use Nerves toolchain parameters and not the host's.

  For a comprehensive list of environment variables, see the documentation
  for the package defining system_platform.
  """
  @spec bootstrap() :: :ok
  def bootstrap do

    nerves_system_path = system_path()
    nerves_toolchain_path = toolchain_path()

    [{"NERVES_SYSTEM", nerves_system_path},
     {"NERVES_TOOLCHAIN", nerves_toolchain_path},
     {"NERVES_APP", File.cwd!}]
    |> Enum.each(fn({k, v}) ->
      cond do
        v == nil -> Mix.shell.info "#{k} is unset"
        File.dir?(v) != true ->
          Mix.shell.error """
          #{k} is set to a path which does not exist:
          #{v}
          """
          exit({:shutdown, 1})
        true -> System.put_env(k, v)
      end
    end)

    if nerves_system_path != nil and File.dir?(nerves_system_path) do
      # Bootstrap the build platform
      platform = Nerves.Env.system.platform
      # Pre 0.4.0 Legacy
      platform = platform || Nerves.Env.system.config[:build_platform]
      ## end re 0.4.0 Legacy
      pkg =
        Nerves.Env.packages_by_type(:system_platform)
        |> List.first
      platform.bootstrap(pkg)
    end
  end

  @doc false
  defp load_packages do
    Mix.Project.deps_paths
    |> Map.put(Mix.Project.config[:app], File.cwd!)
    |> Enum.filter(fn({_, path}) ->
      Package.config_path(path)
      |> File.exists?
    end)
    |> Enum.map(&Package.load_config/1)
    |> validate_packages
  end

  @doc false
  defp validate_packages(packages) do
    for type <- [:system, :toolchain] do
      packages_by_type(type, packages)
      |> validate_one(type)
    end
    packages
  end

  @doc false
  defp validate_one(packages, type) when length(packages) > 1 do
    packages = Enum.map(packages, &(Map.get(&1, :app)))
    Mix.raise """
    Your mix project cannot contain more than one #{type} for the target.
    Your dependancies for the target contian the following #{type}s:
    #{Enum.join(packages, ~s/ /)}
    """
  end
  @doc false
  defp validate_one(packages, _type), do: packages

  @doc false
  defp toolchain_path do
    case Nerves.Env.toolchain do
      nil -> nil
      toolchain ->
        Nerves.Package.Artifact.dir(toolchain, toolchain)
    end
  end
  @doc false
  defp system_path do
    case Nerves.Env.system do
      nil -> nil
      system ->
        toolchain = Nerves.Env.toolchain
        Nerves.Package.Artifact.dir(system, toolchain)
    end
  end

  # Pre 0.4.0 Legacy
  @doc false
  def initialize, do: start()
  @doc false
  def stale? do
    system_manifest =
      system_path()
      |> Path.join(".nerves.lock")
      |> Path.expand

    if stale_check_manifest(system_manifest) do
      false
    else
      true
    end
  end
  @doc false
  def stale_check_manifest(manifest) do
    case File.read(manifest) do
      {:ok, file} ->
        file
        |> :erlang.binary_to_term
        |> Keyword.equal?(packages())
      _ -> false
    end
  end
  @doc false
  def deps, do: packages()
  @doc false
  def deps_by_type(type), do: packages_by_type(type)
  ## End Pre 0.4.0 Legacy

end


Tweak formatting

Tweak formatting

defmodule TapRacer.PlayChannel do
  use Phoenix.Channel

  def join("play", %{"name" => name}, socket) do
    user_id = UUID.uuid4()
    TapRacer.Endpoint.broadcast(
      "console", "join", %{name: name, user_id: user_id}
    )
    {:ok, assign(socket, :user_id, user_id)}
  end

  def terminate(_reason, socket) do
    TapRacer.Endpoint.broadcast(
      "console", "terminate", %{user_id: socket.assigns.user_id}
    )
  end

  def handle_in("tap", _payload, socket) do
    TapRacer.Endpoint.broadcast(
      "console", "tap", %{user_id: socket.assigns.user_id}
    )
    {:noreply, socket}
  end
end

defmodule TapRacer.PlayChannel do
  use Phoenix.Channel

  def join("play", %{"name" => name}, socket) do
    user_id = UUID.uuid4()
    TapRacer.Endpoint.broadcast! "console", "join",
                                 %{name: name, user_id: user_id}
    {:ok, assign(socket, :user_id, user_id)}
  end

  def terminate(_reason, socket) do
    TapRacer.Endpoint.broadcast! "console", "terminate",
                                 %{user_id: socket.assigns.user_id}
  end

  def handle_in("tap", _payload, socket) do
    TapRacer.Endpoint.broadcast! "console", "tap",
                                 %{user_id: socket.assigns.user_id}
    {:noreply, socket}
  end
end


Fix typo for password view template

Fix typo for password view template

defmodule <%= module %> do
  use Phoenix.View, root: "<%= templates_path %>"
  use Phoenix.HTML

  alias Sentinel.Config
end

defmodule <%= inspect module %> do
  use Phoenix.View, root: "<%= templates_path %>"
  use Phoenix.HTML

  alias Sentinel.Config
end


remove test tag

remove test tag

require IEx
defmodule PhxOembed.CardControllerTest do
  use PhxOembed.ConnCase

  alias PhxOembed.Endpoint
  setup %{conn: conn} do
    {:ok, conn: put_req_header(conn, "accept", "application/json")}
  end

  test "shows the right card", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site, title: "test card")
    url = make_url(site.protocol, site.domain, card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    resp = json_response(conn, 200)
    assert resp["title"] == card.title
    assert resp["url"] == url
  end

  test "returns xml when requested", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site, title: "test card")
    url = make_url(site.protocol, site.domain, card.path)
    resp = get(conn, site_card_path(Endpoint, :show, site.id, url: url, format: "xml"))
    assert resp.status == 200
    assert {"content-type", "text/xml; charset=utf-8"} = List.first(resp.resp_headers)
  end

  test "returns 404 when the site id != the card's site", %{conn: conn} do
    site = create(:site)
    site2 = create(:site)
    card = create(:card, site: site)
    url = make_url(site.protocol, site.domain, card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site2.id, url: url))
    assert json_response(conn, 404) == nil
  end

  test "returns 404 when the domain in the url != site domain", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site)
    url = make_url(site.protocol, "fakedomain.com", card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    assert json_response(conn, 404) == nil
  end

  test "returns 404 when card does not exist", %{conn: conn} do
    site = create(:site)
    create(:card, site: site)
    url = make_url(site.protocol, site.domain, "doesnotexist")
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    assert json_response(conn, 404) == nil
  end

  def make_url(protocol, domain, path) do
    protocol <> "://" <> domain <> "/" <> path
  end
end

require IEx
defmodule PhxOembed.CardControllerTest do
  use PhxOembed.ConnCase

  alias PhxOembed.Endpoint
  setup %{conn: conn} do
    {:ok, conn: put_req_header(conn, "accept", "application/json")}
  end

  test "shows the right card", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site, title: "test card")
    url = make_url(site.protocol, site.domain, card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    resp = json_response(conn, 200)
    assert resp["title"] == card.title
    assert resp["url"] == url
  end

  @tag :wip
  test "returns xml when requested", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site, title: "test card")
    url = make_url(site.protocol, site.domain, card.path)
    resp = get(conn, site_card_path(Endpoint, :show, site.id, url: url, format: "xml"))
    assert resp.status == 200
    assert {"content-type", "text/xml; charset=utf-8"} = List.first(resp.resp_headers)
  end

  test "returns 404 when the site id != the card's site", %{conn: conn} do
    site = create(:site)
    site2 = create(:site)
    card = create(:card, site: site)
    url = make_url(site.protocol, site.domain, card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site2.id, url: url))
    assert json_response(conn, 404) == nil
  end

  test "returns 404 when the domain in the url != site domain", %{conn: conn} do
    site = create(:site)
    card = create(:card, site: site)
    url = make_url(site.protocol, "fakedomain.com", card.path)
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    assert json_response(conn, 404) == nil
  end

  test "returns 404 when card does not exist", %{conn: conn} do
    site = create(:site)
    create(:card, site: site)
    url = make_url(site.protocol, site.domain, "doesnotexist")
    conn = get(conn, site_card_path(Endpoint, :show, site.id, url: url))
    assert json_response(conn, 404) == nil
  end

  def make_url(protocol, domain, path) do
    protocol <> "://" <> domain <> "/" <> path
  end
end


Test if notifications were cleaned up.

Test if notifications were cleaned up.

defmodule CoursePlanner.NotifierTest do
  use CoursePlanner.ModelCase
  doctest CoursePlanner.Notifier

  import CoursePlanner.Factory
  import Swoosh.TestAssertions

  alias CoursePlanner.{Notifier, Notifications, User}

  test "save notification to send later" do
    user = insert(:user)
    notification = Notifications.new()
    |> Notifications.type(:user_modified)
    |> Notifications.resource_path("/")
    |> Notifications.to(user)

    Notifier.handle_cast({:save_email, notification}, [])
    saved_user = Repo.get(User, user.id) |> Repo.preload(:notifications)
    [saved_notification] = saved_user.notifications
    assert saved_notification.type == "user_modified"
  end

  test "send saved notifications" do
    user = insert(:user)
    insert(:notification, %{user_id: user.id})
    insert(:notification, %{user_id: user.id})
    insert(:notification, %{user_id: user.id})

    saved_user = Repo.get(User, user.id) |> Repo.preload(:notifications)

    Notifier.handle_cast({:notify_all, saved_user}, [])
    assert_email_sent subject: "Activity Summary"
    sent_user = Repo.get(User, user.id) |> Repo.preload(:notifications)
    assert sent_user.notifications == []
  end

end

defmodule CoursePlanner.NotifierTest do
  use CoursePlanner.ModelCase
  doctest CoursePlanner.Notifier

  import CoursePlanner.Factory
  import Swoosh.TestAssertions

  alias CoursePlanner.{Notifier, Notifications, User}

  test "save notification to send later" do
    user = insert(:user)
    notification = Notifications.new()
    |> Notifications.type(:user_modified)
    |> Notifications.resource_path("/")
    |> Notifications.to(user)

    Notifier.handle_cast({:save_email, notification}, [])
    saved_user = Repo.get(User, user.id) |> Repo.preload(:notifications)
    [saved_notification] = saved_user.notifications
    assert saved_notification.type == "user_modified"
  end

  test "send saved notifications" do
    user = insert(:user)
    insert(:notification, %{user_id: user.id})
    insert(:notification, %{user_id: user.id})
    insert(:notification, %{user_id: user.id})

    saved_user = Repo.get(User, user.id) |> Repo.preload(:notifications)

    Notifier.handle_cast({:notify_all, saved_user}, [])
    assert_email_sent subject: "Activity Summary"
  end

end


Agent for Repo storage, not blocking assets access on reload

defmodule Mercator.PeerAssets.Repo do
  use GenServer
  require Logger

  alias Mercator.PeerAssets.Types.DeckSpawn

  defp reload_interval, do: Application.get_env(:peerassets, :reload_interval)
  defp prod_tag, do: Application.get_env(:peerassets, :PAprod)
  defp test_tag, do: Application.get_env(:peerassets, :PAtest)

  @tag_fee Gold.btc_to_decimal(0.01)
  @listtxn_size if Mix.env == :test, do: 2, else: 10

  ## Client API

  @doc """
  Starts the PeerAssets repository.
  """
  def start_link do
    GenServer.start_link(__MODULE__, :ok, name: :pa_repo)
  end

  @doc """
  Returns a list of registered assets.
  """
  def list_assets(net \\ :PAprod) do
    Agent.get(__MODULE__, &Map.fetch(&1, net))
  end

  @doc """
  Imports the specified P2TH private key.
  """
  def load_tag!(tag) do
    if (!tag_loaded?(tag)) do
      :rpc |> Gold.importprivkey(tag.wif, tag.label)
      true = tag_loaded?(tag)
    end
  end

  ## Server Callbacks

  def init(:ok) do
    # init the asset storage agent
    Agent.start_link(fn ->
      %{
        block_cnt: 0,
        PAprod: [],
        PAtest: []
       }
    end, name: __MODULE__)
    # init the Repo
    init(:retry, true)
  end

  defp init(:retry, log) do
    try do
      load_tag!(prod_tag)
      load_tag!(test_tag)
      Logger.info("PeerAssets.Repo: initialized (reload_interval: " <> Integer.to_string(reload_interval) <> ")")
      reload_assets(1)
      {:ok, %{connected: true}}
    rescue
      _ ->
        if log, do: Logger.warn("PeerAssets.Repo: Failed to establish rpc connection. Will retry every second.")
        # Block synchronously until connection is established
        :timer.sleep(1000)
        init(:retry, false)
    end
  end

  defp reload_assets(timeout) do
    Process.send_after(self(), :reload, timeout)
  end
  def handle_info(:reload, state) do
    new_state =
      case :rpc |> Gold.getblockcount do
        {:ok, block_cnt} ->
          unless Map.get(state, :connected), do: Logger.info("PeerAssets.Repo: rpc connection re-established")
          state = state |> Map.put(:connected, true)
          cond do
            retrieve(:block_cnt) == block_cnt ->
              state # no changes
            true ->
              # Parse
              prod_assets = load_assets!(prod_tag)
              test_assets = load_assets!(test_tag)
              # Store
              block_cnt |> store(:block_cnt)
              prod_assets |> store(:PAprod)
              test_assets |> store(:PAtest)
          end
        {:error, _} ->
          # TODO: log error
          if Map.get(state, :connected), do: Logger.warn("PeerAssets.Repo: rpc connection lost")
          state |> Map.put(:connected, false)# no connection, just ignore until restored
      end

    reload_assets(reload_interval)
    {:noreply, new_state}
  end

  ## Private

  defp retrieve(key) do
    Agent.get(__MODULE__, &Map.get(&1, key))
  end

  defp store(value, key) do
    Agent.update(__MODULE__, &Map.put(&1, key, value))
  end

  defp tag_loaded?(tag) do
    case :rpc
      |> Gold.getaddressesbyaccount!(tag.label)
      |> Enum.find(fn a -> a == tag.address end) do
        nil -> false
          _ -> true
    end
  end

  defp load_assets!(tag) do
    [] |> load_assets!(0, tag)
  end

  defp load_assets!(assets, full_cnt, tag) do
    new_assets = :rpc
    |> Gold.listtransactions!(tag.label, @listtxn_size, full_cnt)

    [new_assets | assets]
    |> load_more_assets!(full_cnt+@listtxn_size, tag)
  end

  defp load_more_assets!([[] | assets], _full_cnt, tag) do
    parsed = assets
    |> List.flatten
    |> Enum.reverse
    |> Enum.filter_map(&(sufficient_tag_fee(&1)),
      fn(%Gold.Transaction{txid: txid}) ->
        txid
        |> Mercator.RPC.gettransaction!
        |> DeckSpawn.parse_txn
      end)

    # Filter for successfully parsed
    success = for {:ok, valid} <- parsed, do: valid

    # Filter for correctly placed tag (some may refer both PAprod & PAtest)
    # TODO: add such a case to official test vectors
    success
    |> Enum.filter(&(&1.tag_address == tag.address))
  end

  defp load_more_assets!(assets, full_cnt, tag) do
    assets |> load_assets!(full_cnt, tag)
  end

  defp sufficient_tag_fee(%Gold.Transaction{amount: amount}) do
    amount
    |> Decimal.compare(@tag_fee)
    |> Decimal.to_integer != -1
  end

end

defmodule Mercator.PeerAssets.Repo do
  use GenServer
  require Logger

  alias Mercator.PeerAssets.Types.DeckSpawn

  defp reload_interval, do: Application.get_env(:peerassets, :reload_interval)
  defp prod_tag, do: Application.get_env(:peerassets, :PAprod)
  defp test_tag, do: Application.get_env(:peerassets, :PAtest)

  @tag_fee Gold.btc_to_decimal(0.01)
  @listtxn_size if Mix.env == :test, do: 2, else: 10

  ## Client API

  @doc """
  Starts the PeerAssets repository.
  """
  def start_link do
    GenServer.start_link(__MODULE__, :ok, name: :pa_repo)
  end

  @doc """
  Returns a list of registered assets.
  """
  def list_assets(net \\ :PAprod) do
    GenServer.call(:pa_repo, {:list_assets, net})
  end

  @doc """
  Imports the specified P2TH private key.
  """
  def load_tag!(tag) do
    if (!tag_loaded?(tag)) do
      :rpc |> Gold.importprivkey(tag.wif, tag.label)
      true = tag_loaded?(tag)
    end
  end

  ## Server Callbacks

  def init(:ok) do
    init(:retry, true)
  end

  defp init(:retry, log) do
    try do
      load_tag!(prod_tag)
      load_tag!(test_tag)
      Logger.info("PeerAssets.Repo: initialized (reload_interval: " <> Integer.to_string(reload_interval) <> ")")
      reload_assets(1)
      {:ok, %{block_cnt: 0, connected: true}}
    rescue
      _ ->
        if log, do: Logger.warn("PeerAssets.Repo: Failed to establish rpc connection. Will retry every second.")
        # Block synchronously until connection is established
        :timer.sleep(1000)
        init(:retry, false)
    end
  end

  def handle_call({:list_assets, net}, _from, state) do
    {:reply, Map.fetch(state, net), state}
  end

  defp reload_assets(timeout) do
    Process.send_after(self(), :reload, timeout)
  end
  def handle_info(:reload, state) do
    new_state =
      case :rpc |> Gold.getblockcount do
        {:ok, block_cnt} ->
          unless Map.get(state, :connected), do: Logger.info("PeerAssets.Repo: rpc connection re-established")
          state = state |> Map.put(:connected, true)
          cond do
            Map.get(state, :block_cnt) == block_cnt ->
              state # no changes
            true ->
              state
              |> Map.put(:block_cnt, block_cnt)
              |> Map.put(:PAprod, load_assets!(prod_tag))
              |> Map.put(:PAtest, load_assets!(test_tag))
          end
        {:error, _} ->
          # TODO: log error
          if Map.get(state, :connected), do: Logger.warn("PeerAssets.Repo: rpc connection lost")
          state |> Map.put(:connected, false)# no connection, just ignore until restored
      end

    reload_assets(reload_interval)
    {:noreply, new_state}
  end

  ## Private

  defp tag_loaded?(tag) do
    case :rpc
      |> Gold.getaddressesbyaccount!(tag.label)
      |> Enum.find(fn a -> a == tag.address end) do
        nil -> false
          _ -> true
    end
  end

  defp load_assets!(tag) do
    [] |> load_assets!(0, tag)
  end

  defp load_assets!(assets, full_cnt, tag) do
    new_assets = :rpc
    |> Gold.listtransactions!(tag.label, @listtxn_size, full_cnt)

    [new_assets | assets]
    |> load_more_assets!(full_cnt+@listtxn_size, tag)
  end

  defp load_more_assets!([[] | assets], _full_cnt, tag) do
    parsed = assets
    |> List.flatten
    |> Enum.reverse
    |> Enum.filter_map(&(sufficient_tag_fee(&1)),
      fn(%Gold.Transaction{txid: txid}) ->
        txid
        |> Mercator.RPC.gettransaction!
        |> DeckSpawn.parse_txn
      end)

    # Filter for successfully parsed
    success = for {:ok, valid} <- parsed, do: valid

    # Filter for correctly placed tag (some may refer both PAprod & PAtest)
    # TODO: add such a case to official test vectors
    success
    |> Enum.filter(&(&1.tag_address == tag.address))
  end

  defp load_more_assets!(assets, full_cnt, tag) do
    assets |> load_assets!(full_cnt, tag)
  end

  defp sufficient_tag_fee(%Gold.Transaction{amount: amount}) do
    amount
    |> Decimal.compare(@tag_fee)
    |> Decimal.to_integer != -1
  end

end


More broker tests

More broker tests

defmodule Server.BrokerTest do
  use ExUnit.Case, async: true
  alias Server.Components.Broker

  setup do
    {:ok, _pid} = Broker.start_link
    peername = {"localhost", 4252}
    username = "dingo"
    {:ok, %{peername: peername, username: username}}
  end

  test "it puts a user online", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
  end

  test "it puts a user offline", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
    assert :ok == Broker.put_offline(peername)
  end

  test "it cannot put a user online twice", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
    assert {:error, :already_online} == Broker.put_online(peername, username)
  end

  test "it cannot put a user offline twice", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
    assert :ok == Broker.put_offline(peername)
    assert {:error, :already_offline} == Broker.put_offline(peername)
  end

  test "it cannot put an unregistered user offline", %{peername: peername, username: username} do
    assert {:error, :already_offline} == Broker.put_offline(peername)
  end
end

defmodule Server.BrokerTest do
  use ExUnit.Case, async: true
  alias Server.Components.Broker

  setup do
    {:ok, _pid} = Broker.start_link
    peername = {"localhost", 4252}
    username = "dingo"
    {:ok, %{peername: peername, username: username}}
  end

  test "it puts a user online", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
  end

  test "it puts a user offline", %{peername: peername, username: username} do
    assert :ok == Broker.put_online(peername, username)
    assert :ok == Broker.put_offline(peername)
  end
end


Add new team members.

Add new team members.

defmodule Xee.PageController do
  use Xee.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end

  def about(conn, _params) do
    themes = Xee.ThemeServer.get_all
              |> Map.to_list
              |> Enum.map(fn {_key, value} -> value end)
              |> Enum.filter(fn theme -> Xee.Theme.granted? theme end)
    members = [
      %{position: "Principal Investigator / Master of Applied Economics", name: "Ryohei Hayashi (林 良平)", url: "https://github.com/RyoheiHayashi"},
      %{position: "System Designer / Xee System Programmer / Experiments Programmer", name: "Ryo Hashiguchi (橋口 遼)", url: "https://github.com/ryo33"},
      %{position: "Xee System Programmer / Experiments Programmer", name: "Iori Ikeda (池田 伊織)", url: "https://github.com/NotFounds"},
      %{position: "Xee System Programmer", name: "Akira Fukunaga (福永 彬)", url: "https://github.com/kagemiku"},
      %{position: "Experiments Programmer", name: "Hiroki Kataoka (片岡 大樹)", url: "https://github.com/kyataoka"},
      %{position: "Experiments Programmer", name: "Koichiro Hatakeyama (畑山 紘一朗)", url: "https://github.com/koo3701"},
      %{position: "Experiments Programmer", name: "Syoya Matsumoto (松元 翔矢)", url: "https://github.com/M42M0"},
      %{position: "Experiments Programmer", name: "Taiki Mizo (溝 大貴)", url: "https://github.com/kazuwo"},
      %{position: "Experiments Programmer / Xee System Programmer", name: "Wataru Yunoue (湯之上 航)", url: "https://github.com/hayarasu308"},
      %{position: "Experiments Programmer", name: "Kenta Konagayoshi (小永吉　健太)", url: "https://github.com/orgs/xeejp/people/konaken"},
    ]
    render conn, "about.html", themes: themes , members: members
  end
end

defmodule Xee.PageController do
  use Xee.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end

  def about(conn, _params) do
    themes = Xee.ThemeServer.get_all
              |> Map.to_list
              |> Enum.map(fn {_key, value} -> value end)
              |> Enum.filter(fn theme -> Xee.Theme.granted? theme end)
    members = [
      %{position: "General Producer / Master of Applied Economics", name: "Ryohei HAYASHI (林 良平)", url: "https://github.com/RyoheiHayashi"},
      %{position: "System Designer / Xee System Programmer / Experiments Programmer", name: "Ryo Hashiguchi (橋口 遼)", url: "https://github.com/ryo33"},
      %{position: "Xee System Programmer / Experiments Programmer", name: "Iori Ikeda (池田 伊織)", url: "https://github.com/NotFounds"},
      %{position: "Xee System Programmer", name: "Akira Fukunaga (福永 彬)", url: "https://github.com/kagemiku"},
      %{position: "Experiments Programmer", name: "Hiroki Kataoka (片岡 大樹)", url: "https://github.com/kyataoka"},
      %{position: "Experiments Programmer", name: "Koichiro Hatakeyama (畑山 紘一郎)", url: "https://github.com/koo3701"},
      %{position: "Experiments Programmer", name: "Syoya Matsumoto (松元 翔矢)", url: "https://github.com/M42M0"},
      %{position: "Experiments Programmer", name: "Taiki Mizo (溝 大貴)", url: "https://github.com/kazuwo"},
      %{position: "Experiments Programmer", name: "Wataru Yunoue (湯之上 航)", url: "https://github.com/hayarasu308"},
    ]
    render conn, "about.html", themes: themes , members: members
  end
end


Add typespec for Float.ratio/1 (#7242)

Add typespec for Float.ratio/1 (#7242)

import Kernel, except: [round: 1]

defmodule Float do
  @moduledoc """
  Functions for working with floating-point numbers.

  ## Kernel functions

  There are functions related to floating-point numbers on the `Kernel` module
  too. Here is a list of them:

    * `Kernel.round/1`: rounds a number to the nearest integer.
    * `Kernel.trunc/1`: returns the integer part of a number.

  ## Known issues

  There are some very well known problems with floating-point numbers
  and arithmetics due to the fact most decimal fractions cannot be
  represented by a floating-point binary.

  For example, the numbers 0.1 and 0.01 are two of them, what means the result
  of squaring 0.1 does not give 0.01 neither the closest representable. Here is
  what happens in this case:

    * The closest representable number to 0.1 is 0.1000000014
    * The closest representable number to 0.01 is 0.0099999997
    * Doing 0.1 * 0.1 should return 0.01, but because 0.1 is actually 0.1000000014,
      the result is 0.010000000000000002, and because this is not the closest
      representable number to 0.01, you'll get the wrong result for this operation

  There are also other known problems like flooring or rounding numbers. See
  `round/2` and `floor/2` for more details about them.
  """

  import Bitwise

  @power_of_2_to_52 4_503_599_627_370_496
  @precision_range 0..15
  @type precision_range :: 0..15

  @doc """
  Parses a binary into a float.

  If successful, returns a tuple in the form of `{float, remainder_of_binary}`;
  when the binary cannot be coerced into a valid float, the atom `:error` is
  returned.

  If the size of float exceeds the maximum size of `1.7976931348623157e+308`,
  the `ArgumentError` exception is raised.

  If you want to convert a string-formatted float directly to a float,
  `String.to_float/1` can be used instead.

  ## Examples

      iex> Float.parse("34")
      {34.0, ""}
      iex> Float.parse("34.25")
      {34.25, ""}
      iex> Float.parse("56.5xyz")
      {56.5, "xyz"}

      iex> Float.parse("pi")
      :error

  """
  @spec parse(binary) :: {float, binary} | :error
  def parse("-" <> binary) do
    case parse_unsigned(binary) do
      :error -> :error
      {number, remainder} -> {-number, remainder}
    end
  end

  def parse("+" <> binary) do
    parse_unsigned(binary)
  end

  def parse(binary) do
    parse_unsigned(binary)
  end

  defp parse_unsigned(<<digit, rest::binary>>) when digit in ?0..?9,
    do: parse_unsigned(rest, false, false, <<digit>>)

  defp parse_unsigned(binary) when is_binary(binary), do: :error

  defp parse_unsigned(<<digit, rest::binary>>, dot?, e?, acc) when digit in ?0..?9,
    do: parse_unsigned(rest, dot?, e?, <<acc::binary, digit>>)

  defp parse_unsigned(<<?., digit, rest::binary>>, false, false, acc) when digit in ?0..?9,
    do: parse_unsigned(rest, true, false, <<acc::binary, ?., digit>>)

  defp parse_unsigned(<<exp_marker, digit, rest::binary>>, dot?, false, acc)
       when exp_marker in 'eE' and digit in ?0..?9,
       do: parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, digit>>)

  defp parse_unsigned(<<exp_marker, sign, digit, rest::binary>>, dot?, false, acc)
       when exp_marker in 'eE' and sign in '-+' and digit in ?0..?9,
       do: parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, sign, digit>>)

  defp parse_unsigned(rest, dot?, _e?, acc),
    do: {:erlang.binary_to_float(add_dot(acc, dot?)), rest}

  defp add_dot(acc, true), do: acc
  defp add_dot(acc, false), do: acc <> ".0"

  @doc """
  Rounds a float to the largest number less than or equal to `num`.

  `floor/2` also accepts a precision to round a floating-point value down
  to an arbitrary number of fractional digits (between 0 and 15).
  The operation is performed on the binary floating point, without a
  conversion to decimal.

  This function always returns a float. `Kernel.trunc/1` may be used instead to
  truncate the result to an integer afterwards.

  ## Known issues

  The behaviour of `floor/2` for floats can be surprising. For example:

      iex> Float.floor(12.52, 2)
      12.51

  One may have expected it to floor to 12.52. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as 12.51999999,
  which explains the behaviour above.

  ## Examples

      iex> Float.floor(34.25)
      34.0
      iex> Float.floor(-56.5)
      -57.0
      iex> Float.floor(34.259, 2)
      34.25

  """
  @spec floor(float, precision_range) :: float
  def floor(number, precision \\ 0)

  def floor(number, precision) when is_float(number) and precision in @precision_range do
    round(number, precision, :floor)
  end

  def floor(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  @doc """
  Rounds a float to the smallest integer greater than or equal to `num`.

  `ceil/2` also accepts a precision to round a floating-point value down
  to an arbitrary number of fractional digits (between 0 and 15).

  The operation is performed on the binary floating point, without a
  conversion to decimal.

  The behaviour of `ceil/2` for floats can be surprising. For example:

      iex> Float.ceil(-12.52, 2)
      -12.51

  One may have expected it to ceil to -12.52. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as -12.51999999,
  which explains the behaviour above.

  This function always returns floats. `Kernel.trunc/1` may be used instead to
  truncate the result to an integer afterwards.

  ## Examples

      iex> Float.ceil(34.25)
      35.0
      iex> Float.ceil(-56.5)
      -56.0
      iex> Float.ceil(34.251, 2)
      34.26

  """
  @spec ceil(float, precision_range) :: float
  def ceil(number, precision \\ 0)

  def ceil(number, precision) when is_float(number) and precision in @precision_range do
    round(number, precision, :ceil)
  end

  def ceil(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  @doc """
  Rounds a floating-point value to an arbitrary number of fractional
  digits (between 0 and 15).

  The rounding direction always ties to half up. The operation is
  performed on the binary floating point, without a conversion to decimal.

  This function only accepts floats and always returns a float. Use
  `Kernel.round/1` if you want a function that accepts both floats
  and integers and always returns an integer.

  ## Known issues

  The behaviour of `round/2` for floats can be surprising. For example:

      iex> Float.round(5.5675, 3)
      5.567

  One may have expected it to round to the half up 5.568. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as 5.567499999,
  which explains the behaviour above. If you want exact rounding for decimals,
  you must use a decimal library. The behaviour above is also in accordance
  to reference implementations, such as "Correctly Rounded Binary-Decimal and
  Decimal-Binary Conversions" by David M. Gay.

  ## Examples

      iex> Float.round(12.5)
      13.0
      iex> Float.round(5.5674, 3)
      5.567
      iex> Float.round(5.5675, 3)
      5.567
      iex> Float.round(-5.5674, 3)
      -5.567
      iex> Float.round(-5.5675)
      -6.0
      iex> Float.round(12.341444444444441, 15)
      12.341444444444441

  """
  @spec round(float, precision_range) :: float
  # This implementation is slow since it relies on big integers.
  # Faster implementations are available on more recent papers
  # and could be implemented in the future.
  def round(float, precision \\ 0)

  def round(float, precision) when is_float(float) and precision in @precision_range do
    round(float, precision, :half_up)
  end

  def round(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  defp round(float, precision, rounding) do
    <<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
    {num, count, _} = decompose(significant)
    count = count - exp + 1023

    cond do
      # There is no decimal precision
      # zero or minus zero
      count <= 0 or (0 == exp and <<0::52>> == significant) ->
        float

      # Precision beyond 15 digits
      count >= 104 ->
        case rounding do
          :ceil when sign === 0 -> 1 / power_of_10(precision)
          :floor when sign === 1 -> -1 / power_of_10(precision)
          _ -> 0.0
        end

      # We are asking more precision than we have
      count <= precision ->
        float

      true ->
        # Difference in precision between float and asked precision
        # We subtract 1 because we need to calculate the remainder too
        diff = count - precision - 1

        # Get up to latest so we calculate the remainder
        power_of_10 = power_of_10(diff)

        # Convert the numerand to decimal base
        num = num * power_of_5(count)

        # Move to the given precision - 1
        num = div(num, power_of_10)
        div = div(num, 10)
        num = rounding(rounding, sign, num, div)

        # Convert back to float without loss
        # http://www.exploringbinary.com/correct-decimal-to-floating-point-using-big-integers/
        den = power_of_10(precision)
        boundary = den <<< 52

        cond do
          num == 0 ->
            0.0

          num >= boundary ->
            {den, exp} = scale_down(num, boundary, 52)
            decimal_to_float(sign, num, den, exp)

          true ->
            {num, exp} = scale_up(num, boundary, 52)
            decimal_to_float(sign, num, den, exp)
        end
    end
  end

  defp scale_up(num, boundary, exp) when num >= boundary, do: {num, exp}
  defp scale_up(num, boundary, exp), do: scale_up(num <<< 1, boundary, exp - 1)

  defp scale_down(num, den, exp) do
    new_den = den <<< 1

    if num < new_den do
      {den >>> 52, exp}
    else
      scale_down(num, new_den, exp + 1)
    end
  end

  defp decimal_to_float(sign, num, den, exp) do
    quo = div(num, den)
    rem = num - quo * den

    tmp =
      case den >>> 1 do
        den when rem > den -> quo + 1
        den when rem < den -> quo
        _ when (quo &&& 1) === 1 -> quo + 1
        _ -> quo
      end

    tmp = tmp - @power_of_2_to_52
    <<tmp::float>> = <<sign::1, exp + 1023::11, tmp::52>>
    tmp
  end

  defp rounding(:floor, 1, _num, div), do: div + 1
  defp rounding(:ceil, 0, _num, div), do: div + 1

  defp rounding(:half_up, _sign, num, div) do
    case rem(num, 10) do
      rem when rem < 5 -> div
      rem when rem >= 5 -> div + 1
    end
  end

  defp rounding(_, _, _, div), do: div

  Enum.reduce(0..104, 1, fn x, acc ->
    defp power_of_10(unquote(x)), do: unquote(acc)
    acc * 10
  end)

  Enum.reduce(0..104, 1, fn x, acc ->
    defp power_of_5(unquote(x)), do: unquote(acc)
    acc * 5
  end)

  @doc """
  Returns a pair of integers whose ratio is exactly equal
  to the original float and with a positive denominator.

  ## Examples

      iex> Float.ratio(3.14)
      {7070651414971679, 2251799813685248}
      iex> Float.ratio(-3.14)
      {-7070651414971679, 2251799813685248}
      iex> Float.ratio(1.5)
      {3, 2}
      iex> Float.ratio(-1.5)
      {-3, 2}
      iex> Float.ratio(16.0)
      {16, 1}
      iex> Float.ratio(-16.0)
      {-16, 1}

  """
  @since "1.4.0"
  @spec ratio(float) :: {pos_integer | neg_integer, pos_integer}
  def ratio(float) when is_float(float) do
    <<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
    {num, _, den} = decompose(significant)
    num = sign(sign, num)

    case exp - 1023 do
      exp when exp > 0 ->
        {den, exp} = shift_right(den, exp)
        {shift_left(num, exp), den}

      exp when exp < 0 ->
        {num, shift_left(den, -exp)}

      0 ->
        {num, den}
    end
  end

  defp decompose(significant) do
    decompose(significant, 1, 0, 2, 1, 1)
  end

  defp decompose(<<1::1, bits::bitstring>>, count, last_count, power, _last_power, acc) do
    decompose(bits, count + 1, count, power <<< 1, power, shift_left(acc, count - last_count) + 1)
  end

  defp decompose(<<0::1, bits::bitstring>>, count, last_count, power, last_power, acc) do
    decompose(bits, count + 1, last_count, power <<< 1, last_power, acc)
  end

  defp decompose(<<>>, _count, last_count, _power, last_power, acc) do
    {acc, last_count, last_power}
  end

  defp sign(0, num), do: num
  defp sign(1, num), do: -num

  defp shift_left(num, 0), do: num
  defp shift_left(num, times), do: shift_left(num <<< 1, times - 1)

  defp shift_right(num, 0), do: {num, 0}
  defp shift_right(1, times), do: {1, times}
  defp shift_right(num, times), do: shift_right(num >>> 1, times - 1)

  @doc """
  Returns a charlist which corresponds to the text representation
  of the given float.

  It uses the shortest representation according to algorithm described
  in "Printing Floating-Point Numbers Quickly and Accurately" in
  Proceedings of the SIGPLAN '96 Conference on Programming Language
  Design and Implementation.

  ## Examples

      iex> Float.to_charlist(7.0)
      '7.0'

  """
  @spec to_charlist(float) :: charlist
  def to_charlist(float) when is_float(float) do
    :io_lib_format.fwrite_g(float)
  end

  @doc """
  Returns a binary which corresponds to the text representation
  of the given float.

  It uses the shortest representation according to algorithm described
  in "Printing Floating-Point Numbers Quickly and Accurately" in
  Proceedings of the SIGPLAN '96 Conference on Programming Language
  Design and Implementation.

  ## Examples

      iex> Float.to_string(7.0)
      "7.0"

  """
  @spec to_string(float) :: String.t()
  def to_string(float) when is_float(float) do
    IO.iodata_to_binary(:io_lib_format.fwrite_g(float))
  end

  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  @doc false
  def to_char_list(float), do: Float.to_charlist(float)

  @doc false
  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  def to_char_list(float, options) do
    :erlang.float_to_list(float, expand_compact(options))
  end

  @doc false
  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  def to_string(float, options) do
    :erlang.float_to_binary(float, expand_compact(options))
  end

  defp invalid_precision_message(precision) do
    "precision #{precision} is out of valid range of #{inspect(@precision_range)}"
  end

  defp expand_compact([{:compact, false} | t]), do: expand_compact(t)
  defp expand_compact([{:compact, true} | t]), do: [:compact | expand_compact(t)]
  defp expand_compact([h | t]), do: [h | expand_compact(t)]
  defp expand_compact([]), do: []
end

import Kernel, except: [round: 1]

defmodule Float do
  @moduledoc """
  Functions for working with floating-point numbers.

  ## Kernel functions

  There are functions related to floating-point numbers on the `Kernel` module
  too. Here is a list of them:

    * `Kernel.round/1`: rounds a number to the nearest integer.
    * `Kernel.trunc/1`: returns the integer part of a number.

  ## Known issues

  There are some very well known problems with floating-point numbers
  and arithmetics due to the fact most decimal fractions cannot be
  represented by a floating-point binary.

  For example, the numbers 0.1 and 0.01 are two of them, what means the result
  of squaring 0.1 does not give 0.01 neither the closest representable. Here is
  what happens in this case:

    * The closest representable number to 0.1 is 0.1000000014
    * The closest representable number to 0.01 is 0.0099999997
    * Doing 0.1 * 0.1 should return 0.01, but because 0.1 is actually 0.1000000014,
      the result is 0.010000000000000002, and because this is not the closest
      representable number to 0.01, you'll get the wrong result for this operation

  There are also other known problems like flooring or rounding numbers. See
  `round/2` and `floor/2` for more details about them.
  """

  import Bitwise

  @power_of_2_to_52 4_503_599_627_370_496
  @precision_range 0..15
  @type precision_range :: 0..15

  @doc """
  Parses a binary into a float.

  If successful, returns a tuple in the form of `{float, remainder_of_binary}`;
  when the binary cannot be coerced into a valid float, the atom `:error` is
  returned.

  If the size of float exceeds the maximum size of `1.7976931348623157e+308`,
  the `ArgumentError` exception is raised.

  If you want to convert a string-formatted float directly to a float,
  `String.to_float/1` can be used instead.

  ## Examples

      iex> Float.parse("34")
      {34.0, ""}
      iex> Float.parse("34.25")
      {34.25, ""}
      iex> Float.parse("56.5xyz")
      {56.5, "xyz"}

      iex> Float.parse("pi")
      :error

  """
  @spec parse(binary) :: {float, binary} | :error
  def parse("-" <> binary) do
    case parse_unsigned(binary) do
      :error -> :error
      {number, remainder} -> {-number, remainder}
    end
  end

  def parse("+" <> binary) do
    parse_unsigned(binary)
  end

  def parse(binary) do
    parse_unsigned(binary)
  end

  defp parse_unsigned(<<digit, rest::binary>>) when digit in ?0..?9,
    do: parse_unsigned(rest, false, false, <<digit>>)

  defp parse_unsigned(binary) when is_binary(binary), do: :error

  defp parse_unsigned(<<digit, rest::binary>>, dot?, e?, acc) when digit in ?0..?9,
    do: parse_unsigned(rest, dot?, e?, <<acc::binary, digit>>)

  defp parse_unsigned(<<?., digit, rest::binary>>, false, false, acc) when digit in ?0..?9,
    do: parse_unsigned(rest, true, false, <<acc::binary, ?., digit>>)

  defp parse_unsigned(<<exp_marker, digit, rest::binary>>, dot?, false, acc)
       when exp_marker in 'eE' and digit in ?0..?9,
       do: parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, digit>>)

  defp parse_unsigned(<<exp_marker, sign, digit, rest::binary>>, dot?, false, acc)
       when exp_marker in 'eE' and sign in '-+' and digit in ?0..?9,
       do: parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, sign, digit>>)

  defp parse_unsigned(rest, dot?, _e?, acc),
    do: {:erlang.binary_to_float(add_dot(acc, dot?)), rest}

  defp add_dot(acc, true), do: acc
  defp add_dot(acc, false), do: acc <> ".0"

  @doc """
  Rounds a float to the largest number less than or equal to `num`.

  `floor/2` also accepts a precision to round a floating-point value down
  to an arbitrary number of fractional digits (between 0 and 15).
  The operation is performed on the binary floating point, without a
  conversion to decimal.

  This function always returns a float. `Kernel.trunc/1` may be used instead to
  truncate the result to an integer afterwards.

  ## Known issues

  The behaviour of `floor/2` for floats can be surprising. For example:

      iex> Float.floor(12.52, 2)
      12.51

  One may have expected it to floor to 12.52. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as 12.51999999,
  which explains the behaviour above.

  ## Examples

      iex> Float.floor(34.25)
      34.0
      iex> Float.floor(-56.5)
      -57.0
      iex> Float.floor(34.259, 2)
      34.25

  """
  @spec floor(float, precision_range) :: float
  def floor(number, precision \\ 0)

  def floor(number, precision) when is_float(number) and precision in @precision_range do
    round(number, precision, :floor)
  end

  def floor(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  @doc """
  Rounds a float to the smallest integer greater than or equal to `num`.

  `ceil/2` also accepts a precision to round a floating-point value down
  to an arbitrary number of fractional digits (between 0 and 15).

  The operation is performed on the binary floating point, without a
  conversion to decimal.

  The behaviour of `ceil/2` for floats can be surprising. For example:

      iex> Float.ceil(-12.52, 2)
      -12.51

  One may have expected it to ceil to -12.52. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as -12.51999999,
  which explains the behaviour above.

  This function always returns floats. `Kernel.trunc/1` may be used instead to
  truncate the result to an integer afterwards.

  ## Examples

      iex> Float.ceil(34.25)
      35.0
      iex> Float.ceil(-56.5)
      -56.0
      iex> Float.ceil(34.251, 2)
      34.26

  """
  @spec ceil(float, precision_range) :: float
  def ceil(number, precision \\ 0)

  def ceil(number, precision) when is_float(number) and precision in @precision_range do
    round(number, precision, :ceil)
  end

  def ceil(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  @doc """
  Rounds a floating-point value to an arbitrary number of fractional
  digits (between 0 and 15).

  The rounding direction always ties to half up. The operation is
  performed on the binary floating point, without a conversion to decimal.

  This function only accepts floats and always returns a float. Use
  `Kernel.round/1` if you want a function that accepts both floats
  and integers and always returns an integer.

  ## Known issues

  The behaviour of `round/2` for floats can be surprising. For example:

      iex> Float.round(5.5675, 3)
      5.567

  One may have expected it to round to the half up 5.568. This is not a bug.
  Most decimal fractions cannot be represented as a binary floating point
  and therefore the number above is internally represented as 5.567499999,
  which explains the behaviour above. If you want exact rounding for decimals,
  you must use a decimal library. The behaviour above is also in accordance
  to reference implementations, such as "Correctly Rounded Binary-Decimal and
  Decimal-Binary Conversions" by David M. Gay.

  ## Examples

      iex> Float.round(12.5)
      13.0
      iex> Float.round(5.5674, 3)
      5.567
      iex> Float.round(5.5675, 3)
      5.567
      iex> Float.round(-5.5674, 3)
      -5.567
      iex> Float.round(-5.5675)
      -6.0
      iex> Float.round(12.341444444444441, 15)
      12.341444444444441

  """
  @spec round(float, precision_range) :: float
  # This implementation is slow since it relies on big integers.
  # Faster implementations are available on more recent papers
  # and could be implemented in the future.
  def round(float, precision \\ 0)

  def round(float, precision) when is_float(float) and precision in @precision_range do
    round(float, precision, :half_up)
  end

  def round(number, precision) when is_float(number) do
    raise ArgumentError, invalid_precision_message(precision)
  end

  defp round(float, precision, rounding) do
    <<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
    {num, count, _} = decompose(significant)
    count = count - exp + 1023

    cond do
      # There is no decimal precision
      # zero or minus zero
      count <= 0 or (0 == exp and <<0::52>> == significant) ->
        float

      # Precision beyond 15 digits
      count >= 104 ->
        case rounding do
          :ceil when sign === 0 -> 1 / power_of_10(precision)
          :floor when sign === 1 -> -1 / power_of_10(precision)
          _ -> 0.0
        end

      # We are asking more precision than we have
      count <= precision ->
        float

      true ->
        # Difference in precision between float and asked precision
        # We subtract 1 because we need to calculate the remainder too
        diff = count - precision - 1

        # Get up to latest so we calculate the remainder
        power_of_10 = power_of_10(diff)

        # Convert the numerand to decimal base
        num = num * power_of_5(count)

        # Move to the given precision - 1
        num = div(num, power_of_10)
        div = div(num, 10)
        num = rounding(rounding, sign, num, div)

        # Convert back to float without loss
        # http://www.exploringbinary.com/correct-decimal-to-floating-point-using-big-integers/
        den = power_of_10(precision)
        boundary = den <<< 52

        cond do
          num == 0 ->
            0.0

          num >= boundary ->
            {den, exp} = scale_down(num, boundary, 52)
            decimal_to_float(sign, num, den, exp)

          true ->
            {num, exp} = scale_up(num, boundary, 52)
            decimal_to_float(sign, num, den, exp)
        end
    end
  end

  defp scale_up(num, boundary, exp) when num >= boundary, do: {num, exp}
  defp scale_up(num, boundary, exp), do: scale_up(num <<< 1, boundary, exp - 1)

  defp scale_down(num, den, exp) do
    new_den = den <<< 1

    if num < new_den do
      {den >>> 52, exp}
    else
      scale_down(num, new_den, exp + 1)
    end
  end

  defp decimal_to_float(sign, num, den, exp) do
    quo = div(num, den)
    rem = num - quo * den

    tmp =
      case den >>> 1 do
        den when rem > den -> quo + 1
        den when rem < den -> quo
        _ when (quo &&& 1) === 1 -> quo + 1
        _ -> quo
      end

    tmp = tmp - @power_of_2_to_52
    <<tmp::float>> = <<sign::1, exp + 1023::11, tmp::52>>
    tmp
  end

  defp rounding(:floor, 1, _num, div), do: div + 1
  defp rounding(:ceil, 0, _num, div), do: div + 1

  defp rounding(:half_up, _sign, num, div) do
    case rem(num, 10) do
      rem when rem < 5 -> div
      rem when rem >= 5 -> div + 1
    end
  end

  defp rounding(_, _, _, div), do: div

  Enum.reduce(0..104, 1, fn x, acc ->
    defp power_of_10(unquote(x)), do: unquote(acc)
    acc * 10
  end)

  Enum.reduce(0..104, 1, fn x, acc ->
    defp power_of_5(unquote(x)), do: unquote(acc)
    acc * 5
  end)

  @doc """
  Returns a pair of integers whose ratio is exactly equal
  to the original float and with a positive denominator.

  ## Examples

      iex> Float.ratio(3.14)
      {7070651414971679, 2251799813685248}
      iex> Float.ratio(-3.14)
      {-7070651414971679, 2251799813685248}
      iex> Float.ratio(1.5)
      {3, 2}
      iex> Float.ratio(-1.5)
      {-3, 2}
      iex> Float.ratio(16.0)
      {16, 1}
      iex> Float.ratio(-16.0)
      {-16, 1}

  """
  @since "1.4.0"
  def ratio(float) when is_float(float) do
    <<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
    {num, _, den} = decompose(significant)
    num = sign(sign, num)

    case exp - 1023 do
      exp when exp > 0 ->
        {den, exp} = shift_right(den, exp)
        {shift_left(num, exp), den}

      exp when exp < 0 ->
        {num, shift_left(den, -exp)}

      0 ->
        {num, den}
    end
  end

  defp decompose(significant) do
    decompose(significant, 1, 0, 2, 1, 1)
  end

  defp decompose(<<1::1, bits::bitstring>>, count, last_count, power, _last_power, acc) do
    decompose(bits, count + 1, count, power <<< 1, power, shift_left(acc, count - last_count) + 1)
  end

  defp decompose(<<0::1, bits::bitstring>>, count, last_count, power, last_power, acc) do
    decompose(bits, count + 1, last_count, power <<< 1, last_power, acc)
  end

  defp decompose(<<>>, _count, last_count, _power, last_power, acc) do
    {acc, last_count, last_power}
  end

  defp sign(0, num), do: num
  defp sign(1, num), do: -num

  defp shift_left(num, 0), do: num
  defp shift_left(num, times), do: shift_left(num <<< 1, times - 1)

  defp shift_right(num, 0), do: {num, 0}
  defp shift_right(1, times), do: {1, times}
  defp shift_right(num, times), do: shift_right(num >>> 1, times - 1)

  @doc """
  Returns a charlist which corresponds to the text representation
  of the given float.

  It uses the shortest representation according to algorithm described
  in "Printing Floating-Point Numbers Quickly and Accurately" in
  Proceedings of the SIGPLAN '96 Conference on Programming Language
  Design and Implementation.

  ## Examples

      iex> Float.to_charlist(7.0)
      '7.0'

  """
  @spec to_charlist(float) :: charlist
  def to_charlist(float) when is_float(float) do
    :io_lib_format.fwrite_g(float)
  end

  @doc """
  Returns a binary which corresponds to the text representation
  of the given float.

  It uses the shortest representation according to algorithm described
  in "Printing Floating-Point Numbers Quickly and Accurately" in
  Proceedings of the SIGPLAN '96 Conference on Programming Language
  Design and Implementation.

  ## Examples

      iex> Float.to_string(7.0)
      "7.0"

  """
  @spec to_string(float) :: String.t()
  def to_string(float) when is_float(float) do
    IO.iodata_to_binary(:io_lib_format.fwrite_g(float))
  end

  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  @doc false
  def to_char_list(float), do: Float.to_charlist(float)

  @doc false
  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  def to_char_list(float, options) do
    :erlang.float_to_list(float, expand_compact(options))
  end

  @doc false
  # TODO: Remove by 2.0
  # (hard-deprecated in elixir_dispatch)
  def to_string(float, options) do
    :erlang.float_to_binary(float, expand_compact(options))
  end

  defp invalid_precision_message(precision) do
    "precision #{precision} is out of valid range of #{inspect(@precision_range)}"
  end

  defp expand_compact([{:compact, false} | t]), do: expand_compact(t)
  defp expand_compact([{:compact, true} | t]), do: [:compact | expand_compact(t)]
  defp expand_compact([h | t]), do: [h | expand_compact(t)]
  defp expand_compact([]), do: []
end