Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New practice exercise variable-length-quantity #798

Merged

Conversation

jiegillet
Copy link
Contributor

New one.
Nothing too special, except that bit manipulation code is alway hard to read (or at least I can't make it legible).

@github-actions
Copy link
Contributor

Thank you for contributing to exercism/elixir 💜 🎉. This is an automated PR comment 🤖 for the maintainers of this repository that helps with the PR review process. You can safely ignore it and wait for a maintainer to review your changes.

Based on the files changed in this PR, it would be good to pay attention to the following details when reviewing the PR:

  • General steps

    • 🏆 Does this PR need to receive a label with a reputation modifier (reputation/contributed_code/{minor,major})? (A medium reputation amount is awarded by default, see docs)
  • Any exercise changed

    • 👤 Does the author of the PR need to be added as an author or contributor in <exercise>/.meta/config.json (see docs)?
    • 🔬 Do the analyzer and the analyzer comments exist for this exercise? Do they need to be changed?
    • 📜 Does the design file (<exercise>/.meta/design.md) need to be updated to document new implementation decisions?
  • Practice exercise changed

    • 🌲 Do prerequisites, practices, and difficulty in config.json need to be updated?
    • 🧑‍🏫 Are the changes in accordance with the community-wide problem specifiations?
  • Practice exercise tests changed

    • ⚪️ Are all tests except the first one skipped?
    • 📜 Does <exercise>/.meta/tests.toml need updating?

Automated comment created by PR Commenter 🤖.

@neenjaw
Copy link
Contributor

neenjaw commented Jun 29, 2021

What if we changed this exercise a bit, rather than working with a list of integers, what if we use bitstrings? So the exercise would be converting integers to VLQ encoded binaries, and VLQ encoded binaries to integers. I don't think we have anything like this in our practice list, might be super interesting to promote a binary-based pattern matching exercise.

To encode the integer you can do something like this:

integer = 78
<<encoded::7>> = <<integer::7>>
remainder = integer - encoded

if remainder == 0 do
  <<0::1, encoded::7, following::binary>>
else
  encode(remainder, <<1::1, encoded::7, following::binary>>)
end

@neenjaw neenjaw self-requested a review June 29, 2021 15:42
@angelikatyborska
Copy link
Member

I'm with @neenjaw, bitstrings are the perfect choice for this exercise. We definitely need more practice exercises for bitstrings!

Copy link
Contributor

@neenjaw neenjaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just adding a request for changes based on: #798 (comment)

@jiegillet
Copy link
Contributor Author

Excellent suggestion. It also made my code more readable :)

Also CI with 1.8 is failing, I'm not sure why. Any idea?

Copy link
Contributor

@neenjaw neenjaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice solution, looks awesome. <<_::8>> is equivalent to the binary type since a binary is always divisible by 8 bits. bitstrings can be any number of bits.

That's an interesting error. I wonder if there has been a regression somewhere in older versions because I thought that would have worked. Anyway, this seems to solve it with use Bitwise then using the infix operators:

defmodule VariableLengthQuantity do
  use Bitwise

  @doc """
  Encode integers into a bitstring of VLQ encoded bytes
  """
  @spec encode(integers :: [integer]) :: <<_::8>>
  def encode(integers) when is_list(integers) do
    integers
    |> Enum.map(&encode/1)
    |> Enum.map(&String.reverse/1)
    |> Enum.join(<<>>)
  end

  def encode(0), do: <<0>>

  def encode(int, leading \\ 0)
  def encode(0, _), do: <<>>

  def encode(int, leading) do
    rest = int >>> 7
    <<leading::1, int::7, encode(rest, 1)::binary>>
  end

  @doc """
  Decode a bitstring of VLQ encoded bytes into a series of integers
  """
  @spec decode(bytes :: <<_::8>>) :: {:ok, [integer]} | {:error, String.t()}
  def decode(bytes, _status \\ :complete, acc \\ 0)
  def decode(<<>>, :complete, _), do: {:ok, []}
  def decode(<<>>, :incomplete, _), do: {:error, "incomplete sequence"}

  def decode(<<lead::1, byte::7, bytes::binary>>, _status, acc) do
    acc = (acc <<< 7) + byte

    if lead == 1 do
      decode(bytes, :incomplete, acc)
    else
      with {:ok, result} <- decode(bytes, :complete),
           do: {:ok, [acc | result]}
    end
  end
end

jiegillet and others added 2 commits June 30, 2021 15:08
Co-authored-by: Tim Austin <tim@neenjaw.com>
@jiegillet
Copy link
Contributor Author

Nice, the inline operator looks better anyway :)

@angelikatyborska angelikatyborska added the x:size/large Large amount of work label Jun 30, 2021
Copy link
Member

@angelikatyborska angelikatyborska left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect 🥳 I'm so happy we finally have a bitstrings practice exercise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:size/large Large amount of work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants