Skip to content

Commit d980303

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 2fc1d8f + 23edc86 commit d980303

File tree

5 files changed

+360
-8
lines changed

5 files changed

+360
-8
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Struct to hold an x, y position. A tuple would also be fine.
2+
struct Pos
3+
x::Float64
4+
y::Float64
5+
end
6+
7+
# This returns a measure of how far "left" the vector is with a cross product
8+
function cross(point1::Pos, point2::Pos, point3::Pos)
9+
vec1 = Pos(point2.x - point1.x, point2.y - point1.y)
10+
vec2 = Pos(point3.x - point2.x, point3.y - point2.y)
11+
ret_angle = vec1.x*vec2.y - vec1.y*vec2.x
12+
return ret_angle*ret_angle
13+
end
14+
15+
function jarvis_march(points::Vector{Pos})
16+
hull = Vector{Pos}()
17+
18+
# sorting array based on leftmost point
19+
sort!(points, by = item -> item.x)
20+
push!(hull, points[1])
21+
22+
i = 1
23+
curr_point = points[2]
24+
25+
# Find angle between points
26+
curr_product = cross(Pos(0,0), hull[1], curr_point)
27+
28+
# We will hold a temp variable with the highest cross product as we iterate
29+
# through all the points and move our hull vector forward.
30+
while (curr_point != hull[1])
31+
for point in points
32+
product = 0.0
33+
34+
# Special case for the first element when there is no hull[i-1]
35+
if (i == 1)
36+
if (hull[i] != point)
37+
product = cross(Pos(0,0), hull[i], point)
38+
end
39+
else
40+
if (hull[i] != point && hull[i-1] != point)
41+
product = cross(hull[i-1], hull[i], point)
42+
end
43+
end
44+
if (product > curr_product)
45+
curr_point = point
46+
curr_product = product
47+
end
48+
end
49+
50+
# Pushing to hull, moving simulation forward and resetting the product
51+
push!(hull, curr_point)
52+
curr_product = 0
53+
i += 1
54+
end
55+
56+
return hull
57+
end
58+
59+
function main()
60+
61+
# These points are chosen such that there is a clearly defined hull with
62+
# several interior points. As a note, these will be generated either
63+
# randomly or via some mesh in practice.
64+
points = [Pos(2,1.5), Pos(1, 1), Pos(2, 4), Pos(3, 1), Pos(2,2), Pos(2,0.5)]
65+
hull = jarvis_march(points)
66+
println(hull)
67+
end
68+
69+
main()

