elixir/phoenix: fix "used underscored variable" warnings

defmodule Hello.PageController do
  use Hello.Web, :controller
  alias Hello.World
  alias Hello.Fortune

  def index(conn, _params) do
    json conn, %{"TE Benchmarks\n" => "Started"}
  end

  # avoid namespace collision
  def _json(conn, _params) do
    json conn, %{message: "Hello, world!"}
  end

  def db(conn, _params) do
    json conn, Repo.get(World, :random.uniform(10000))
  end

  def queries(conn, params) do
    q = try do
      case String.to_integer(params["queries"]) do
        x when x < 1    -> 1
        x when x > 500  -> 500
        x               -> x
      end
    rescue
      ArgumentError -> 1
    end
    json conn, Enum.map(1..q, fn _ -> Repo.get(World, :random.uniform(10000)) end)
  end

  def fortunes(conn, _params) do
    fortunes = List.insert_at(Repo.all(Fortune), 0, %Fortune{:id => 0, :message  => "Additional fortune added at request time."})
    render conn, "fortunes.html", fortunes: Enum.sort(fortunes, fn f1, f2 -> f1.message < f2.message end)
  end

  def updates(conn, params) do
    q = try do
      case String.to_integer(params["queries"]) do
        x when x < 1    -> 1
        x when x > 500  -> 500
        x               -> x
      end
    rescue
      ArgumentError -> 1
    end
    json conn, Enum.map(1..q, fn _ ->
      w = Repo.get(World, :random.uniform(10000))
      changeset = World.changeset(w, %{randomNumber: :random.uniform(10000)})
      Repo.update(changeset)
      w end)
  end

  def plaintext(conn, _params) do
    text conn, "Hello, world!"
  end
end

defmodule Hello.PageController do
  use Hello.Web, :controller
  alias Hello.World
  alias Hello.Fortune

  def index(conn, _params) do
    json conn, %{"TE Benchmarks\n" => "Started"}
  end

  # avoid namespace collision
  def _json(conn, _params) do
    json conn, %{message: "Hello, world!"}
  end

  def db(conn, _params) do
    json conn, Repo.get(World, :random.uniform(10000))
  end

  def queries(conn, _params) do
    q = try do
      case String.to_integer(_params["queries"]) do
        x when x < 1    -> 1
        x when x > 500  -> 500
        x               -> x
      end
    rescue
      ArgumentError -> 1
    end
    json conn, Enum.map(1..q, fn _ -> Repo.get(World, :random.uniform(10000)) end)
  end

  def fortunes(conn, _params) do
    fortunes = List.insert_at(Repo.all(Fortune), 0, %Fortune{:id => 0, :message  => "Additional fortune added at request time."})
    render conn, "fortunes.html", fortunes: Enum.sort(fortunes, fn f1, f2 -> f1.message < f2.message end)
  end

  def updates(conn, _params) do
    q = try do
      case String.to_integer(_params["queries"]) do
        x when x < 1    -> 1
        x when x > 500  -> 500
        x               -> x
      end
    rescue
      ArgumentError -> 1
    end
    json conn, Enum.map(1..q, fn _ ->
      w = Repo.get(World, :random.uniform(10000))
      changeset = World.changeset(w, %{randomNumber: :random.uniform(10000)})
      Repo.update(changeset)
      w end)
  end

  def plaintext(conn, _params) do
    text conn, "Hello, world!"
  end
end


try it in a loop

try it in a loop

defmodule Blockytalky.SonicPi do
  @moduledoc """
  This is a module completely of "functions-as-data" sonicpi programs.
  to achieve subdivisions sleep in % amounts relative to beats.
  e.g.
  to rest for 1/8th + 1/16th and then play a 16th note, it would look like:
    sync :beat1
    sleep 0.75
    play <pitch>
  """
  def listen_port, do:  Application.get_env(:blockytalky, :music_port, 9090)
  def eval_port, do: Application.get_env(:blockytalky, :music_eval_port, 5050)
  ####
  #System-y functions
  def public_cues do
    [:down_beat, :up_beat, :beat1, :beat2, :beat3, :beat4]
  end
  def init do
    set_amp(50) <> tempo(120) <> maestro_beat_pattern(false,4)
  end
  @doc """
  Takes a value between 0 and 100 and converts it to one between 0 and 5
  """
  def set_amp(value) do
    value = value / 20
    """
    $amp = #{value}
    set_volume #{value}
    """
  end
  def tempo(val) do
    """
    $tempo = #{val}
    """
  end
  @doc """
  takes the name of the btu to sync to or false as a first param
  takes beats per measure >= 1 as a second param.
  When syncing to a parent btu, it will listen for a first message,
  then it will keep track of time internally for one measure
  #the default sonic pi eval port is pretty slow on raspberry pi
  #this loop listens on a different port to speed it up
  """
  def maestro_beat_pattern(parent, beats_per_measure) do
    sync_to_network = case parent do
      false -> "#"
      name -> #Ruby code to listen until the parent sends a sync message
      """
      synced = false
      until synced  do
        begin
          synced = false
          msg = $u2.recvfrom_nonblock(1024) # "["hostname,tempo",[..args..]]"
          msg_payload = msg[0].split(",")
          host = msg_payload[0]
          if(host == "#{name}")
            t = msg_payload[1]
            beat = msg_payload[2]
            $tempo = t.to_f
            use_bpm $tempo
            $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},\#{beat\}", 0, '224.0.0.1', #{listen_port}
            cue beat.to_sym
            if beat != "up_beat"
              if $next_beat != nil && $next_beat.alive?
                $next_beat.kill
              end
              cue :down_beat
              beat_num = beat[-1,1].to_i
              $next_beat = in_thread do
                loop do
                  sleep 0.98
                  next_num = (beat_num % #{beats_per_measure}) + 1
                  cue ("beat" + next_num.to_s ).to_sym
                  beat_num = beat_num + 1
                end
              end
            end
            synced = true
          end
          sleep 1.0 / 128.0
        rescue
          sleep 1.0 / 64.0
          next
        end
      end
      """
    end
    beat_signaling = cond do
      beats_per_measure > 1 ->
      Enum.reduce(1..beats_per_measure,"",fn(x,acc) ->
        acc
        <>
        """
        cue :down_beat
        cue :beat#{x}
        $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},beat#{x}", 0, '224.0.0.1', #{listen_port}
        sleep 0.5
        cue :up_beat
        $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},up_beat", 0, '224.0.0.1', #{listen_port}
        sleep 0.5
        """
      end)
      true ->
        "#"
      end
    #return program:
    """
    use_debug false
    if $u1 != nil && !$u1.closed?
      $u1.close
    end
    if $u2 != nil && !$u2.closed?
      $u2.close
    end
    if $u3 != nil && !$u3.closed?
      $u3.close
    end
    $u1 = UDPSocket.new
    $u2 = UDPSocket.new
    $u2.bind("0.0.0.0", #{listen_port})
    # Main tempo cueing / UDP broadcasting thread
    live_loop :beat_pattern do
      use_bpm $tempo
      #{if parent != false, do: sync_to_network, else: beat_signaling}
    end

    $u3 = UDPSocket.new
    $u3.bind("127.0.0.1", #{eval_port})
    if $eval_thread != nil && $eval_thread.alive?
      $eval_thread.kill
    end
    $eval_thread = in_thread do
      loop do
        begin
          program, addr = $u3.recvfrom_nonblock(65655)
          eval(program)
          sleep 1.0 / 64.0
        rescue IO::WaitReadable
          sleep 1.0 / 64.0
          next
        end
      end
    end
    """
  end
  def start_motif(body_program)  do
    """
    $next_motif = define :next_motif do
      #{body_program}
    end
    if $current_motif == nil
      $current_motif = $next_motif
      $next_motif = nil
    end
    if $my_motif_thread == nil || $my_motif_thread.alive? == false
      $my_motif_thread = in_thread do
        until $current_motif == nil do
          use_bpm $tempo
          $current_motif.()
          $current_motif = $next_motif
          $next_motif = nil
        end
      end
    end
    """
  end
  def loop_motif(body_program) do
    """
    $next_motif = define :next_motif do
      #{body_program}
    end
    if $current_motif == nil
      $current_motif = $next_motif
      $next_motif = nil
    end
    if $my_motif_thread == nil || $my_motif_thread.alive? == false
      $my_motif_thread = in_thread do
        loop do
          use_bpm $tempo
          $current_motif.()
          if $next_motif != nil
            $current_motif = $next_motif
            $next_motif = nil
          end
        end
      end
    end
    """
  end
  def stop_motif() do
    """
    $my_motif_thread.kill
    $current_motif = nil
    $next_motif = nil
    """
  end
  def cue(cue_flag) do
    """
    cue #{cue_flag}
    """
  end
  ####
  #User-y functions, e.g. motif body pieces
  @doc """
  The SonicPi specific DSL code-string for playing a pitch.
  """
  def play_synth(pitches, duration) when is_list(pitches) do
    pitches
    |> Enum.map(fn pitch -> play_synth(pitch, duration) end)
    |> Enum.join("\n")
  end
  def play_synth(pitch, duration) do
    p = case pitch do
      n when is_integer(n) -> n
      ":" <> s -> pitch
      non_atom -> ":" <> non_atom
    end
    """
    use_bpm $tempo
    play #{p}, sustain: #{duration}, amp: $amp
    """
  end
  @doc """
  TODO: Make this sensitive to the beats per measure set by the user (when they get that option)
  """
  def sleep(duration), do: sleep(duration, :beats)
  def sleep(duration,units) do
    t = case units do
      :beats -> duration
      :measures -> duration * 4
    end
    t = t * 0.99
    """
    use_bpm $tempo
    sleep #{t}
    """
  end
  def trigger_sample(sample) do
    """
    use_bpm $tempo
    sample #{inspect sample}, amp: $amp
    """
  end
  def sync(flag) do
    "sync :#{flag}"
  end

