Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "variable-length-quantity",
"name": "Variable Length Quantity",
"uuid": "6738a318-df5b-40bb-9877-04af138f11a7",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "change",
"name": "Change",
Expand Down
5 changes: 5 additions & 0 deletions exercises/practice/variable-length-quantity/.busted
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
return {
default = {
ROOT = { '.' }
}
}
34 changes: 34 additions & 0 deletions exercises/practice/variable-length-quantity/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Instructions

Implement variable length quantity encoding and decoding.

The goal of this exercise is to implement [VLQ][vlq] encoding/decoding.

In short, the goal of this encoding is to encode integer values in a way that would save bytes.
Only the first 7 bits of each byte are significant (right-justified; sort of like an ASCII byte).
So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes.
Of course, you will have a variable number of bytes depending upon your integer.
To indicate which is the last byte of the series, you leave bit #7 clear.
In all of the preceding bytes, you set bit #7.

So, if an integer is between `0-127`, it can be represented as one byte.
Although VLQ can deal with numbers of arbitrary sizes, for this exercise we will restrict ourselves to only numbers that fit in a 32-bit unsigned integer.
Here are examples of integers as 32-bit values, and the variable length quantities that they translate to:

```text
NUMBER VARIABLE QUANTITY
00000000 00
00000040 40
0000007F 7F
00000080 81 00
00002000 C0 00
00003FFF FF 7F
00004000 81 80 00
00100000 C0 80 00
001FFFFF FF FF 7F
00200000 81 80 80 00
08000000 C0 80 80 00
0FFFFFFF FF FF FF 7F
```

[vlq]: https://en.wikipedia.org/wiki/Variable-length_quantity
19 changes: 19 additions & 0 deletions exercises/practice/variable-length-quantity/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"glennj"
],
"files": {
"solution": [
"variable_length_quantity.moon"
],
"test": [
"variable_length_quantity_spec.moon"
],
"example": [
".meta/example.moon"
]
},
"blurb": "Implement variable length quantity encoding and decoding.",
"source": "A poor Splice developer having to implement MIDI encoding/decoding.",
"source_url": "https://splice.com"
}
27 changes: 27 additions & 0 deletions exercises/practice/variable-length-quantity/.meta/example.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
List = require 'pl.List'