chapters/convolutions/convolutions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ In code, this looks something like:
6464
{% sample lang="hs" %}
6565
[import:1-5, lang:"haskell"](code/haskell/convolution.hs)
6666
{% sample lang="c"%}
67-
[import:46-58, lang:"c_cpp"](code/c/convolutions.c)
67+
[import:44-57, lang:"c_cpp"](code/c/convolutions.c)
6868
{% sample lang="cpp"%}
6969
[import:68-88, lang:"c_cpp"](code/c++/convolutions.cpp)
7070
{% endmethod %}
@@ -113,7 +113,7 @@ The FFT-based convolution in Haskell is complicated, so here is some simple juli
113113
[import:19-22, lang:"julia"](code/julia/conv.jl)
114114
Where the `.*` operator is an element-wise multiplication.
115115
{% sample lang="c"%}
116-
[import:60-69, lang:"c_cpp"](code/c/convolutions.c)
116+
[import:59-69, lang:"c_cpp"](code/c/convolutions.c)
117117
{% sample lang="cpp"%}
118118
[import:90-105, lang:"c_cpp"](code/c++/convolutions.cpp)
119119
{% endmethod %}
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
#include <algorithm>
2+
#include <array>
3+
#include <bitset>
4+
#include <cassert>
5+
#include <cctype>
6+
#include <cstddef>
7+
#include <limits>
8+
#include <memory>
9+
#include <string>
10+
#include <utility>
11+
#include <vector>
12+
13+
#include <iostream>
14+
15+
using std::begin;
16+
using std::end;
17+
18+
namespace huffman {
19+
20+
[[noreturn]] inline void unreachable() {
21+
std::cerr << "this should never happen\n";
22+
std::abort();
23+
}
24+
25+
// --- interface ---
26+
class codebook {
27+
struct node {
28+
int frequency;
29+
30+
node(int freq) : frequency(freq) {}
31+
virtual ~node() = 0;
32+
};
33+
34+
// never null
35+
using node_ptr = std::unique_ptr<node const>;
36+
using bitstring = std::vector<bool>;
37+
38+
// this is a flatmap between char and a bitstring
39+
// there should only ever be one character with a given
40+
// value at any time.
41+
using encoder_t = std::vector<std::pair<char, bitstring>>;
42+
43+
struct leaf final : node {
44+
char key;
45+
46+
leaf(int freq, char key) : node(freq), key(key) {}
47+
};
48+
49+
struct branch final : node {
50+
node_ptr lhs;
51+
node_ptr rhs;
52+
53+
branch(node_ptr lhs, node_ptr rhs)
54+
: node(lhs->frequency + rhs->frequency), lhs(std::move(lhs)),
55+
rhs(std::move(rhs)) {}
56+
};
57+
58+
// this allows us to share [codebook]s among encoded strings
59+
struct data {
60+
node_ptr decoder;
61+
encoder_t encoder;
62+
};
63+
std::shared_ptr<data const> underlying_;
64+
65+
public:
66+
template <class Iter>
67+
codebook(Iter const first, Iter const last);
68+
69+
template <class Iter>
70+
std::vector<bool> encode(Iter first, Iter last) const;
71+
72+
template <class Iter>
73+
std::string decode(Iter first, Iter last) const;
74+
};
75+
76+
struct encoded_string {
77+
codebook codes;
78+
std::vector<bool> string;
79+
80+
explicit encoded_string(std::string const& s)
81+
: codes(begin(s), end(s)), string(codes.encode(begin(s), end(s))) {}
82+
83+
encoded_string(codebook codes, std::string const& s)
84+
: codes(codes), string(codes.encode(begin(s), end(s))) {}
85+
86+
std::string decoded() const {
87+
return codes.decode(begin(string), end(string));
88+
}
89+
};
90+
91+
// --- implementation ---
92+
inline codebook::node::~node() {}
93+
94+
inline std::vector<bool> with_new_bit(std::vector<bool> bits, bool b) {
95+
bits.push_back(b);
96+
return bits;
97+
}
98+
99+
template <class Iter>
100+
codebook::codebook(Iter const first, Iter const last) {
101+
struct helper {
102+
static node_ptr make_decoder(Iter const first, Iter const last) {
103+
// in this part of the function, we build up a frequency list
104+
// sorted by frequency, descending
105+
auto freq = std::vector<leaf>();
106+
107+
std::for_each(first, last, [&freq](char c) {
108+
auto const it = std::find_if(
109+
begin(freq), end(freq), [c](auto const& p) { return p.key == c; });
110+
if (it != end(freq)) {
111+
// it's already in the list
112+
it->frequency += 1;
113+
} else {
114+
// it's not already in the list
115+
freq.emplace_back(1, c);
116+
}
117+
});
118+
119+
if (freq.empty()) {
120+
throw std::invalid_argument("attempted to codebook an empty range");
121+
}
122+
123+
std::sort(begin(freq), end(freq), [](auto const& lhs, auto const& rhs) {
124+
return lhs.frequency > rhs.frequency;
125+
});
126+
127+
auto ret = std::vector<std::unique_ptr<node>>();
128+
std::transform(
129+
begin(freq), end(freq), std::back_inserter(ret), [](auto const l) {
130+
return std::make_unique<leaf>(l);
131+
});
132+
133+
while (ret.size() > 1) {
134+
auto rhs = std::move(ret.back());
135+
ret.pop_back();
136+
auto lhs = std::move(ret.back());
137+
ret.pop_back();
138+
139+
auto new_node =
140+
std::make_unique<branch>(std::move(lhs), std::move(rhs));
141+
auto const new_freq = new_node->frequency;
142+
143+
// look for the first element with a smaller frequency
144+
auto const it =
145+
std::find_if(begin(ret), end(ret), [new_freq](auto const& n) {
146+
return n->frequency < new_freq;
147+
});
148+
// and insert the new_node before that element
149+
ret.insert(it, std::move(new_node));
150+
// in this way, we keep the list sorted by frequency
151+
}
152+
153+
return std::move(ret.back());
154+
}
155+
156+
static void
157+
encoder_rec(node const* cur, std::vector<bool> bits, encoder_t& out) {
158+
if (auto l = dynamic_cast<leaf const*>(cur)) {
159+
out.push_back(std::make_pair(l->key, std::move(bits)));
160+
} else if (auto b = dynamic_cast<branch const*>(cur)) {
161+
encoder_rec(b->lhs.get(), with_new_bit(bits, 0), out);
162+
encoder_rec(b->rhs.get(), with_new_bit(std::move(bits), 1), out);
163+
} else {
164+
unreachable();
165+
}
166+
}
167+
168+
static encoder_t make_encoder(node const& decoder) {
169+
auto ret = encoder_t();
170+
171+
encoder_rec(&decoder, std::vector<bool>(), ret);
172+
173+
return ret;
174+
}
175+
};
176+
177+
auto decoder = helper::make_decoder(first, last);
178+
auto encoder = helper::make_encoder(*decoder);
179+
underlying_ = std::make_shared<data const>(
180+
data{std::move(decoder), std::move(encoder)});
181+
}
182+
183+
template <class Iter>
184+
std::vector<bool> codebook::encode(Iter const first, Iter const last) const {
185+
std::vector<bool> ret;
186+
187+
auto& encoder = underlying_->encoder;
188+
std::for_each(first, last, [&ret, &encoder](char c) {
189+
auto const it =
190+
std::find_if(begin(encoder), end(encoder), [c](auto const& p) {
191+
return p.first == c;
192+
});
193+
if (it != end(encoder)) {
194+
auto const& code = it->second;
195+
std::copy(begin(code), end(code), std::back_inserter(ret));
196+
} else {
197+
throw std::invalid_argument(
198+
"The range has a character which was not in the huffman set");
199+
}
200+
});
201+
202+
return ret;
203+
}
204+
205+
template <class Iter>
206+
std::string codebook::decode(Iter const first, Iter const last) const {
207+
std::string ret;
208+
209+
node const* const top = underlying_->decoder.get();
210+
211+
// returns a pair:
212+
// the second member is the decoded character
213+
// the first member is the place we've gotten to in the range
214+
// i.e., if [0] is an 'a', and we have
215+
// [it, last) = { 0, 1, 1, 0 }
216+
// we return (it', 'a') such that
217+
// [it', last) = { 1, 1, 0 }
218+
auto decode_single =
219+
[top](Iter it, Iter const last) -> std::pair<Iter, char> {
220+
node const* current_node = top;
221+
222+
for (; it != last; ++it) {
223+
if (auto l = dynamic_cast<leaf const*>(current_node)) {
224+
return std::make_pair(it, l->key);
225+
} else if (auto b = dynamic_cast<branch const*>(current_node)) {
226+
if (*it) {
227+
current_node = b->rhs.get();
228+
} else {
229+
current_node = b->lhs.get();
230+
}
231+
} else {
232+
unreachable();
233+
}
234+
}
235+
236+
if (auto l = dynamic_cast<leaf const*>(current_node)) {
237+
return std::make_pair(last, l->key);
238+
} else {
239+
throw std::invalid_argument(
240+
"The range was not encoded with this huffman set");
241+
}
242+
};
243+
244+
for (auto it = first; it != last;) {
245+
auto p = decode_single(it, last);
246+
it = p.first;
247+
ret.push_back(p.second);
248+
}
249+
250+
return ret;
251+
}
252+
253+
} // namespace huffman
254+
255+
int main() {
256+
std::string to_be_encoded =
257+
R"(She should have died hereafter.
258+
There would have been a time for such a word.
259+
Tomorrow, and tomorrow, and tomorrow,
260+
Creeps in this petty pace from day to day
261+
To the last syllable of recorded time,
262+
And all our yesterdays have lighted fools
263+
The way to dusty death. Out, out, brief candle!
264+
Life's but a walking shadow, a poor player
265+
That struts and frets his hour upon the stage
266+
And then is heard no more. It is a tale
267+
Told by an idiot, full of sound and fury,
268+
Signifying nothing.)";
269+
270+
auto encoded = huffman::encoded_string(to_be_encoded);
271+
272+
std::cout << "Encoded, the string looks like: ";
273+
for (bool b : encoded.string) {
274+
std::cout << b;
275+
}
276+
std::cout << "\nand decoded, the string looks like: " << encoded.decoded();
277+
std::cout << "\n\nAs opposed to the original, which is "
278+
<< to_be_encoded.size() * 8 << " bits, the encoded has size "
279+
<< encoded.string.size() << ".\n";
280+
}

chapters/data_compression/huffman/huffman.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,7 @@ Whether you use a stack or straight-up recursion also depends on the language, b
8686
{% sample lang="hs" %}
8787
### Haskell
8888
[import, lang:"haskell"](code/haskell/huffman.hs)
89+
{% sample lang="cpp" %}
90+
### C++
91+
[import, lang:"cpp"](code/c++/huffman.cpp)
8992
{% endmethod %}

0 commit comments

Comments
 (0)