end

defmodule Blockytalky.SonicPi do
  @moduledoc """
  This is a module completely of "functions-as-data" sonicpi programs.
  to achieve subdivisions sleep in % amounts relative to beats.
  e.g.
  to rest for 1/8th + 1/16th and then play a 16th note, it would look like:
    sync :beat1
    sleep 0.75
    play <pitch>
  """
  def listen_port, do:  Application.get_env(:blockytalky, :music_port, 9090)
  def eval_port, do: Application.get_env(:blockytalky, :music_eval_port, 5050)
  ####
  #System-y functions
  def public_cues do
    [:down_beat, :up_beat, :beat1, :beat2, :beat3, :beat4]
  end
  def init do
    set_amp(50) <> tempo(120) <> maestro_beat_pattern(false,4)
  end
  @doc """
  Takes a value between 0 and 100 and converts it to one between 0 and 5
  """
  def set_amp(value) do
    value = value / 20
    """
    $amp = #{value}
    set_volume #{value}
    """
  end
  def tempo(val) do
    """
    $tempo = #{val}
    """
  end
  @doc """
  takes the name of the btu to sync to or false as a first param
  takes beats per measure >= 1 as a second param.
  When syncing to a parent btu, it will listen for a first message,
  then it will keep track of time internally for one measure
  #the default sonic pi eval port is pretty slow on raspberry pi
  #this loop listens on a different port to speed it up
  """
  def maestro_beat_pattern(parent, beats_per_measure) do
    sync_to_network = case parent do
      false -> "#"
      name -> #Ruby code to listen until the parent sends a sync message
      """
      synced = false
      until synced  do
        begin
          synced = false
          msg = $u2.recvfrom_nonblock(1024) # "["hostname,tempo",[..args..]]"
          msg_payload = msg[0].split(",")
          host = msg_payload[0]
          if(host == "#{name}")
            t = msg_payload[1]
            beat = msg_payload[2]
            $tempo = t.to_f
            use_bpm $tempo
            $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},\#{beat\}", 0, '224.0.0.1', #{listen_port}
            cue beat.to_sym
            if beat != "up_beat"
              if $next_beat != nil && $next_beat.alive?
                $next_beat.kill
              end
              cue :down_beat
              $next_beat = in_thread do
                sleep 1.0
                beat_num = beat[-1,1].to_i
                next_num = (beat_num % #{beats_per_measure}) + 1
                cue ("beat" + next_num.to_s ).to_sym
              end
            end
            synced = true
          end
          sleep 1.0 / 128.0
        rescue
          sleep 1.0 / 64.0
          next
        end
      end
      """
    end
    beat_signaling = cond do
      beats_per_measure > 1 ->
      Enum.reduce(1..beats_per_measure,"",fn(x,acc) ->
        acc
        <>
        """
        cue :down_beat
        cue :beat#{x}
        $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},beat#{x}", 0, '224.0.0.1', #{listen_port}
        sleep 0.5
        cue :up_beat
        $u1.send "#{Blockytalky.RuntimeUtils.btu_id},\#{$tempo\},up_beat", 0, '224.0.0.1', #{listen_port}
        sleep 0.5
        """
      end)
      true ->
        "#"
      end
    #return program:
    """
    use_debug false
    if $u1 != nil && !$u1.closed?
      $u1.close
    end
    if $u2 != nil && !$u2.closed?
      $u2.close
    end
    if $u3 != nil && !$u3.closed?
      $u3.close
    end
    $u1 = UDPSocket.new
    $u2 = UDPSocket.new
    $u2.bind("0.0.0.0", #{listen_port})
    # Main tempo cueing / UDP broadcasting thread
    live_loop :beat_pattern do
      use_bpm $tempo
      #{if parent != false, do: sync_to_network, else: beat_signaling}
    end

    $u3 = UDPSocket.new
    $u3.bind("127.0.0.1", #{eval_port})
    if $eval_thread != nil && $eval_thread.alive?
      $eval_thread.kill
    end
    $eval_thread = in_thread do
      loop do
        begin
          program, addr = $u3.recvfrom_nonblock(65655)
          eval(program)
          sleep 1.0 / 64.0
        rescue IO::WaitReadable
          sleep 1.0 / 64.0
          next
        end
      end
    end
    """
  end
  def start_motif(body_program)  do
    """
    $next_motif = define :next_motif do
      #{body_program}
    end
    if $current_motif == nil
      $current_motif = $next_motif
      $next_motif = nil
    end
    if $my_motif_thread == nil || $my_motif_thread.alive? == false
      $my_motif_thread = in_thread do
        until $current_motif == nil do
          use_bpm $tempo
          $current_motif.()
          $current_motif = $next_motif
          $next_motif = nil
        end
      end
    end
    """
  end
  def loop_motif(body_program) do
    """
    $next_motif = define :next_motif do
      #{body_program}
    end
    if $current_motif == nil
      $current_motif = $next_motif
      $next_motif = nil
    end
    if $my_motif_thread == nil || $my_motif_thread.alive? == false
      $my_motif_thread = in_thread do
        loop do
          use_bpm $tempo
          $current_motif.()
          if $next_motif != nil
            $current_motif = $next_motif
            $next_motif = nil
          end
        end
      end
    end
    """
  end
  def stop_motif() do
    """
    $my_motif_thread.kill
    $current_motif = nil
    $next_motif = nil
    """
  end
  def cue(cue_flag) do
    """
    cue #{cue_flag}
    """
  end
  ####
  #User-y functions, e.g. motif body pieces
  @doc """
  The SonicPi specific DSL code-string for playing a pitch.
  """
  def play_synth(pitches, duration) when is_list(pitches) do
    pitches
    |> Enum.map(fn pitch -> play_synth(pitch, duration) end)
    |> Enum.join("\n")
  end
  def play_synth(pitch, duration) do
    p = case pitch do
      n when is_integer(n) -> n
      ":" <> s -> pitch
      non_atom -> ":" <> non_atom
    end
    """
    use_bpm $tempo
    play #{p}, sustain: #{duration}, amp: $amp
    """
  end
  @doc """
  TODO: Make this sensitive to the beats per measure set by the user (when they get that option)
  """
  def sleep(duration), do: sleep(duration, :beats)
  def sleep(duration,units) do
    t = case units do
      :beats -> duration
      :measures -> duration * 4
    end
    t = t * 0.99
    """
    use_bpm $tempo
    sleep #{t}
    """
  end
  def trigger_sample(sample) do
    """
    use_bpm $tempo
    sample #{inspect sample}, amp: $amp
    """
  end
  def sync(flag) do
    "sync :#{flag}"
  end

end


Game has many moves

Game has many moves

defmodule Chess.Store.Game do
  @moduledoc false

  use Ecto.Schema
  use Timex.Ecto.Timestamps

  import Ecto.Changeset
  import Ecto.Query
  import ChessWeb.Gettext

  alias Chess.Board
  alias Chess.GameState
  alias Chess.Store.Game
  alias Chess.Store.Move
  alias Chess.Store.User

  schema "games" do
    field :board, :map, default: Board.default()
    field :turn, :string, default: "white"
    field :state, :string

    belongs_to :user, User
    belongs_to :opponent, User, references: :id

    has_many :moves, Move

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, required_attrs())
    |> validate_required(required_attrs())
    |> foreign_key_constraint(:user_id)
    |> foreign_key_constraint(:opponent_id)
  end

  def move_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, required_attrs())
    |> validate_king_in_check(struct, params)
    |> change_turn(struct.turn)
    |> check_game_state
  end

  def change_turn(changeset, turn) do
    changeset
    |> put_change(:turn, _change_turn(turn))
  end

  def _change_turn("black"), do: "white"
  def _change_turn("white"), do: "black"

  def for_user(user) do
    for_user_id(user.id)
  end

  def for_user_id(user_id) do
    from game in Game,
      where: game.user_id == ^user_id,
      or_where: game.opponent_id == ^user_id
  end

  def check_game_state(changeset) do
    changeset
    |> put_change(
      :state, GameState.state(changeset.changes.board, changeset.changes.turn)
    )
  end

  def validate_king_in_check(changeset, %Game{turn: turn}, %{board: board}) do
    if GameState.king_in_check?(board, turn) do
      changeset
      |> add_error(
        :board,
        gettext("That move would leave your king in check")
      )
    else
      changeset
    end
  end
  def validate_king_in_check(changeset, _, _), do: changeset

  def ordered(query) do
    query
    |> order_by([game], desc: game.inserted_at)
  end

  defp required_attrs, do: ~w[board turn user_id opponent_id]a
end

defmodule Chess.Store.Game do
  @moduledoc false

  use Ecto.Schema
  use Timex.Ecto.Timestamps

  import Ecto.Changeset
  import Ecto.Query
  import ChessWeb.Gettext

  alias Chess.Board
  alias Chess.Store.Game
  alias Chess.Store.User
  alias Chess.GameState

  schema "games" do
    field :board, :map, default: Board.default()
    field :turn, :string, default: "white"
    field :state, :string

    belongs_to :user, User
    belongs_to :opponent, User, references: :id

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, required_attrs())
    |> validate_required(required_attrs())
    |> foreign_key_constraint(:user_id)
    |> foreign_key_constraint(:opponent_id)
  end

  def move_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, required_attrs())
    |> validate_king_in_check(struct, params)
    |> change_turn(struct.turn)
    |> check_game_state
  end

  def change_turn(changeset, turn) do
    changeset
    |> put_change(:turn, _change_turn(turn))
  end

  def _change_turn("black"), do: "white"
  def _change_turn("white"), do: "black"

  def for_user(user) do
    for_user_id(user.id)
  end

  def for_user_id(user_id) do
    from game in Game,
      where: game.user_id == ^user_id,
      or_where: game.opponent_id == ^user_id
  end

  def check_game_state(changeset) do
    changeset
    |> put_change(
      :state, GameState.state(changeset.changes.board, changeset.changes.turn)
    )
  end

  def validate_king_in_check(changeset, %Game{turn: turn}, %{board: board}) do
    if GameState.king_in_check?(board, turn) do
      changeset
      |> add_error(
        :board,
        gettext("That move would leave your king in check")
      )
    else
      changeset
    end
  end
  def validate_king_in_check(changeset, _, _), do: changeset

  def ordered(query) do
    query
    |> order_by([game], desc: game.inserted_at)
  end

  defp required_attrs, do: ~w[board turn user_id opponent_id]a
end


Consolidate test deletions in delete_all

defmodule RemoteRetro.UserRetroCase do
  use ExUnit.CaseTemplate
  alias RemoteRetro.{Repo, User, Retro, Participation}
  import Ecto.Query, only: [from: 2]

  @test_user_one Application.get_env(:remote_retro, :test_user_one)
  @test_user_two Application.get_env(:remote_retro, :test_user_two)

  setup_all _tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
    # we are setting :auto here so that the data persists for all tests,
    # normally (with :shared mode) every process runs in a transaction
    # and rolls back when it exits. setup_all runs in a distinct process
    # from each test so the data doesn't exist for each test.
    Ecto.Adapters.SQL.Sandbox.mode(Repo, :auto)

    {:ok, facilitator} = User.upsert_record_from(oauth_info: @test_user_one)
    {:ok, non_facilitator} = User.upsert_record_from(oauth_info: @test_user_two)
    retro = Repo.insert!(%Retro{facilitator_id: facilitator.id, stage: "idea-generation"})
    participation = Repo.insert!(%Participation{user_id: facilitator.id, retro_id: retro.id})
    participation_two = Repo.insert!(%Participation{user_id: non_facilitator.id, retro_id: retro.id})

    on_exit fn ->
      # this callback needs to checkout its own connection since it
      # runs in its own process
      :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
      Ecto.Adapters.SQL.Sandbox.mode(Repo, :auto)

      participation_ids = [participation.id, participation_two.id]
      from(p in Participation, where: p.id in ^participation_ids) |> Repo.delete_all

      Repo.delete(retro)

      user_ids = [facilitator.id, non_facilitator.id]
      from(u in User, where: u.id in ^user_ids) |> Repo.delete_all

      :ok
    end

    [facilitator: facilitator, retro: retro, non_facilitator: non_facilitator]
  end
end

defmodule RemoteRetro.UserRetroCase do
  use ExUnit.CaseTemplate
  alias RemoteRetro.{Repo, User, Retro, Participation}

  @test_user_one Application.get_env(:remote_retro, :test_user_one)
  @test_user_two Application.get_env(:remote_retro, :test_user_two)

  setup_all _tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
    # we are setting :auto here so that the data persists for all tests,
    # normally (with :shared mode) every process runs in a transaction
    # and rolls back when it exits. setup_all runs in a distinct process
    # from each test so the data doesn't exist for each test.
    Ecto.Adapters.SQL.Sandbox.mode(Repo, :auto)

    {:ok, facilitator} = User.upsert_record_from(oauth_info: @test_user_one)
    {:ok, non_facilitator} = User.upsert_record_from(oauth_info: @test_user_two)
    retro = Repo.insert!(%Retro{facilitator_id: facilitator.id, stage: "idea-generation"})
    participation = Repo.insert!(%Participation{user_id: facilitator.id, retro_id: retro.id})
    participation_two = Repo.insert!(%Participation{user_id: non_facilitator.id, retro_id: retro.id})

    on_exit fn ->
      # this callback needs to checkout its own connection since it
      # runs in its own process
      :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
      Ecto.Adapters.SQL.Sandbox.mode(Repo, :auto)

      Repo.delete(participation)
      Repo.delete(participation_two)
      Repo.delete(retro)
      Repo.delete(facilitator)
      Repo.delete(non_facilitator)

      :ok
    end

    [facilitator: facilitator, retro: retro, non_facilitator: non_facilitator]
  end
end


