Skip to content

Commit 6764840

Browse files
Add intergalactic transmision (#758)
* Fixes to concept tree * Add Intergalactic Transmission exercise with parity bit implementation * Apply suggestions from code review Co-authored-by: Ryan Hartlage <2488333+ryanplusplus@users.noreply.github.com> --------- Co-authored-by: Ryan Hartlage <2488333+ryanplusplus@users.noreply.github.com>
1 parent cb1ac1c commit 6764840

9 files changed

Lines changed: 394 additions & 0 deletions

File tree

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,14 @@
10861086
"prerequisites": [],
10871087
"difficulty": 4
10881088
},
1089+
{
1090+
"slug": "intergalactic-transmission",
1091+
"name": "Intergalactic Transmission",
1092+
"uuid": "5e7320db-0ec4-4db2-806a-4845f4bf8180",
1093+
"practices": [],
1094+
"prerequisites": [],
1095+
"difficulty": 4
1096+
},
10891097
{
10901098
"slug": "forth",
10911099
"name": "Forth",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Instructions
2+
3+
Your job is to help implement
4+
5+
- the transmitter, which calculates the transmission sequence, and
6+
- the receiver, which decodes it.
7+
8+
A parity bit is simple way of detecting transmission errors.
9+
The transmitters and receivers can only transmit and receive _exactly_ eight bits at a time (including the parity bit).
10+
The parity bit is set so that there is an _even_ number of 1 bits in each transmission, and the parity bit is always the first bit from the right.
11+
So if the receiver receives `11000001`, `01110101` or `01000000` (i.e. a transmission with an odd number of 1 bits), it knows there is an error.
12+
13+
However, messages are rarely this short, and need to be transmitted in a sequence when they are longer.
14+
15+
For example, consider the message `11000000 00000001 11000000 11011110` (or `C0 01 C0 DE` in hex).
16+
17+
Since each transmission contains exactly eight bits, it can only contain seven bits of data and the parity bit.
18+
A parity bit must then be inserted after every seven bits of data:
19+
20+
```text
21+
11000000 00000001 11000000 11011110
22+
↑ ↑ ↑ ↑ (7th bits)
23+
```
24+
25+
The transmission sequence for this message looks like this:
26+
27+
```text
28+
1100000_ 0000000_ 0111000_ 0001101_ 1110
29+
↑ ↑ ↑ ↑ (parity bits)
30+
```
31+
32+
The data in the first transmission in the sequence (`1100000`) has two 1 bits (an even number), so the parity bit is 0.
33+
The first transmission becomes `11000000` (or `C0` in hex).
34+
35+
The data in the next transmission (`0000000`) has zero 1 bits (an even number again), so the parity bit is 0 again.
36+
The second transmission thus becomes `00000000` (or `00` in hex).
37+
38+
The data for the next two transmissions (`0111000` and `0001101`) have three 1 bits.
39+
Their parity bits are set to 1 so that they have an even number of 1 bits in the transmission.
40+
They are transmitted as `01110001` and `00011011` (or `71` and `1B` in hex).
41+
42+
The last transmission (`1110`) has only four bits of data.
43+
Since exactly eight bits are transmitted at a time and the parity bit is the rightmost bit, three 0 bits and then the parity bit are added to make up eight bits.
44+
It now looks like this (where `_` is the parity bit):
45+
46+
```text
47+
1110 000_
48+
↑↑↑ (added 0 bits)
49+
```
50+
51+
There is an odd number of 1 bits again, so the parity bit is 1.
52+
The last transmission in the sequence becomes `11100001` (or `E1` in hex).
53+
54+
The entire transmission sequence for this message is `11000000 00000000 01110001 00011011 11100001` (or `C0 00 71 1B E1` in hex).
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Introduction
2+
3+
Trillions upon trillions of messages zip between Earth and neighboring galaxies every millisecond.
4+
But transmitting over such long distances is tricky.
5+
Pesky solar flares, temporal distortions, stray forces, and even the flap of a space butterfly's wing can cause a random bit to change during transmission.
6+
7+
Now imagine the consequences:
8+
9+
- Crashing the Intergalactic Share Market when "buy low" turns to "sell now".
10+
- Losing contact with the Kepler Whirl system when "save new worm hole" becomes "cave new worm hole".
11+
- Or plunging the universe into existential horror by replacing a cowboy emoji 🤠 with a clown emoji 🤡.
12+
13+
Detecting corrupted messages isn't just important — it's critical.
14+
The receiver _must_ know when something has gone wrong before disaster strikes.
15+
16+
But how?
17+
Scientists and engineers from across the universe have been battling this problem for eons.
18+
Entire cosmic AI superclusters churn through the data.
19+
And then, one day, a legend resurfaces — an ancient, powerful method, whispered in debugging forums, muttered by engineers who've seen too much...
20+
21+
The Parity Bit!
22+
23+
A method so simple, so powerful, that it might just save interstellar communication.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"authors": ["meatball133"],
3+
"files": {
4+
"solution": [
5+
"src/intergalactic_transmission.cr"
6+
],
7+
"test": [
8+
"spec/intergalactic_transmission_spec.cr"
9+
],
10+
"example": [
11+
".meta/src/example.cr"
12+
]
13+
},
14+
"blurb": "Add parity bits to a message for transmission",
15+
"source": "Kah Goh",
16+
"source_url": "https://github.com/exercism/problem-specifications/pull/2543"
17+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
class IntergalacticTransmission
2+
def self.transmit_sequence(message : Array(Int32))
3+
temp = [] of Int32
4+
result = [] of Int32
5+
i = 0
6+
while i < message.size || temp.size > 0
7+
if temp.size == 7 || i >= message.size
8+
temp = expand_number(sum(temp.reverse!)).reverse!
9+
temp.pop
10+
temp << (temp.sum.even? ? 0 : 1)
11+
result << sum(temp)
12+
temp.clear
13+
else
14+
expanded_number = expand_number(message[i])
15+
expanded_number = temp + expanded_number
16+
temp = expanded_number.pop(temp.size + 1)
17+
expanded_number << (expanded_number.sum.even? ? 0 : 1)
18+
result << sum(expanded_number)
19+
i += 1
20+
end
21+
end
22+
23+
result
24+
end
25+
26+
def self.sum(arr : Array(Int32))
27+
sum = 0
28+
arr.each_with_index do |num, i|
29+
sum += num * (2 ** (arr.size - 1 - i))
30+
end
31+
sum
32+
end
33+
34+
def self.expand_number(num : Int32)
35+
current = [] of Int32
36+
(8 - num.bit_length).times do |_|
37+
current << 0
38+
end
39+
current.concat(num == 0 ? [] of Int32 : num.digits(2).reverse!)
40+
current
41+
end
42+
43+
def self.decode_message(transmission : Array(Int32))
44+
result = [] of Int32
45+
temp = [] of Int32
46+
transmission.each do |num|
47+
expanded_number = expand_number(num)
48+
parity_bit = expanded_number.pop
49+
if parity_bit != (expanded_number.sum.even? ? 0 : 1)
50+
raise ArgumentError.new("Parity bit does not match the data")
51+
end
52+
53+
temp.concat(expanded_number)
54+
if temp.size >= 8
55+
result << sum(temp.shift(8))
56+
end
57+
end
58+
59+
result
60+
end
61+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require "spec"
2+
require "../src/*"
3+
4+
describe "<%-= to_capitalized(@json["exercise"].to_s) %>" do
5+
<%- @json["cases"].as_a.each do |cases| %>
6+
<%- cases["cases"].as_a.each do |subcase| %>
7+
<%= status()%> "<%-= subcase["description"] %>" do
8+
<%- if subcase["expected"].as_h? %>
9+
expect_raises(ArgumentError) do
10+
<%-= to_capitalized(@json["exercise"].to_s) %>.<%-= subcase["property"].to_s.underscore %>(<%-= subcase["input"]["message"].to_s == "[]" ? "[] of Int32" : "[#{subcase["input"]["message"].as_a.join(", ")}]" %>)
11+
end
12+
<% else %>
13+
<%-= to_capitalized(@json["exercise"].to_s) %>.<%-= subcase["property"].to_s.underscore %>(<%-= subcase["input"]["message"].to_s == "[]" ? "[] of Int32" : "[#{subcase["input"]["message"].as_a.join(", ")}]" %>).should eq <%-= subcase["expected"].to_s == "[]" ? "[] of Int32" : "[#{subcase["expected"].as_a.join(", ")}]" %>
14+
<% end %>
15+
end
16+
<% end %>
17+
<% end %>
18+
end
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+
[f99d4046-b429-4582-9324-f0bcac7ab51c]
13+
description = "calculate transmit sequences -> empty message"
14+
15+
[ee27ea2d-8999-4f23-9275-8f6879545f86]
16+
description = "calculate transmit sequences -> 0x00 is transmitted as 0x0000"
17+
18+
[97f27f98-8020-402d-be85-f21ba54a6df0]
19+
description = "calculate transmit sequences -> 0x02 is transmitted as 0x0300"
20+
21+
[24712fb9-0336-4e2f-835e-d2350f29c420]
22+
description = "calculate transmit sequences -> 0x06 is transmitted as 0x0600"
23+
24+
[7630b5a9-dba1-4178-b2a0-4a376f7414e0]
25+
description = "calculate transmit sequences -> 0x05 is transmitted as 0x0581"
26+
27+
[ab4fe80b-ef8e-4a99-b4fb-001937af415d]
28+
description = "calculate transmit sequences -> 0x29 is transmitted as 0x2881"
29+
30+
[4e200d84-593b-4449-b7c0-4de1b6a0955e]
31+
description = "calculate transmit sequences -> 0xc001c0de is transmitted as 0xc000711be1"
32+
33+
[fbc537e9-6b21-4f4a-8c2b-9cf9b702a9b7]
34+
description = "calculate transmit sequences -> six byte message"
35+
36+
[d5b75adf-b5fc-4f77-b4ab-77653e30f07c]
37+
description = "calculate transmit sequences -> seven byte message"
38+
39+
[6d8b297b-da1d-435e-bcd7-55fbb1400e73]
40+
description = "calculate transmit sequences -> eight byte message"
41+
42+
[54a0642a-d5aa-490c-be89-8e171a0cab6f]
43+
description = "calculate transmit sequences -> twenty byte message"
44+
45+
[9a8084dd-3336-474c-90cb-8a852524604d]
46+
description = "decode received messages -> empty message"
47+
48+
[879af739-0094-4736-9127-bd441b1ddbbf]
49+
description = "decode received messages -> zero message"
50+
51+
[7a89eeef-96c5-4329-a246-ec181a8e959a]
52+
description = "decode received messages -> 0x0300 is decoded to 0x02"
53+
54+
[3e515af7-8b62-417f-960c-3454bca7f806]
55+
description = "decode received messages -> 0x0581 is decoded to 0x05"
56+
57+
[a1b4a3f7-9f05-4b7a-b86e-d7c6fc3f16a9]
58+
description = "decode received messages -> 0x2881 is decoded to 0x29"
59+
60+
[2e99d617-4c91-4ad5-9217-e4b2447d6e4a]
61+
description = "decode received messages -> first byte has wrong parity"
62+
63+
[507e212d-3dae-42e8-88b4-2223838ff8d2]
64+
description = "decode received messages -> second byte has wrong parity"
65+
66+
[b985692e-6338-46c7-8cea-bc38996d4dfd]
67+
description = "decode received messages -> 0xcf4b00 is decoded to 0xce94"
68+
69+
[7a1f4d48-696d-4679-917c-21b7da3ff3fd]
70+
description = "decode received messages -> 0xe2566500 is decoded to 0xe2ad90"
71+
72+
[467549dc-a558-443b-80c5-ff3d4eb305d4]
73+
description = "decode received messages -> six byte message"
74+
75+
[1f3be5fb-093a-4661-9951-c1c4781c71ea]
76+
description = "decode received messages -> seven byte message"
77+
78+
[6065b8b3-9dcd-45c9-918c-b427cfdb28c1]
79+
description = "decode received messages -> last byte has wrong parity"
80+
81+
[98af97b7-9cca-4c4c-9de3-f70e227a4cb1]
82+
description = "decode received messages -> eight byte message"
83+
84+
[aa7d4785-2bb9-43a4-a38a-203325c464fb]
85+
description = "decode received messages -> twenty byte message"
86+
87+
[4c86e034-b066-42ac-8497-48f9bc1723c1]
88+
description = "decode received messages -> wrong parity on 16th byte"
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
require "spec"
2+
require "../src/*"
3+
4+
describe "IntergalacticTransmission" do
5+
it "empty message" do
6+
IntergalacticTransmission.transmit_sequence([] of Int32).should eq [] of Int32
7+
end
8+
9+
pending "0x00 is transmitted as 0x0000" do
10+
IntergalacticTransmission.transmit_sequence([0x00]).should eq [0x00, 0x00]
11+
end
12+
13+
pending "0x02 is transmitted as 0x0300" do
14+
IntergalacticTransmission.transmit_sequence([0x02]).should eq [0x03, 0x00]
15+
end
16+
17+
pending "0x06 is transmitted as 0x0600" do
18+
IntergalacticTransmission.transmit_sequence([0x06]).should eq [0x06, 0x00]
19+
end
20+
21+
pending "0x05 is transmitted as 0x0581" do
22+
IntergalacticTransmission.transmit_sequence([0x05]).should eq [0x05, 0x81]
23+
end
24+
25+
pending "0x29 is transmitted as 0x2881" do
26+
IntergalacticTransmission.transmit_sequence([0x29]).should eq [0x28, 0x81]
27+
end
28+
29+
pending "0xc001c0de is transmitted as 0xc000711be1" do
30+
IntergalacticTransmission.transmit_sequence([0xc0, 0x01, 0xc0, 0xde]).should eq [0xc0, 0x00, 0x71, 0x1b, 0xe1]
31+
end
32+
33+
pending "six byte message" do
34+
IntergalacticTransmission.transmit_sequence([0x47, 0x72, 0x65, 0x61, 0x74, 0x21]).should eq [0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84]
35+
end
36+
37+
pending "seven byte message" do
38+
IntergalacticTransmission.transmit_sequence([0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21]).should eq [0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42]
39+
end
40+
41+
pending "eight byte message" do
42+
IntergalacticTransmission.transmit_sequence([0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21]).should eq [0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81]
43+
end
44+
45+
pending "twenty byte message" do
46+
IntergalacticTransmission.transmit_sequence([0x45, 0x78, 0x65, 0x72, 0x63, 0x69, 0x73, 0x6d, 0x20, 0x69, 0x73, 0x20, 0x61, 0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x21]).should eq [0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, 0x1b, 0x2e, 0x33, 0x03, 0x84, 0xee, 0x65, 0xb8, 0xdb, 0xed, 0xd7, 0x28, 0x84]
47+
end
48+
49+
pending "empty message" do
50+
IntergalacticTransmission.decode_message([] of Int32).should eq [] of Int32
51+
end
52+
53+
pending "zero message" do
54+
IntergalacticTransmission.decode_message([0x00, 0x00]).should eq [0x00]
55+
end
56+
57+
pending "0x0300 is decoded to 0x02" do
58+
IntergalacticTransmission.decode_message([0x03, 0x00]).should eq [0x02]
59+
end
60+
61+
pending "0x0581 is decoded to 0x05" do
62+
IntergalacticTransmission.decode_message([0x05, 0x81]).should eq [0x05]
63+
end
64+
65+
pending "0x2881 is decoded to 0x29" do
66+
IntergalacticTransmission.decode_message([0x28, 0x81]).should eq [0x29]
67+
end
68+
69+
pending "first byte has wrong parity" do
70+
expect_raises(ArgumentError) do
71+
IntergalacticTransmission.decode_message([0x07, 0x00])
72+
end
73+
end
74+
75+
pending "second byte has wrong parity" do
76+
expect_raises(ArgumentError) do
77+
IntergalacticTransmission.decode_message([0x03, 0x68])
78+
end
79+
end
80+
81+
pending "0xcf4b00 is decoded to 0xce94" do
82+
IntergalacticTransmission.decode_message([0xcf, 0x4b, 0x00]).should eq [0xce, 0x94]
83+
end
84+
85+
pending "0xe2566500 is decoded to 0xe2ad90" do
86+
IntergalacticTransmission.decode_message([0xe2, 0x56, 0x65, 0x00]).should eq [0xe2, 0xad, 0x90]
87+
end
88+
89+
pending "six byte message" do
90+
IntergalacticTransmission.decode_message([0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84]).should eq [0x47, 0x72, 0x65, 0x61, 0x74, 0x21]
91+
end
92+
93+
pending "seven byte message" do
94+
IntergalacticTransmission.decode_message([0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42]).should eq [0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21]
95+
end
96+
97+
pending "last byte has wrong parity" do
98+
expect_raises(ArgumentError) do
99+
IntergalacticTransmission.decode_message([0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x43])
100+
end
101+
end
102+
103+
pending "eight byte message" do
104+
IntergalacticTransmission.decode_message([0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81]).should eq [0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21]
105+
end
106+
107+
pending "twenty byte message" do
108+
IntergalacticTransmission.decode_message([0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, 0x1b, 0x2e, 0x33, 0x03, 0x84, 0xee, 0x65, 0xb8, 0xdb, 0xed, 0xd7, 0x28, 0x84]).should eq [0x45, 0x78, 0x65, 0x72, 0x63, 0x69, 0x73, 0x6d, 0x20, 0x69, 0x73, 0x20, 0x61, 0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x21]
109+
end
110+
111+
pending "wrong parity on 16th byte" do
112+
expect_raises(ArgumentError) do
113+
IntergalacticTransmission.decode_message([0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, 0x1b, 0x2e, 0x33, 0x03, 0x84, 0xef, 0x65, 0xb8, 0xdb, 0xed, 0xd7, 0x28, 0x84])
114+
end
115+
end
116+
end

0 commit comments

Comments
 (0)