{
encode: (numbers) ->
all_bytes = List!
for num in *numbers
bytes = List!
while num > 0
byte = num & 0x7F
num >>= 7
byte |= 0x80 if #bytes > 0
bytes\put byte
bytes\append 0 if #bytes == 0
all_bytes\extend bytes
all_bytes

decode: (bytes) ->
assert bytes[#bytes] & 0x80 == 0, 'incomplete sequence'
numbers = List!
current = 0
for byte in *bytes
current = (current << 7) | (byte & 0x7F)
if (byte & 0x80) == 0
numbers\append current
current = 0
numbers
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
int_list = (list) -> "{#{table.concat list, ', '}}"

{
module_name: 'VariableLengthQuantity',

generate_test: (case, level) ->
local lines
if case.expected.error
lines = {
"f = -> VariableLengthQuantity.#{case.property} #{int_list case.input.integers}"
"assert.has.errors f, #{quote case.expected.error}"
}
else
lines = {
"result = VariableLengthQuantity.#{case.property} #{int_list case.input.integers}",
"expected = #{int_list case.expected}"
"assert.are.same expected, result"
}
table.concat [indent line, level for line in *lines], '\n'
}
103 changes: 103 additions & 0 deletions exercises/practice/variable-length-quantity/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[35c9db2e-f781-4c52-b73b-8e76427defd0]
description = "Encode a series of integers, producing a series of bytes. -> zero"

[be44d299-a151-4604-a10e-d4b867f41540]
description = "Encode a series of integers, producing a series of bytes. -> arbitrary single byte"

[890bc344-cb80-45af-b316-6806a6971e81]
description = "Encode a series of integers, producing a series of bytes. -> asymmetric single byte"

[ea399615-d274-4af6-bbef-a1c23c9e1346]
description = "Encode a series of integers, producing a series of bytes. -> largest single byte"

[77b07086-bd3f-4882-8476-8dcafee79b1c]
description = "Encode a series of integers, producing a series of bytes. -> smallest double byte"

[63955a49-2690-4e22-a556-0040648d6b2d]
description = "Encode a series of integers, producing a series of bytes. -> arbitrary double byte"

[4977d113-251b-4d10-a3ad-2f5a7756bb58]
description = "Encode a series of integers, producing a series of bytes. -> asymmetric double byte"

[29da7031-0067-43d3-83a7-4f14b29ed97a]
description = "Encode a series of integers, producing a series of bytes. -> largest double byte"

[3345d2e3-79a9-4999-869e-d4856e3a8e01]
description = "Encode a series of integers, producing a series of bytes. -> smallest triple byte"

[5df0bc2d-2a57-4300-a653-a75ee4bd0bee]
description = "Encode a series of integers, producing a series of bytes. -> arbitrary triple byte"

[6731045f-1e00-4192-b5ae-98b22e17e9f7]
description = "Encode a series of integers, producing a series of bytes. -> asymmetric triple byte"

[f51d8539-312d-4db1-945c-250222c6aa22]
description = "Encode a series of integers, producing a series of bytes. -> largest triple byte"

[da78228b-544f-47b7-8bfe-d16b35bbe570]
description = "Encode a series of integers, producing a series of bytes. -> smallest quadruple byte"

[11ed3469-a933-46f1-996f-2231e05d7bb6]
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quadruple byte"

[b45ef770-cbba-48c2-bd3c-c6362679516e]
description = "Encode a series of integers, producing a series of bytes. -> asymmetric quadruple byte"

[d5f3f3c3-e0f1-4e7f-aad0-18a44f223d1c]
description = "Encode a series of integers, producing a series of bytes. -> largest quadruple byte"

[91a18b33-24e7-4bfb-bbca-eca78ff4fc47]
description = "Encode a series of integers, producing a series of bytes. -> smallest quintuple byte"

[5f34ff12-2952-4669-95fe-2d11b693d331]
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quintuple byte"

[9be46731-7cd5-415c-b960-48061cbc1154]
description = "Encode a series of integers, producing a series of bytes. -> asymmetric quintuple byte"

[7489694b-88c3-4078-9864-6fe802411009]
description = "Encode a series of integers, producing a series of bytes. -> maximum 32-bit integer input"

[f9b91821-cada-4a73-9421-3c81d6ff3661]
description = "Encode a series of integers, producing a series of bytes. -> two single-byte values"

[68694449-25d2-4974-ba75-fa7bb36db212]
description = "Encode a series of integers, producing a series of bytes. -> two multi-byte values"

[51a06b5c-de1b-4487-9a50-9db1b8930d85]
description = "Encode a series of integers, producing a series of bytes. -> many multi-byte values"

[baa73993-4514-4915-bac0-f7f585e0e59a]
description = "Decode a series of bytes, producing a series of integers. -> one byte"

[72e94369-29f9-46f2-8c95-6c5b7a595aee]
description = "Decode a series of bytes, producing a series of integers. -> two bytes"

[df5a44c4-56f7-464e-a997-1db5f63ce691]
description = "Decode a series of bytes, producing a series of integers. -> three bytes"

[1bb58684-f2dc-450a-8406-1f3452aa1947]
description = "Decode a series of bytes, producing a series of integers. -> four bytes"

[cecd5233-49f1-4dd1-a41a-9840a40f09cd]
description = "Decode a series of bytes, producing a series of integers. -> maximum 32-bit integer"

[e7d74ba3-8b8e-4bcb-858d-d08302e15695]
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error"

[aa378291-9043-4724-bc53-aca1b4a3fcb6]
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error, even if value is zero"

[a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee]
description = "Decode a series of bytes, producing a series of integers. -> multiple values"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
encode: (numbers) ->
error 'Implement the encode function'

decode: (bytes) ->
error 'Implement the decode function'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
VariableLengthQuantity = require 'variable_length_quantity'

describe 'variable-length-quantity', ->
describe 'Encode a series of integers, producing a series of bytes.', ->
it 'zero', ->
result = VariableLengthQuantity.encode {0}
expected = {0}
assert.are.same expected, result

pending 'arbitrary single byte', ->
result = VariableLengthQuantity.encode {64}
expected = {64}
assert.are.same expected, result

pending 'asymmetric single byte', ->
result = VariableLengthQuantity.encode {83}
expected = {83}
assert.are.same expected, result

pending 'largest single byte', ->
result = VariableLengthQuantity.encode {127}
expected = {127}
assert.are.same expected, result

pending 'smallest double byte', ->
result = VariableLengthQuantity.encode {128}
expected = {129, 0}
assert.are.same expected, result

pending 'arbitrary double byte', ->
result = VariableLengthQuantity.encode {8192}
expected = {192, 0}
assert.are.same expected, result

pending 'asymmetric double byte', ->
result = VariableLengthQuantity.encode {173}
expected = {129, 45}
assert.are.same expected, result

pending 'largest double byte', ->
result = VariableLengthQuantity.encode {16383}
expected = {255, 127}
assert.are.same expected, result

pending 'smallest triple byte', ->
result = VariableLengthQuantity.encode {16384}
expected = {129, 128, 0}
assert.are.same expected, result

pending 'arbitrary triple byte', ->
result = VariableLengthQuantity.encode {1048576}
expected = {192, 128, 0}
assert.are.same expected, result

pending 'asymmetric triple byte', ->
result = VariableLengthQuantity.encode {120220}
expected = {135, 171, 28}
assert.are.same expected, result

pending 'largest triple byte', ->
result = VariableLengthQuantity.encode {2097151}
expected = {255, 255, 127}
assert.are.same expected, result

pending 'smallest quadruple byte', ->
result = VariableLengthQuantity.encode {2097152}
expected = {129, 128, 128, 0}
assert.are.same expected, result

pending 'arbitrary quadruple byte', ->
result = VariableLengthQuantity.encode {134217728}
expected = {192, 128, 128, 0}
assert.are.same expected, result

pending 'asymmetric quadruple byte', ->
result = VariableLengthQuantity.encode {3503876}
expected = {129, 213, 238, 4}
assert.are.same expected, result

pending 'largest quadruple byte', ->
result = VariableLengthQuantity.encode {268435455}
expected = {255, 255, 255, 127}
assert.are.same expected, result

pending 'smallest quintuple byte', ->
result = VariableLengthQuantity.encode {268435456}
expected = {129, 128, 128, 128, 0}
assert.are.same expected, result

pending 'arbitrary quintuple byte', ->
result = VariableLengthQuantity.encode {4278190080}
expected = {143, 248, 128, 128, 0}
assert.are.same expected, result

pending 'asymmetric quintuple byte', ->
result = VariableLengthQuantity.encode {2254790917}
expected = {136, 179, 149, 194, 5}
assert.are.same expected, result

pending 'maximum 32-bit integer input', ->
result = VariableLengthQuantity.encode {4294967295}
expected = {143, 255, 255, 255, 127}
assert.are.same expected, result

pending 'two single-byte values', ->
result = VariableLengthQuantity.encode {64, 127}
expected = {64, 127}
assert.are.same expected, result

pending 'two multi-byte values', ->
result = VariableLengthQuantity.encode {16384, 1193046}
expected = {129, 128, 0, 200, 232, 86}
assert.are.same expected, result

pending 'many multi-byte values', ->
result = VariableLengthQuantity.encode {8192, 1193046, 268435455, 0, 16383, 16384}
expected = {192, 0, 200, 232, 86, 255, 255, 255, 127, 0, 255, 127, 129, 128, 0}
assert.are.same expected, result

describe 'Decode a series of bytes, producing a series of integers.', ->
pending 'one byte', ->
result = VariableLengthQuantity.decode {127}
expected = {127}
assert.are.same expected, result

pending 'two bytes', ->
result = VariableLengthQuantity.decode {192, 0}
expected = {8192}
assert.are.same expected, result

pending 'three bytes', ->
result = VariableLengthQuantity.decode {255, 255, 127}
expected = {2097151}
assert.are.same expected, result

pending 'four bytes', ->
result = VariableLengthQuantity.decode {129, 128, 128, 0}
expected = {2097152}
assert.are.same expected, result

pending 'maximum 32-bit integer', ->
result = VariableLengthQuantity.decode {143, 255, 255, 255, 127}
expected = {4294967295}
assert.are.same expected, result

pending 'incomplete sequence causes error', ->
f = -> VariableLengthQuantity.decode {255}
assert.has.errors f, 'incomplete sequence'

pending 'incomplete sequence causes error, even if value is zero', ->
f = -> VariableLengthQuantity.decode {128}
assert.has.errors f, 'incomplete sequence'

pending 'multiple values', ->
result = VariableLengthQuantity.decode {192, 0, 200, 232, 86, 255, 255, 255, 127, 0, 255, 127, 129, 128, 0}
expected = {8192, 1193046, 268435455, 0, 16383, 16384}
assert.are.same expected, result
Loading