Skip to content

Commit fda7741

Browse files
authored
add variable-length-quantity exercise (#427)
* add variable-lenght-quantity exercise * have tests use hex, update error message
1 parent e2646c7 commit fda7741

File tree

7 files changed

+326
-1
lines changed

7 files changed

+326
-1
lines changed

config.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,14 @@
918918
"practices": [],
919919
"prerequisites": [],
920920
"difficulty": 10
921+
},
922+
{
923+
"slug": "variable-length-quantity",
924+
"name": "Variable Length Quantity",
925+
"uuid": "5073fd82-3025-4370-ae93-37b661c17483",
926+
"practices": [],
927+
"prerequisites": [],
928+
"difficulty": 6
921929
}
922930
]
923931
},
@@ -964,4 +972,4 @@
964972
"typing/strong",
965973
"used_for/scripts"
966974
]
967-
}
975+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Instructions
2+
3+
Implement variable length quantity encoding and decoding.
4+
5+
The goal of this exercise is to implement [VLQ][vlq] encoding/decoding.
6+
7+
In short, the goal of this encoding is to encode integer values in a way that would save bytes.
8+
Only the first 7 bits of each byte are significant (right-justified; sort of like an ASCII byte).
9+
So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes.
10+
Of course, you will have a variable number of bytes depending upon your integer.
11+
To indicate which is the last byte of the series, you leave bit #7 clear.
12+
In all of the preceding bytes, you set bit #7.
13+
14+
So, if an integer is between `0-127`, it can be represented as one byte.
15+
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.
16+
Here are examples of integers as 32-bit values, and the variable length quantities that they translate to:
17+
18+
```text
19+
NUMBER VARIABLE QUANTITY
20+
00000000 00
21+
00000040 40
22+
0000007F 7F
23+
00000080 81 00
24+
00002000 C0 00
25+
00003FFF FF 7F
26+
00004000 81 80 00
27+
00100000 C0 80 00
28+
001FFFFF FF FF 7F
29+
00200000 81 80 80 00
30+
08000000 C0 80 80 00
31+
0FFFFFFF FF FF FF 7F
32+
```
33+
34+
[vlq]: https://en.wikipedia.org/wiki/Variable-length_quantity
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"kmarker1101"
4+
],
5+
"files": {
6+
"solution": [
7+
"variable-length-quantity.el"
8+
],
9+
"test": [
10+
"variable-length-quantity-test.el"
11+
],
12+
"example": [
13+
".meta/example.el"
14+
]
15+
},
16+
"blurb": "Implement variable length quantity encoding and decoding.",
17+
"source": "A poor Splice developer having to implement MIDI encoding/decoding.",
18+
"source_url": "https://splice.com"
19+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
;;; variable-length-quantity.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*-
2+
3+
;;; Commentary:
4+
5+
;;; Code:
6+
7+
8+
(defun encode (hex-numbers)
9+
"Encode a list of hexadecimal numbers into VLQ format, returning a list of hexadecimal values."
10+
(let ((encode-single (lambda (number)
11+
(let ((byte-string (list (logand number #x7F)))) ; Initialize with the least significant byte
12+
(setq number (lsh number -7)) ; Shift right before the loop
13+
(while (> number 0)
14+
(push (logior (logand number #x7F) #x80) byte-string)
15+
(setq number (lsh number -7)))
16+
byte-string))))
17+
(apply #'append (mapcar encode-single hex-numbers))))
18+
19+
20+
(defun decode (hex-values)
21+
"Decode a list of hexadecimal values from VLQ format into hexadecimal numbers."
22+
(let ((values '())
23+
(number 0)
24+
(incomplete t))
25+
(dolist (byte hex-values)
26+
(setq number (lsh number 7))
27+
(setq number (logior number (logand byte #x7F)))
28+
(if (= (logand byte #x80) 0)
29+
(progn
30+
(setq values (cons number values))
31+
(setq number 0)
32+
(setq incomplete nil))
33+
(setq incomplete t)))
34+
(if incomplete
35+
(error "imcomplete sequence")
36+
(reverse values))))
37+
38+
39+
(provide 'variable-length-quantity)
40+
;;; variable-length-quantity.el ends here
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[35c9db2e-f781-4c52-b73b-8e76427defd0]
13+
description = "Encode a series of integers, producing a series of bytes. -> zero"
14+
15+
[be44d299-a151-4604-a10e-d4b867f41540]
16+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary single byte"
17+
18+
[ea399615-d274-4af6-bbef-a1c23c9e1346]
19+
description = "Encode a series of integers, producing a series of bytes. -> largest single byte"
20+
21+
[77b07086-bd3f-4882-8476-8dcafee79b1c]
22+
description = "Encode a series of integers, producing a series of bytes. -> smallest double byte"
23+
24+
[63955a49-2690-4e22-a556-0040648d6b2d]
25+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary double byte"
26+
27+
[29da7031-0067-43d3-83a7-4f14b29ed97a]
28+
description = "Encode a series of integers, producing a series of bytes. -> largest double byte"
29+
30+
[3345d2e3-79a9-4999-869e-d4856e3a8e01]
31+
description = "Encode a series of integers, producing a series of bytes. -> smallest triple byte"
32+
33+
[5df0bc2d-2a57-4300-a653-a75ee4bd0bee]
34+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary triple byte"
35+
36+
[f51d8539-312d-4db1-945c-250222c6aa22]
37+
description = "Encode a series of integers, producing a series of bytes. -> largest triple byte"
38+
39+
[da78228b-544f-47b7-8bfe-d16b35bbe570]
40+
description = "Encode a series of integers, producing a series of bytes. -> smallest quadruple byte"
41+
42+
[11ed3469-a933-46f1-996f-2231e05d7bb6]
43+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quadruple byte"
44+
45+
[d5f3f3c3-e0f1-4e7f-aad0-18a44f223d1c]
46+
description = "Encode a series of integers, producing a series of bytes. -> largest quadruple byte"
47+
48+
[91a18b33-24e7-4bfb-bbca-eca78ff4fc47]
49+
description = "Encode a series of integers, producing a series of bytes. -> smallest quintuple byte"
50+
51+
[5f34ff12-2952-4669-95fe-2d11b693d331]
52+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quintuple byte"
53+
54+
[7489694b-88c3-4078-9864-6fe802411009]
55+
description = "Encode a series of integers, producing a series of bytes. -> maximum 32-bit integer input"
56+
57+
[f9b91821-cada-4a73-9421-3c81d6ff3661]
58+
description = "Encode a series of integers, producing a series of bytes. -> two single-byte values"
59+
60+
[68694449-25d2-4974-ba75-fa7bb36db212]
61+
description = "Encode a series of integers, producing a series of bytes. -> two multi-byte values"
62+
63+
[51a06b5c-de1b-4487-9a50-9db1b8930d85]
64+
description = "Encode a series of integers, producing a series of bytes. -> many multi-byte values"
65+
66+
[baa73993-4514-4915-bac0-f7f585e0e59a]
67+
description = "Decode a series of bytes, producing a series of integers. -> one byte"
68+
69+
[72e94369-29f9-46f2-8c95-6c5b7a595aee]
70+
description = "Decode a series of bytes, producing a series of integers. -> two bytes"
71+
72+
[df5a44c4-56f7-464e-a997-1db5f63ce691]
73+
description = "Decode a series of bytes, producing a series of integers. -> three bytes"
74+
75+
[1bb58684-f2dc-450a-8406-1f3452aa1947]
76+
description = "Decode a series of bytes, producing a series of integers. -> four bytes"
77+
78+
[cecd5233-49f1-4dd1-a41a-9840a40f09cd]
79+
description = "Decode a series of bytes, producing a series of integers. -> maximum 32-bit integer"
80+
81+
[e7d74ba3-8b8e-4bcb-858d-d08302e15695]
82+
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error"
83+
84+
[aa378291-9043-4724-bc53-aca1b4a3fcb6]
85+
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error, even if value is zero"
86+
87+
[a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee]
88+
description = "Decode a series of bytes, producing a series of integers. -> multiple values"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
;;; variable-length-quantity-test.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*-
2+
3+
;;; Commentary:
4+
5+
;;; Code:
6+
7+
8+
(load-file "variable-length-quantity.el")
9+
(declare-function encode "variable-length-quantity.el" (integers))
10+
(declare-function decode "variable-length-quantity.el" (integers))
11+
12+
13+
(ert-deftest zero ()
14+
(should (equal (encode '(#x0)) '(#x0))))
15+
16+
17+
(ert-deftest arbitrary-single-byte ()
18+
(should (equal (encode '(#x40)) '(#x40))))
19+
20+
21+
(ert-deftest largest-single-byte ()
22+
(should (equal (encode '(#x7F)) '(#x7F))))
23+
24+
25+
(ert-deftest smallest-double-byte ()
26+
(should (equal (encode '(#x80)) '(#x81 #x0))))
27+
28+
29+
(ert-deftest arbitrary-double-byte ()
30+
(should (equal (encode '(#x2000)) '(#xC0 #x0))))
31+
32+
33+
(ert-deftest largest-double-byte ()
34+
(should (equal (encode '(#x3FFF)) '(#xFF #x7F))))
35+
36+
37+
(ert-deftest smallest-triple-byte ()
38+
(should (equal (encode '(#x4000)) '(#x81 #x80 #x0))))
39+
40+
41+
(ert-deftest arbitrary-triple-byte ()
42+
(should (equal (encode '(#x100000)) '(#xC0 #x80 #x0))))
43+
44+
45+
(ert-deftest largest-triple-byte ()
46+
(should (equal (encode '(#x1FFFFF)) '(#xFF #xFF #x7F))))
47+
48+
49+
(ert-deftest smallest-quadruple-byte ()
50+
(should (equal (encode '(#x200000)) '(#x81 #x80 #x80 #x0))))
51+
52+
53+
(ert-deftest arbitrary-quadruple-byte ()
54+
(should (equal (encode '(#x8000000)) '(#xC0 #x80 #x80 #x0))))
55+
56+
57+
(ert-deftest largest-quadruple-byte ()
58+
(should (equal (encode '(#xFFFFFFF)) '(#xFF #xFF #xFF #x7F))))
59+
60+
61+
(ert-deftest smallest-quintuple-byte ()
62+
(should (equal (encode '(#x10000000)) '(#x81 #x80 #x80 #x80 #x0))))
63+
64+
65+
(ert-deftest arbitrary-quintuple-byte ()
66+
(should (equal (encode '(#xFF000000)) '(#x8F #xF8 #x80 #x80 #x0))))
67+
68+
69+
(ert-deftest maximum-32-bit-integer-input ()
70+
(should (equal (encode '(#xFFFFFFFF)) '(#x8F #xFF #xFF #xFF #x7F))))
71+
72+
73+
(ert-deftest two-single-byte-values ()
74+
(should (equal (encode '(#x40 #x7F)) '(#x40 #x7F))))
75+
76+
77+
(ert-deftest two-multi-byte-values ()
78+
(should (equal (encode '(#x4000 #x123456)) '(#x81 #x80 #x0 #xC8 #xE8 #x56))))
79+
80+
81+
(ert-deftest many-multi-byte-values ()
82+
(should (equal (encode '(#x2000 #x123456 #xFFFFFFF #x0 #x3FFF #x4000)) '(#xC0 #x0 #xC8 #xE8 #x56 #xFF #xFF #xFF #x7F #x0 #xFF #x7F #x81 #x80 #x0))))
83+
84+
85+
(ert-deftest one-byte ()
86+
(should (equal (decode '(#x7F)) '(#x7F))))
87+
88+
89+
(ert-deftest two-bytes ()
90+
(should (equal (decode '(#xC0 #x0)) '(#x2000))))
91+
92+
93+
(ert-deftest three-bytes ()
94+
(should (equal (decode '(#xFF #xFF #x7F)) '(#x1FFFFF))))
95+
96+
97+
(ert-deftest four-bytes ()
98+
(should (equal (decode '(#x81 #x80 #x80 #x0)) '(#x200000))))
99+
100+
101+
(ert-deftest maximum-32-bit-integer ()
102+
(should (equal (decode '(#x8F #xFF #xFF #xFF #x7F)) '(#xFFFFFFFF))))
103+
104+
105+
(ert-deftest incomplete-sequence-causes-error ()
106+
(should-error (decode '(#xFF))))
107+
108+
109+
(ert-deftest incomplete-sequence-causes-error-even-if-value-is-zero ()
110+
(should-error (decode '(#x80))))
111+
112+
113+
(ert-deftest multiple-values ()
114+
(should (equal (decode '(#xC0 #x0 #xC8 #xE8 #x56 #xFF #xFF #xFF #x7F #x0 #xFF #x7F #x81 #x80 #x0)) '(#x2000 #x123456 #xFFFFFFF #x0 #x3FFF #x4000))))
115+
116+
117+
(provide 'variable-length-quantity-test)
118+
;;; variable-length-quantity-test.el ends here
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
;;; variable-length-quantity.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*-
2+
3+
;;; Commentary:
4+
5+
;;; Code:
6+
7+
8+
(defun encode (integers)
9+
(error "Delete this S-Expression and write your own implementation"))
10+
11+
12+
(defun decode (integers)
13+
(error "Delete this S-Expression and write your own implementation"))
14+
15+
16+
(provide 'variable-length-quantity)
17+
;;; variable-length-quantity.el ends here
18+

0 commit comments

Comments
 (0)