fixed typo in float moduledoc (#7915)

fixed typo in float moduledoc (#7915)

[ci skip]

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 and most operations are not exact,
  but operate on approximations. Those issues are not specific
  to Elixir, they are a property of floating point representation itself.

  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.

  To learn more about floating-point arithmetic visit:

    * [0.30000000000000004.com](http://0.30000000000000004.com/)
    * [What Every Programmer Should Know About Floating-Point Arithmetic](http://floating-point-gui.de/)

  """

  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, 0) when is_float(number) do
    :math.floor(number)
  end

  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, 0) when is_float(number) do
    :math.ceil(number)
  end

  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, 0) when is_float(float) do
    float |> :erlang.round() |> :erlang.float()
  end

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

  def round(float, precision) when is_float(float) 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}

  """
  @doc 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

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use Float.to_charlist/1 instead"
  def to_char_list(float), do: Float.to_charlist(float)

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use :erlang.float_to_list/2 instead"
  def to_char_list(float, options) do
    :erlang.float_to_list(float, expand_compact(options))
  end

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use :erlang.float_to_binary/2 instead"
  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 and most operations are not exact,
  but operate on approximations. Those issues are not specific
  to Elixir, they are a property of floating point representation itself.

  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.

  To learn more about floting-point arithmetic visit:

    * [0.30000000000000004.com](http://0.30000000000000004.com/)
    * [What Every Programmer Should Know About Floating-Point Arithmetic](http://floating-point-gui.de/)

  """

  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, 0) when is_float(number) do
    :math.floor(number)
  end

  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, 0) when is_float(number) do
    :math.ceil(number)
  end

  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, 0) when is_float(float) do
    float |> :erlang.round() |> :erlang.float()
  end

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

  def round(float, precision) when is_float(float) 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}

  """
  @doc 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

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use Float.to_charlist/1 instead"
  def to_char_list(float), do: Float.to_charlist(float)

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use :erlang.float_to_list/2 instead"
  def to_char_list(float, options) do
    :erlang.float_to_list(float, expand_compact(options))
  end

  @doc false
  # TODO: Remove by 2.0
  @deprecated "Use :erlang.float_to_binary/2 instead"
  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


docs(Regex): typo in deprecation warning for /r

defmodule Regex do
  @moduledoc ~S"""
  Provides regular expressions for Elixir. Built on top of Erlang's `:re`
  module.

  As the `:re` module, Regex is based on PCRE
  (Perl Compatible Regular Expressions). More information can be
  found in the [`:re` module documentation](http://www.erlang.org/doc/man/re.html).

  Regular expressions in Elixir can be created using `Regex.compile!/2`
  or using the special form with [`~r`](Kernel.html#sigil_r/2) or [`~R`](Kernel.html#sigil_R/2):

      # A simple regular expressions that matches foo anywhere in the string
      ~r/foo/

      # A regular expression with case insensitive and unicode options
      ~r/foo/iu

  A Regex is represented internally as the `Regex` struct. Therefore,
  `%Regex{}` can be used whenever there is a need to match on them.

  ## Modifiers

  The modifiers available when creating a Regex are:

    * `unicode` (u) - enables unicode specific patterns like `\p` and change
      modifiers like `\w`, `\W`, `\s` and friends to also match on unicode.
      It expects valid unicode strings to be given on match

    * `caseless` (i) - add case insensitivity

    * `dotall` (s) - causes dot to match newlines and also set newline to
      anycrlf; the new line setting can be overridden by setting `(*CR)` or
      `(*LF)` or `(*CRLF)` or `(*ANY)` according to re documentation

    * `multiline` (m) - causes `^` and `$` to mark the beginning and end of
      each line; use `\A` and `\z` to match the end or beginning of the string

    * `extended` (x) - whitespace characters are ignored except when escaped
      and allow `#` to delimit comments

    * `firstline` (f) - forces the unanchored pattern to match before or at the
      first newline, though the matched text may continue over the newline

    * `ungreedy` (U) - inverts the "greediness" of the regexp
      (the previous `r` option is deprecated in favor of `U`)

  The options not available are:

    * `anchored` - not available, use `^` or `\A` instead
    * `dollar_endonly` - not available, use `\z` instead
    * `no_auto_capture` - not available, use `?:` instead
    * `newline` - not available, use `(*CR)` or `(*LF)` or `(*CRLF)` or
      `(*ANYCRLF)` or `(*ANY)` at the beginning of the regexp according to the
      re documentation

  ## Captures

  Many functions in this module handle what to capture in a regex
  match via the `:capture` option. The supported values are:

    * `:all` - all captured subpatterns including the complete matching string
      (this is the default)

    * `:first` - only the first captured subpattern, which is always the
      complete matching part of the string; all explicitly captured subpatterns
      are discarded

    * `:all_but_first`- all but the first matching subpattern, i.e. all
      explicitly captured subpatterns, but not the complete matching part of
      the string

    * `:none` - do not return matching subpatterns at all

    * `:all_names` - captures all names in the Regex

    * `list(binary)` - a list of named captures to capture

  """

  defstruct re_pattern: nil, source: "", opts: ""

  @type t :: %__MODULE__{re_pattern: term, source: binary, opts: binary}

  defmodule CompileError do
    defexception message: "regex could not be compiled"
  end

  @doc """
  Compiles the regular expression.

  The given options can either be a binary with the characters
  representing the same regex options given to the `~r` sigil,
  or a list of options, as expected by the Erlang's [`:re` module](http://www.erlang.org/doc/man/re.html).

  It returns `{:ok, regex}` in case of success,
  `{:error, reason}` otherwise.

  ## Examples

      iex> Regex.compile("foo")
      {:ok, ~r"foo"}

      iex> Regex.compile("*foo")
      {:error, {'nothing to repeat', 0}}

  """
  @spec compile(binary, binary | [term]) :: {:ok, t} | {:error, any}
  def compile(source, options \\ "")

  def compile(source, options) when is_binary(options) do
    case translate_options(options, []) do
      {:error, rest} ->
        {:error, {:invalid_option, rest}}

      translated_options ->
        compile(source, translated_options, options)
    end
  end

  def compile(source, options) when is_list(options) do
    compile(source, options, "")
  end

  defp compile(source, opts, doc_opts) when is_binary(source) do
    case :re.compile(source, opts) do
      {:ok, re_pattern} ->
        {:ok, %Regex{re_pattern: re_pattern, source: source, opts: doc_opts}}
      error ->
        error
    end
  end

  @doc """
  Compiles the regular expression according to the given options.
  Fails with `Regex.CompileError` if the regex cannot be compiled.
  """
  @spec compile(binary, binary | [term]) :: t
  def compile!(source, options \\ "") do
    case compile(source, options) do
      {:ok, regex} -> regex
      {:error, {reason, at}} -> raise Regex.CompileError, message: "#{reason} at position #{at}"
    end
  end

  @doc """
  Returns a boolean indicating whether there was a match or not.

  ## Examples

      iex> Regex.match?(~r/foo/, "foo")
      true

      iex> Regex.match?(~r/foo/, "bar")
      false

  """
  @spec match?(t, String.t) :: boolean
  def match?(%Regex{re_pattern: compiled}, string) when is_binary(string) do
    :re.run(string, compiled, [{:capture, :none}]) == :match
  end

  @doc """
  Returns `true` if the given `term` is a regex.
  Otherwise returns `false`.

  ## Examples

      iex> Regex.regex?(~r/foo/)
      true

      iex> Regex.regex?(0)
      false

  """
  @spec regex?(t) :: true
  @spec regex?(any) :: false
  def regex?(term)
  def regex?(%Regex{}), do: true
  def regex?(_), do: false

  @doc """
  Runs the regular expression against the given string until the first match.
  It returns a list with all captures or `nil` if no match occurred.

  ## Options

    * `:return`  - set to `:index` to return indexes. Defaults to `:binary`.
    * `:capture` - what to capture in the result. Check the moduledoc for `Regex`
      to see the possible capture values.

  ## Examples

      iex> Regex.run(~r/c(d)/, "abcd")
      ["cd", "d"]

      iex> Regex.run(~r/e/, "abcd")
      nil

      iex> Regex.run(~r/c(d)/, "abcd", return: :index)
      [{2, 2}, {3, 1}]

  """
  @spec run(t, binary, [term]) :: nil | [binary] | [{integer, integer}]
  def run(regex, string, options \\ [])

  def run(%Regex{re_pattern: compiled}, string, options) when is_binary(string) do
    return   = Keyword.get(options, :return, :binary)
    captures = Keyword.get(options, :capture, :all)

    case :re.run(string, compiled, [{:capture, captures, return}]) do
      :nomatch -> nil
      :match   -> []
      {:match, results} -> results
    end
  end

  @doc """
  Returns the given captures as a map or `nil` if no captures are
  found. The option `:return` can be set to `:index` to get indexes
  back.

  ## Examples

      iex> Regex.named_captures(~r/c(?<foo>d)/, "abcd")
      %{"foo" => "d"}

      iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "abcd")
      %{"bar" => "d", "foo" => "b"}

      iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "efgh")
      nil

  """
  @spec named_captures(t, String.t, [term]) :: map | nil
  def named_captures(regex, string, options \\ []) when is_binary(string) do
    names = names(regex)
    options = Keyword.put(options, :capture, names)
    results = run(regex, string, options)
    if results, do: Enum.zip(names, results) |> Enum.into(%{})
  end

  @doc """
  Returns the underlying `re_pattern` in the regular expression.
  """
  @spec re_pattern(t) :: term
  def re_pattern(%Regex{re_pattern: compiled}) do
    compiled
  end

  @doc """
  Returns the regex source as a binary.

  ## Examples

      iex> Regex.source(~r(foo))
      "foo"

  """
  @spec source(t) :: String.t
  def source(%Regex{source: source}) do
    source
  end

  @doc """
  Returns the regex options as a string.

  ## Examples

      iex> Regex.opts(~r(foo)m)
      "m"

  """
  @spec opts(t) :: String.t
  def opts(%Regex{opts: opts}) do
    opts
  end

  @doc """
  Returns a list of names in the regex.

  ## Examples

      iex> Regex.names(~r/(?<foo>bar)/)
      ["foo"]

  """
  @spec names(t) :: [String.t]
  def names(%Regex{re_pattern: re_pattern}) do
    {:namelist, names} = :re.inspect(re_pattern, :namelist)
    names
  end

  @doc """
  Same as `run/3`, but scans the target several times collecting all
  matches of the regular expression.

  A list of lists is returned, where each entry in the primary list represents a
  match and each entry in the secondary list represents the captured contents.

  ## Options

    * `:return`  - set to `:index` to return indexes. Defaults to `:binary`.
    * `:capture` - what to capture in the result. Check the moduledoc for `Regex`
      to see the possible capture values.

  ## Examples

      iex> Regex.scan(~r/c(d|e)/, "abcd abce")
      [["cd", "d"], ["ce", "e"]]

      iex> Regex.scan(~r/c(?:d|e)/, "abcd abce")
      [["cd"], ["ce"]]

      iex> Regex.scan(~r/e/, "abcd")
      []

  """
  @spec scan(t, String.t, [term]) :: [[String.t]]
  def scan(regex, string, options \\ [])

  def scan(%Regex{re_pattern: compiled}, string, options) when is_binary(string) do
    return   = Keyword.get(options, :return, :binary)
    captures = Keyword.get(options, :capture, :all)
    options  = [{:capture, captures, return}, :global]

    case :re.run(string, compiled, options) do
      :match -> []
      :nomatch -> []
      {:match, results} -> results
    end
  end

  @doc """
  Splits the given target based on the given pattern and in the given number of
  parts.

  ## Options

    * `:parts` - when specified, splits the string into the given number of
      parts. If not specified, `:parts` defaults to `:infinity`, which will
      split the string into the maximum number of parts possible based on the
      given pattern.

    * `:trim` - when `true`, removes empty strings (`""`) from the result.

    * `:on` - specifies which captures to split the string on, and in what
      order. Defaults to `:first` which means captures inside the regex do not
      affect the splitting process.

    * `:include_captures` - when `true`, includes in the result the matches of
      the regular expression. Defaults to `false`.

  ## Examples

      iex> Regex.split(~r/-/, "a-b-c")
      ["a", "b", "c"]

      iex> Regex.split(~r/-/, "a-b-c", [parts: 2])
      ["a", "b-c"]

      iex> Regex.split(~r/-/, "abc")
      ["abc"]

      iex> Regex.split(~r//, "abc")
      ["a", "b", "c", ""]

      iex> Regex.split(~r/a(?<second>b)c/, "abc")
      ["", ""]

      iex> Regex.split(~r/a(?<second>b)c/, "abc", on: [:second])
      ["a", "c"]

      iex> Regex.split(~r/(x)/, "Elixir", include_captures: true)
      ["Eli", "x", "ir"]

      iex> Regex.split(~r/a(?<second>b)c/, "abc", on: [:second], include_captures: true)
      ["a", "b", "c"]

  """
  @spec split(t, String.t, [term]) :: [String.t]
  def split(regex, string, options \\ [])

  def split(%Regex{}, "", opts) do
    if Keyword.get(opts, :trim, false) do
      []
    else
      [""]
    end
  end

  def split(%Regex{re_pattern: compiled}, string, opts) when is_binary(string) and is_list(opts) do
    on = Keyword.get(opts, :on, :first)
    case :re.run(string, compiled, [:global, capture: on]) do
      {:match, matches} ->
        do_split(matches, string, 0,
                 parts_to_index(Keyword.get(opts, :parts, :infinity)),
                 Keyword.get(opts, :trim, false),
                 Keyword.get(opts, :include_captures, false))
      :match ->
        [string]
      :nomatch ->
        [string]
    end
  end

  defp parts_to_index(:infinity),                      do: 0
  defp parts_to_index(n) when is_integer(n) and n > 0, do: n

  defp do_split(_, string, offset, _counter, true, _with_captures) when byte_size(string) <= offset,
    do: []

  defp do_split(_, string, offset, 1, _trim, _with_captures),
    do: [binary_part(string, offset, byte_size(string) - offset)]

  defp do_split([], string, offset, _counter, _trim, _with_captures),
    do: [binary_part(string, offset, byte_size(string) - offset)]

  defp do_split([[{pos, _}|h]|t], string, offset, counter, trim, with_captures) when pos - offset < 0,
    do: do_split([h|t], string, offset, counter, trim, with_captures)

  defp do_split([[]|t], string, offset, counter, trim, with_captures),
    do: do_split(t, string, offset, counter, trim, with_captures)

  defp do_split([[{pos, length}|h]|t], string, offset, counter, trim, true) do
    new_offset = pos + length
    keep = pos - offset

    if keep == 0 and length == 0 do
      do_split([h|t], string, new_offset, counter, trim, true)
    else
      <<_::binary-size(offset), part::binary-size(keep), match::binary-size(length), _::binary>> = string

      if keep == 0 and (length == 0 or trim) do
        [match | do_split([h|t], string, new_offset, counter - 1, trim, true)]
      else
        [part, match | do_split([h|t], string, new_offset, counter - 1, trim, true)]
      end
    end
  end

  defp do_split([[{pos, length}|h]|t], string, offset, counter, trim, false) do
    new_offset = pos + length
    keep = pos - offset

    if keep == 0 and (length == 0 or trim) do
      do_split([h|t], string, new_offset, counter, trim, false)
    else
      <<_::binary-size(offset), part::binary-size(keep), _::binary>> = string
      [part | do_split([h|t], string, new_offset, counter - 1, trim, false)]
    end
  end

  @doc ~S"""
  Receives a regex, a binary and a replacement, returns a new
  binary where all matches are replaced by the replacement.

  The replacement can be either a string or a function. The string
  is used as a replacement for every match and it allows specific
  captures to be accessed via `\\N` or `\g{N}`, where `N` is the
  capture. In case `\\0` is used, the whole match is inserted.

  When the replacement is a function, the function may have arity
  N where each argument maps to a capture, with the first argument
  being the whole match. If the function expects more arguments
  than captures found, the remaining arguments will receive `""`.

  ## Options

    * `:global` - when `false`, replaces only the first occurrence
      (defaults to `true`)

  ## Examples

      iex> Regex.replace(~r/d/, "abc", "d")
      "abc"

      iex> Regex.replace(~r/b/, "abc", "d")
      "adc"

      iex> Regex.replace(~r/b/, "abc", "[\\0]")
      "a[b]c"

      iex> Regex.replace(~r/a(b|d)c/, "abcadc", "[\\1]")
      "[b][d]"

      iex> Regex.replace(~r/\.(\d)$/, "500.5", ".\\g{1}0")
      "500.50"

      iex> Regex.replace(~r/a(b|d)c/, "abcadc", fn _, x -> "[#{x}]" end)
      "[b][d]"

      iex> Regex.replace(~r/a/, "abcadc", "A", global: false)
      "Abcadc"

  """
  @spec replace(t, String.t, String.t | (... -> String.t), [term]) :: String.t
  def replace(regex, string, replacement, options \\ [])

  def replace(regex, string, replacement, options)
      when is_binary(string) and is_binary(replacement) and is_list(options) do
    do_replace(regex, string, precompile_replacement(replacement), options)
  end

  def replace(regex, string, replacement, options)
      when is_binary(string) and is_function(replacement) and is_list(options)  do
    {:arity, arity} = :erlang.fun_info(replacement, :arity)
    do_replace(regex, string, {replacement, arity}, options)
  end

  defp do_replace(%Regex{re_pattern: compiled}, string, replacement, options) do
    opts = if Keyword.get(options, :global) != false, do: [:global], else: []
    opts = [{:capture, :all, :index}|opts]

    case :re.run(string, compiled, opts) do
      :nomatch ->
        string
      {:match, [mlist|t]} when is_list(mlist) ->
        apply_list(string, replacement, [mlist|t]) |> IO.iodata_to_binary
      {:match, slist} ->
        apply_list(string, replacement, [slist]) |> IO.iodata_to_binary
    end
  end

  defp precompile_replacement(""),
    do: []

  defp precompile_replacement(<<?\\, ?g, ?{, rest::binary>>) when byte_size(rest) > 0 do
    {ns, <<?}, rest::binary>>} = pick_int(rest)
    [List.to_integer(ns) | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<?\\, ?\\, rest::binary>>) do
    [<<?\\>> | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<?\\, x, rest::binary>>) when x in ?0..?9 do
    {ns, rest} = pick_int(rest)
    [List.to_integer([x|ns]) | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<x, rest::binary>>) do
    case precompile_replacement(rest) do
      [head | t] when is_binary(head) ->
        [<<x, head::binary>> | t]
      other ->
        [<<x>> | other]
    end
  end

  defp pick_int(<<x, rest::binary>>) when x in ?0..?9 do
    {found, rest} = pick_int(rest)
    {[x|found], rest}
  end

  defp pick_int(bin) do
    {[], bin}
  end

  defp apply_list(string, replacement, list) do
    apply_list(string, string, 0, replacement, list)
  end

  defp apply_list(_, "", _, _, []) do
    []
  end

  defp apply_list(_, string, _, _, []) do
    string
  end

  defp apply_list(whole, string, pos, replacement, [[{mpos, _} | _] | _] = list) when mpos > pos do
    length = mpos - pos
    <<untouched::binary-size(length), rest::binary>> = string
    [untouched | apply_list(whole, rest, mpos, replacement, list)]
  end

  defp apply_list(whole, string, pos, replacement, [[{pos, length} | _] = head | tail]) do
    <<_::size(length)-binary, rest::binary>> = string
    new_data = apply_replace(whole, replacement, head)
    [new_data | apply_list(whole, rest, pos + length, replacement, tail)]
  end

  defp apply_replace(string, {fun, arity}, indexes) do
    apply(fun, get_indexes(string, indexes, arity))
  end

  defp apply_replace(_, [bin], _) when is_binary(bin) do
    bin
  end

  defp apply_replace(string, repl, indexes) do
    indexes = List.to_tuple(indexes)

    for part <- repl do
      cond do
        is_binary(part) ->
          part
        part >= tuple_size(indexes) ->
          ""
        true ->
          get_index(string, elem(indexes, part))
      end
    end
  end

  defp get_index(_string, {pos, _len}) when pos < 0 do
    ""
  end

  defp get_index(string, {pos, len}) do
    <<_::size(pos)-binary, res::size(len)-binary, _::binary>> = string
    res
  end

  defp get_indexes(_string, _, 0) do
    []
  end

  defp get_indexes(string, [], arity) do
    [""|get_indexes(string, [], arity - 1)]
  end

  defp get_indexes(string, [h|t], arity) do
    [get_index(string, h)|get_indexes(string, t, arity - 1)]
  end

  {:ok, pattern} = :re.compile(~S"[.^$*+?()[{\\\|\s#]", [:unicode])
  @escape_pattern pattern

  @doc ~S"""
  Escapes a string to be literally matched in a regex.

  ## Examples

      iex> Regex.escape(".")
      "\\."

      iex> Regex.escape("\\what if")
      "\\\\what\\ if"

  """
  @spec escape(String.t) :: String.t
  def escape(string) when is_binary(string) do
    :re.replace(string, @escape_pattern, "\\\\&", [:global, {:return, :binary}])
  end

  # Helpers

  @doc false
  # Unescape map function used by Macro.unescape_string.
  def unescape_map(?f), do: ?\f
  def unescape_map(?n), do: ?\n
  def unescape_map(?r), do: ?\r
  def unescape_map(?t), do: ?\t
  def unescape_map(?v), do: ?\v
  def unescape_map(?a), do: ?\a
  def unescape_map(_),  do: false

  # Private Helpers

  defp translate_options(<<?u, t::binary>>, acc), do: translate_options(t, [:unicode, :ucp|acc])
  defp translate_options(<<?i, t::binary>>, acc), do: translate_options(t, [:caseless|acc])
  defp translate_options(<<?x, t::binary>>, acc), do: translate_options(t, [:extended|acc])
  defp translate_options(<<?f, t::binary>>, acc), do: translate_options(t, [:firstline|acc])
  defp translate_options(<<?U, t::binary>>, acc), do: translate_options(t, [:ungreedy|acc])
  defp translate_options(<<?s, t::binary>>, acc), do: translate_options(t, [:dotall, {:newline, :anycrlf}|acc])
  defp translate_options(<<?m, t::binary>>, acc), do: translate_options(t, [:multiline|acc])

  defp translate_options(<<?r, t::binary>>, acc) do
    IO.write :stderr, "warning: the /r modifier in regular expressions is deprecated, please use /U instead\n" <>
                      Exception.format_stacktrace
    translate_options(t, [:ungreedy|acc])
  end

  defp translate_options(<<>>, acc), do: acc
  defp translate_options(rest, _acc), do: {:error, rest}
end

defmodule Regex do
  @moduledoc ~S"""
  Provides regular expressions for Elixir. Built on top of Erlang's `:re`
  module.

  As the `:re` module, Regex is based on PCRE
  (Perl Compatible Regular Expressions). More information can be
  found in the [`:re` module documentation](http://www.erlang.org/doc/man/re.html).

  Regular expressions in Elixir can be created using `Regex.compile!/2`
  or using the special form with [`~r`](Kernel.html#sigil_r/2) or [`~R`](Kernel.html#sigil_R/2):

      # A simple regular expressions that matches foo anywhere in the string
      ~r/foo/

      # A regular expression with case insensitive and unicode options
      ~r/foo/iu

  A Regex is represented internally as the `Regex` struct. Therefore,
  `%Regex{}` can be used whenever there is a need to match on them.

  ## Modifiers

  The modifiers available when creating a Regex are:

    * `unicode` (u) - enables unicode specific patterns like `\p` and change
      modifiers like `\w`, `\W`, `\s` and friends to also match on unicode.
      It expects valid unicode strings to be given on match

    * `caseless` (i) - add case insensitivity

    * `dotall` (s) - causes dot to match newlines and also set newline to
      anycrlf; the new line setting can be overridden by setting `(*CR)` or
      `(*LF)` or `(*CRLF)` or `(*ANY)` according to re documentation

    * `multiline` (m) - causes `^` and `$` to mark the beginning and end of
      each line; use `\A` and `\z` to match the end or beginning of the string

    * `extended` (x) - whitespace characters are ignored except when escaped
      and allow `#` to delimit comments

    * `firstline` (f) - forces the unanchored pattern to match before or at the
      first newline, though the matched text may continue over the newline

    * `ungreedy` (U) - inverts the "greediness" of the regexp
      (the previous `r` option is deprecated in favor of `U`)

  The options not available are:

    * `anchored` - not available, use `^` or `\A` instead
    * `dollar_endonly` - not available, use `\z` instead
    * `no_auto_capture` - not available, use `?:` instead
    * `newline` - not available, use `(*CR)` or `(*LF)` or `(*CRLF)` or
      `(*ANYCRLF)` or `(*ANY)` at the beginning of the regexp according to the
      re documentation

  ## Captures

  Many functions in this module handle what to capture in a regex
  match via the `:capture` option. The supported values are:

    * `:all` - all captured subpatterns including the complete matching string
      (this is the default)

    * `:first` - only the first captured subpattern, which is always the
      complete matching part of the string; all explicitly captured subpatterns
      are discarded

    * `:all_but_first`- all but the first matching subpattern, i.e. all
      explicitly captured subpatterns, but not the complete matching part of
      the string

    * `:none` - do not return matching subpatterns at all

    * `:all_names` - captures all names in the Regex

    * `list(binary)` - a list of named captures to capture

  """

  defstruct re_pattern: nil, source: "", opts: ""

  @type t :: %__MODULE__{re_pattern: term, source: binary, opts: binary}

  defmodule CompileError do
    defexception message: "regex could not be compiled"
  end

  @doc """
  Compiles the regular expression.

  The given options can either be a binary with the characters
  representing the same regex options given to the `~r` sigil,
  or a list of options, as expected by the Erlang's [`:re` module](http://www.erlang.org/doc/man/re.html).

  It returns `{:ok, regex}` in case of success,
  `{:error, reason}` otherwise.

  ## Examples

      iex> Regex.compile("foo")
      {:ok, ~r"foo"}

      iex> Regex.compile("*foo")
      {:error, {'nothing to repeat', 0}}

  """
  @spec compile(binary, binary | [term]) :: {:ok, t} | {:error, any}
  def compile(source, options \\ "")

  def compile(source, options) when is_binary(options) do
    case translate_options(options, []) do
      {:error, rest} ->
        {:error, {:invalid_option, rest}}

      translated_options ->
        compile(source, translated_options, options)
    end
  end

  def compile(source, options) when is_list(options) do
    compile(source, options, "")
  end

  defp compile(source, opts, doc_opts) when is_binary(source) do
    case :re.compile(source, opts) do
      {:ok, re_pattern} ->
        {:ok, %Regex{re_pattern: re_pattern, source: source, opts: doc_opts}}
      error ->
        error
    end
  end

  @doc """
  Compiles the regular expression according to the given options.
  Fails with `Regex.CompileError` if the regex cannot be compiled.
  """
  @spec compile(binary, binary | [term]) :: t
  def compile!(source, options \\ "") do
    case compile(source, options) do
      {:ok, regex} -> regex
      {:error, {reason, at}} -> raise Regex.CompileError, message: "#{reason} at position #{at}"
    end
  end

  @doc """
  Returns a boolean indicating whether there was a match or not.

  ## Examples

      iex> Regex.match?(~r/foo/, "foo")
      true

      iex> Regex.match?(~r/foo/, "bar")
      false

  """
  @spec match?(t, String.t) :: boolean
  def match?(%Regex{re_pattern: compiled}, string) when is_binary(string) do
    :re.run(string, compiled, [{:capture, :none}]) == :match
  end

  @doc """
  Returns `true` if the given `term` is a regex.
  Otherwise returns `false`.

  ## Examples

      iex> Regex.regex?(~r/foo/)
      true

      iex> Regex.regex?(0)
      false

  """
  @spec regex?(t) :: true
  @spec regex?(any) :: false
  def regex?(term)
  def regex?(%Regex{}), do: true
  def regex?(_), do: false

  @doc """
  Runs the regular expression against the given string until the first match.
  It returns a list with all captures or `nil` if no match occurred.

  ## Options

    * `:return`  - set to `:index` to return indexes. Defaults to `:binary`.
    * `:capture` - what to capture in the result. Check the moduledoc for `Regex`
      to see the possible capture values.

  ## Examples

      iex> Regex.run(~r/c(d)/, "abcd")
      ["cd", "d"]

      iex> Regex.run(~r/e/, "abcd")
      nil

      iex> Regex.run(~r/c(d)/, "abcd", return: :index)
      [{2, 2}, {3, 1}]

  """
  @spec run(t, binary, [term]) :: nil | [binary] | [{integer, integer}]
  def run(regex, string, options \\ [])

  def run(%Regex{re_pattern: compiled}, string, options) when is_binary(string) do
    return   = Keyword.get(options, :return, :binary)
    captures = Keyword.get(options, :capture, :all)

    case :re.run(string, compiled, [{:capture, captures, return}]) do
      :nomatch -> nil
      :match   -> []
      {:match, results} -> results
    end
  end

  @doc """
  Returns the given captures as a map or `nil` if no captures are
  found. The option `:return` can be set to `:index` to get indexes
  back.

  ## Examples

      iex> Regex.named_captures(~r/c(?<foo>d)/, "abcd")
      %{"foo" => "d"}

      iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "abcd")
      %{"bar" => "d", "foo" => "b"}

      iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "efgh")
      nil

  """
  @spec named_captures(t, String.t, [term]) :: map | nil
  def named_captures(regex, string, options \\ []) when is_binary(string) do
    names = names(regex)
    options = Keyword.put(options, :capture, names)
    results = run(regex, string, options)
    if results, do: Enum.zip(names, results) |> Enum.into(%{})
  end

  @doc """
  Returns the underlying `re_pattern` in the regular expression.
  """
  @spec re_pattern(t) :: term
  def re_pattern(%Regex{re_pattern: compiled}) do
    compiled
  end

  @doc """
  Returns the regex source as a binary.

  ## Examples

      iex> Regex.source(~r(foo))
      "foo"

  """
  @spec source(t) :: String.t
  def source(%Regex{source: source}) do
    source
  end

  @doc """
  Returns the regex options as a string.

  ## Examples

      iex> Regex.opts(~r(foo)m)
      "m"

  """
  @spec opts(t) :: String.t
  def opts(%Regex{opts: opts}) do
    opts
  end

  @doc """
  Returns a list of names in the regex.

  ## Examples

      iex> Regex.names(~r/(?<foo>bar)/)
      ["foo"]

  """
  @spec names(t) :: [String.t]
  def names(%Regex{re_pattern: re_pattern}) do
    {:namelist, names} = :re.inspect(re_pattern, :namelist)
    names
  end

  @doc """
  Same as `run/3`, but scans the target several times collecting all
  matches of the regular expression.

  A list of lists is returned, where each entry in the primary list represents a
  match and each entry in the secondary list represents the captured contents.

  ## Options

    * `:return`  - set to `:index` to return indexes. Defaults to `:binary`.
    * `:capture` - what to capture in the result. Check the moduledoc for `Regex`
      to see the possible capture values.

  ## Examples

      iex> Regex.scan(~r/c(d|e)/, "abcd abce")
      [["cd", "d"], ["ce", "e"]]

      iex> Regex.scan(~r/c(?:d|e)/, "abcd abce")
      [["cd"], ["ce"]]

      iex> Regex.scan(~r/e/, "abcd")
      []

  """
  @spec scan(t, String.t, [term]) :: [[String.t]]
  def scan(regex, string, options \\ [])

  def scan(%Regex{re_pattern: compiled}, string, options) when is_binary(string) do
    return   = Keyword.get(options, :return, :binary)
    captures = Keyword.get(options, :capture, :all)
    options  = [{:capture, captures, return}, :global]

    case :re.run(string, compiled, options) do
      :match -> []
      :nomatch -> []
      {:match, results} -> results
    end
  end

  @doc """
  Splits the given target based on the given pattern and in the given number of
  parts.

  ## Options

    * `:parts` - when specified, splits the string into the given number of
      parts. If not specified, `:parts` defaults to `:infinity`, which will
      split the string into the maximum number of parts possible based on the
      given pattern.

    * `:trim` - when `true`, removes empty strings (`""`) from the result.

    * `:on` - specifies which captures to split the string on, and in what
      order. Defaults to `:first` which means captures inside the regex do not
      affect the splitting process.

    * `:include_captures` - when `true`, includes in the result the matches of
      the regular expression. Defaults to `false`.

  ## Examples

      iex> Regex.split(~r/-/, "a-b-c")
      ["a", "b", "c"]

      iex> Regex.split(~r/-/, "a-b-c", [parts: 2])
      ["a", "b-c"]

      iex> Regex.split(~r/-/, "abc")
      ["abc"]

      iex> Regex.split(~r//, "abc")
      ["a", "b", "c", ""]

      iex> Regex.split(~r/a(?<second>b)c/, "abc")
      ["", ""]

      iex> Regex.split(~r/a(?<second>b)c/, "abc", on: [:second])
      ["a", "c"]

      iex> Regex.split(~r/(x)/, "Elixir", include_captures: true)
      ["Eli", "x", "ir"]

      iex> Regex.split(~r/a(?<second>b)c/, "abc", on: [:second], include_captures: true)
      ["a", "b", "c"]

  """
  @spec split(t, String.t, [term]) :: [String.t]
  def split(regex, string, options \\ [])

  def split(%Regex{}, "", opts) do
    if Keyword.get(opts, :trim, false) do
      []
    else
      [""]
    end
  end

  def split(%Regex{re_pattern: compiled}, string, opts) when is_binary(string) and is_list(opts) do
    on = Keyword.get(opts, :on, :first)
    case :re.run(string, compiled, [:global, capture: on]) do
      {:match, matches} ->
        do_split(matches, string, 0,
                 parts_to_index(Keyword.get(opts, :parts, :infinity)),
                 Keyword.get(opts, :trim, false),
                 Keyword.get(opts, :include_captures, false))
      :match ->
        [string]
      :nomatch ->
        [string]
    end
  end

  defp parts_to_index(:infinity),                      do: 0
  defp parts_to_index(n) when is_integer(n) and n > 0, do: n

  defp do_split(_, string, offset, _counter, true, _with_captures) when byte_size(string) <= offset,
    do: []

  defp do_split(_, string, offset, 1, _trim, _with_captures),
    do: [binary_part(string, offset, byte_size(string) - offset)]

  defp do_split([], string, offset, _counter, _trim, _with_captures),
    do: [binary_part(string, offset, byte_size(string) - offset)]

  defp do_split([[{pos, _}|h]|t], string, offset, counter, trim, with_captures) when pos - offset < 0,
    do: do_split([h|t], string, offset, counter, trim, with_captures)

  defp do_split([[]|t], string, offset, counter, trim, with_captures),
    do: do_split(t, string, offset, counter, trim, with_captures)

  defp do_split([[{pos, length}|h]|t], string, offset, counter, trim, true) do
    new_offset = pos + length
    keep = pos - offset

    if keep == 0 and length == 0 do
      do_split([h|t], string, new_offset, counter, trim, true)
    else
      <<_::binary-size(offset), part::binary-size(keep), match::binary-size(length), _::binary>> = string

      if keep == 0 and (length == 0 or trim) do
        [match | do_split([h|t], string, new_offset, counter - 1, trim, true)]
      else
        [part, match | do_split([h|t], string, new_offset, counter - 1, trim, true)]
      end
    end
  end

  defp do_split([[{pos, length}|h]|t], string, offset, counter, trim, false) do
    new_offset = pos + length
    keep = pos - offset

    if keep == 0 and (length == 0 or trim) do
      do_split([h|t], string, new_offset, counter, trim, false)
    else
      <<_::binary-size(offset), part::binary-size(keep), _::binary>> = string
      [part | do_split([h|t], string, new_offset, counter - 1, trim, false)]
    end
  end

  @doc ~S"""
  Receives a regex, a binary and a replacement, returns a new
  binary where all matches are replaced by the replacement.

  The replacement can be either a string or a function. The string
  is used as a replacement for every match and it allows specific
  captures to be accessed via `\\N` or `\g{N}`, where `N` is the
  capture. In case `\\0` is used, the whole match is inserted.

  When the replacement is a function, the function may have arity
  N where each argument maps to a capture, with the first argument
  being the whole match. If the function expects more arguments
  than captures found, the remaining arguments will receive `""`.

  ## Options

    * `:global` - when `false`, replaces only the first occurrence
      (defaults to `true`)

  ## Examples

      iex> Regex.replace(~r/d/, "abc", "d")
      "abc"

      iex> Regex.replace(~r/b/, "abc", "d")
      "adc"

      iex> Regex.replace(~r/b/, "abc", "[\\0]")
      "a[b]c"

      iex> Regex.replace(~r/a(b|d)c/, "abcadc", "[\\1]")
      "[b][d]"

      iex> Regex.replace(~r/\.(\d)$/, "500.5", ".\\g{1}0")
      "500.50"

      iex> Regex.replace(~r/a(b|d)c/, "abcadc", fn _, x -> "[#{x}]" end)
      "[b][d]"

      iex> Regex.replace(~r/a/, "abcadc", "A", global: false)
      "Abcadc"

  """
  @spec replace(t, String.t, String.t | (... -> String.t), [term]) :: String.t
  def replace(regex, string, replacement, options \\ [])

  def replace(regex, string, replacement, options)
      when is_binary(string) and is_binary(replacement) and is_list(options) do
    do_replace(regex, string, precompile_replacement(replacement), options)
  end

  def replace(regex, string, replacement, options)
      when is_binary(string) and is_function(replacement) and is_list(options)  do
    {:arity, arity} = :erlang.fun_info(replacement, :arity)
    do_replace(regex, string, {replacement, arity}, options)
  end

  defp do_replace(%Regex{re_pattern: compiled}, string, replacement, options) do
    opts = if Keyword.get(options, :global) != false, do: [:global], else: []
    opts = [{:capture, :all, :index}|opts]

    case :re.run(string, compiled, opts) do
      :nomatch ->
        string
      {:match, [mlist|t]} when is_list(mlist) ->
        apply_list(string, replacement, [mlist|t]) |> IO.iodata_to_binary
      {:match, slist} ->
        apply_list(string, replacement, [slist]) |> IO.iodata_to_binary
    end
  end

  defp precompile_replacement(""),
    do: []

  defp precompile_replacement(<<?\\, ?g, ?{, rest::binary>>) when byte_size(rest) > 0 do
    {ns, <<?}, rest::binary>>} = pick_int(rest)
    [List.to_integer(ns) | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<?\\, ?\\, rest::binary>>) do
    [<<?\\>> | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<?\\, x, rest::binary>>) when x in ?0..?9 do
    {ns, rest} = pick_int(rest)
    [List.to_integer([x|ns]) | precompile_replacement(rest)]
  end

  defp precompile_replacement(<<x, rest::binary>>) do
    case precompile_replacement(rest) do
      [head | t] when is_binary(head) ->
        [<<x, head::binary>> | t]
      other ->
        [<<x>> | other]
    end
  end

  defp pick_int(<<x, rest::binary>>) when x in ?0..?9 do
    {found, rest} = pick_int(rest)
    {[x|found], rest}
  end

  defp pick_int(bin) do
    {[], bin}
  end

  defp apply_list(string, replacement, list) do
    apply_list(string, string, 0, replacement, list)
  end

  defp apply_list(_, "", _, _, []) do
    []
  end

  defp apply_list(_, string, _, _, []) do
    string
  end

  defp apply_list(whole, string, pos, replacement, [[{mpos, _} | _] | _] = list) when mpos > pos do
    length = mpos - pos
    <<untouched::binary-size(length), rest::binary>> = string
    [untouched | apply_list(whole, rest, mpos, replacement, list)]
  end

  defp apply_list(whole, string, pos, replacement, [[{pos, length} | _] = head | tail]) do
    <<_::size(length)-binary, rest::binary>> = string
    new_data = apply_replace(whole, replacement, head)
    [new_data | apply_list(whole, rest, pos + length, replacement, tail)]
  end

  defp apply_replace(string, {fun, arity}, indexes) do
    apply(fun, get_indexes(string, indexes, arity))
  end

  defp apply_replace(_, [bin], _) when is_binary(bin) do
    bin
  end

  defp apply_replace(string, repl, indexes) do
    indexes = List.to_tuple(indexes)

    for part <- repl do
      cond do
        is_binary(part) ->
          part
        part >= tuple_size(indexes) ->
          ""
        true ->
          get_index(string, elem(indexes, part))
      end
    end
  end

  defp get_index(_string, {pos, _len}) when pos < 0 do
    ""
  end

  defp get_index(string, {pos, len}) do
    <<_::size(pos)-binary, res::size(len)-binary, _::binary>> = string
    res
  end

  defp get_indexes(_string, _, 0) do
    []
  end

  defp get_indexes(string, [], arity) do
    [""|get_indexes(string, [], arity - 1)]
  end

  defp get_indexes(string, [h|t], arity) do
    [get_index(string, h)|get_indexes(string, t, arity - 1)]
  end

  {:ok, pattern} = :re.compile(~S"[.^$*+?()[{\\\|\s#]", [:unicode])
  @escape_pattern pattern

  @doc ~S"""
  Escapes a string to be literally matched in a regex.

  ## Examples

      iex> Regex.escape(".")
      "\\."

      iex> Regex.escape("\\what if")
      "\\\\what\\ if"

  """
  @spec escape(String.t) :: String.t
  def escape(string) when is_binary(string) do
    :re.replace(string, @escape_pattern, "\\\\&", [:global, {:return, :binary}])
  end

  # Helpers

  @doc false
  # Unescape map function used by Macro.unescape_string.
  def unescape_map(?f), do: ?\f
  def unescape_map(?n), do: ?\n
  def unescape_map(?r), do: ?\r
  def unescape_map(?t), do: ?\t
  def unescape_map(?v), do: ?\v
  def unescape_map(?a), do: ?\a
  def unescape_map(_),  do: false

  # Private Helpers

  defp translate_options(<<?u, t::binary>>, acc), do: translate_options(t, [:unicode, :ucp|acc])
  defp translate_options(<<?i, t::binary>>, acc), do: translate_options(t, [:caseless|acc])
  defp translate_options(<<?x, t::binary>>, acc), do: translate_options(t, [:extended|acc])
  defp translate_options(<<?f, t::binary>>, acc), do: translate_options(t, [:firstline|acc])
  defp translate_options(<<?U, t::binary>>, acc), do: translate_options(t, [:ungreedy|acc])
  defp translate_options(<<?s, t::binary>>, acc), do: translate_options(t, [:dotall, {:newline, :anycrlf}|acc])
  defp translate_options(<<?m, t::binary>>, acc), do: translate_options(t, [:multiline|acc])

  defp translate_options(<<?r, t::binary>>, acc) do
    IO.write :stderr, "warning: the /r modified in regular expressions is deprecated, please use /U instead\n" <>
                      Exception.format_stacktrace
    translate_options(t, [:ungreedy|acc])
  end

  defp translate_options(<<>>, acc), do: acc
  defp translate_options(rest, _acc), do: {:error, rest}
end