PDF String Algorithms in C: Efficient Text Representation and Search 1st Edition Thomas Mailund download
PDF String Algorithms in C: Efficient Text Representation and Search 1st Edition Thomas Mailund download
com
https://textbookfull.com/product/string-algorithms-in-c-
efficient-text-representation-and-search-1st-edition-thomas-
mailund/
OR CLICK BUTTON
DOWNLOAD NOW
https://textbookfull.com/product/the-joys-of-hashing-hash-table-
programming-with-c-1st-edition-thomas-mailund/
textboxfull.com
https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund/
textboxfull.com
https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund-2/
textboxfull.com
C Data Structures and Algorithms Explore the possibilities
of C for developing a variety of efficient applications
Marcin Jamro
https://textbookfull.com/product/c-data-structures-and-algorithms-
explore-the-possibilities-of-c-for-developing-a-variety-of-efficient-
applications-marcin-jamro/
textboxfull.com
https://textbookfull.com/product/functional-data-structures-in-r-
advanced-statistical-programming-in-r-thomas-mailund/
textboxfull.com
https://textbookfull.com/product/introducing-markdown-and-pandoc-
using-markup-language-and-document-converter-1st-edition-thomas-
mailund/
textboxfull.com
String
Algorithms in C
Ef f icient Text Representation and Search
—
Thomas Mailund
WOW! eBook
www.wowebook.org
String Algorithms in C
Efficient Text Representation
and Search
Thomas Mailund
WOW! eBook
www.wowebook.org
String Algorithms in C: Efficient Text Representation and Search
Thomas Mailund
Aarhus N, Denmark
WOW! eBook
www.wowebook.org
Table of Contents
About the Author���������������������������������������������������������������������������������������������������� vii
Chapter 1: Introduction�������������������������������������������������������������������������������������������� 1
Notation and conventions������������������������������������������������������������������������������������������������������������� 1
Graphical notation������������������������������������������������������������������������������������������������������������������������� 2
Code conventions�������������������������������������������������������������������������������������������������������������������������� 3
Reporting a sequence of results��������������������������������������������������������������������������������������������������� 5
iii
WOW! eBook
www.wowebook.org
Table of Contents
iv
WOW! eBook
www.wowebook.org
Table of Contents
Index��������������������������������������������������������������������������������������������������������������������� 287
WOW! eBook
www.wowebook.org
About the Author
Thomas Mailund is an associate professor in bioinformatics at Aarhus University,
Denmark. He has a background in math and computer science. For the past decade,
his main focus has been on genetics and evolutionary studies, particularly comparative
genomics, speciation, and gene flow between emerging species. He has published R
Data Science Quick Reference, The Joys of Hashing, Domain-Specific Languages in R,
Beginning Data Science in R, Functional Programming in R, and Metaprogramming in R,
all from Apress, as well as other books.
vii
WOW! eBook
www.wowebook.org
About the Technical Reviewer
Jason Whitehorn is an experienced entrepreneur and
software developer and has helped many companies
automate and enhance their business solutions through data
synchronization, SaaS architecture, and machine learning.
Jason obtained his Bachelor of Science in Computer Science
from Arkansas State University, but he traces his passion
for development back many years before then, having first
taught himself to program BASIC on his family’s computer
while still in middle school.
When he’s not mentoring and helping his team at work,
writing, or pursuing one of his many side projects, Jason enjoys spending time with his
wife and four children and living in the Tulsa, Oklahoma region. More information about
Jason can be found on his website: https://jason.whitehorn.us.
ix
WOW! eBook
www.wowebook.org
CHAPTER 1
Introduction
Algorithms operating on strings are fundamental to many computer programs, and in
particular searching for one string in another is the core of many algorithms. An example
is searching for a word in a text document, where we want to know everywhere it occurs.
This search can be exact, meaning that we are looking for the positions where the word
occurs verbatim, or approximative, where we allow for some spelling mistakes.
This book will teach you fundamental algorithms and data structures for exact and
approximative search. The goal of the book is not to cover the theory behind the material
in great detail. However, we will see theoretical considerations where relevant. The
purpose of the book is to give you examples of how the algorithms can be implemented.
For every algorithm and data structure in the book, I will present working C code and
nowhere will I use pseudocode. When I argue for the correctness and running time of
algorithms, I do so intentionally informal. I aim at giving you an idea about why the
algorithms solve a specific problem in a given time, but I will not mathematically prove so.
You can copy all the algorithms and data structures in this book from the pages,
but they are also available in a library on GitHub: https://github.com/mailund/
stralg. You can download and link against the library or copy snippets of code into
your own projects. On GitHub you can also find all the programs I have used for time
measurement experiments so you can compare the algorithm’s performance on your
own machine and in your own runtime environment.
N
otation and conventions
Unless otherwise stated, we use x, y, and p to refer to strings and i, j, k, l, and h to denote
indices. We use 𝜖 to denote the empty string. We use a, b, and c for single characters. As
in C, we do not distinguish between strings and pointers to a sequence of characters.
Since the book is about algorithms in C, the notation we use matches that which is
used for strings, pointers, and arrays in C. Arrays and strings are indexed from zero,
1
© Thomas Mailund 2020
T. Mailund, String Algorithms in C, https://doi.org/10.1007/978-1-4842-5920-7_1
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
that is, A[0] is the first value in array A (and x[0] is the first character in string x). The ith
character in a string is at index i − 1.
When we refer to a substring, we define it using two indices, i and j, i ≤ j, and we
write x[i, j] for the substring. The first index is included and the second is not, that is,
x[i, j] = x[i]x[i + 1] · · · x[ j − 1]. If a string has length n, then the substring x[0, n] is the full
string. If we have a character a and a string x, then ax denotes the string that has a as its
first character and is then followed by the string x. We use ak to denote a sequence of as
of length k. The string a3 x has a as its first three characters and is then followed by x.
A substring that starts at index 0, x[0, i], is a prefix of the string, and it is a proper prefix
if it is neither the empty string x[0, 0] = 𝜖 nor the full string x[0, n]. A substring that ends
in n, x[i, n], is a suffix, and it is a proper suffix if it is neither the empty string nor the full
string. We will sometimes use x[i, ] for this suffix.
We use $ to denote a sentinel in a string, that is, it is a character that is not found in
the rest of the string. It is typically placed at the end of the string. The zero-terminated
C strings have the zero byte as their termination sentinel, and unless otherwise stated,
$ refers to that. All C strings x have a zero sentinel at index n if the string has length n,
x = x[0]x[1] · · · x[n − 1]0. For some algorithms, the sentinel is essential; in others, it is not.
We will leave it out of the notation when a sentinel isn’t needed for an algorithm, but
naturally include the sentinel when it is necessary.
G
raphical notation
Most data structures and algorithmic ideas are simpler to grasp if we use drawings to
capture the structure of strings rather than textual notation. Because of this, I have chosen to
provide more figures in this book than you will typically see in a book on algorithms. I hope
you will appreciate it. If there is anything you find unclear about an algorithm, I suggest you
try to draw key strings yourself and work out the properties you have problems with.
In figures, we represent strings as rectangles. We show indices into a string as arrows
pointing to the index in the string; see Figure 1-1. In this notation, we do not distinguish
between pointers and indices. If a variable is an index j and it points into x, then what
it points to is x[ j], naturally. If the variable is a pointer, y, then what it points to is ∗y.
Whether we are working with pointers or indices should be clear from the context. It will
undoubtedly be clear from the C implementations. We represent substrings by boxes of
a different color inside the original string-rectangle. If we specify the indices defining the
substring, we include their start and stop index (where the stop index points one after
the end of the substring).
2
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
Figure 1-2. Graphical notation for comparing indices in two different strings
C
ode conventions
There is a trade-off between long variables and type names and then the line within a
book. In many cases, I have had to use an indentation that you might not be used to. In
function prototypes and function definitions, I will generally write with one variable per
line, indented under the function return type and name, for example:
void compute_z_array(
const unsigned char *x,
uint32_t n,
uint32_t *Z
);
3
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
void compute_reverse_z_array(
const unsigned char *x,
uint32_t m,
uint32_t *Z
);
I make an exception for functions that take no arguments, that is, have void as their
argument type.
There are many places where an algorithm needs to use characters to look up in
arrays. If you use the conventional C string type, char *, then the character can be either
signed or unsigned, depending on your compiler, and you have to cast the type to avoid
warnings. A couple of places we also have to make assumptions about the alphabet
size. Because of this, I use arrays of uint8_t with a zero termination sentinel as strings.
On practically all platforms, char is 8 bits so this type is, for all intents and purposes, C
strings. We are just guaranteed that we can use it unsigned and that the alphabet size
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
void bmh_search(
const uint8_t *x,
const uint8_t *p
) {
uint32_t n = strlen((char *)x);
uint32_t m = strlen((char *)p);
// Preprocessing
int jump_table[256];
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
// Searching
for (uint32_t j = 0;
j < n - m + 1;
j += jump_table[x[j + m - 1]]) {
int i = m - 1;
while (i > 0 && p[i] == x[j + i])
--i;
if (i == 0 && p[0] == x[j]) {
REPORT(j);
}
}
}
If a global report function is all you need in your program, then this is an excellent
solution. Often, however, we need different reporting functions for separate calls to the
search function. Or we need the report function to collect data for further processing
(and preferably not use global variables). We need some handle to choose different
report functions and to provide them with data.
One approach is using callbacks: Provide a report function and data argument to the
search function and call the report function with the data when we find an occurrence.
In the following implementation, I am assuming we have defined the function type for
reporting, report_function, and the type for data we can add to it, report_function_
data, somewhere outside of the search function.
void bmh_search_callback(
const uint8_t *x,
const uint8_t *p,
report_function report,
report_function_data data
) {
uint32_t n = strlen((char *)x);
uint32_t = strlen((char *)p);
// Preprocessing
uint32_t jump_table[256];
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
// Searching
for (uint32_t j = 0;
j < n - m + 1;
j += jump_table[x[j + m - 1]]) {
int i = m - 1;
while (i > 0 && p[i] == x[j + i])
--i;
if (i == 0 && p[0] == x[j]) {
report(j, data);
}
}
}
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
The iterator structure contains the loop information. That means it must save the
preprocessing data from when we create it and information about how to resume the
loop after each time it is suspended. To report occurrences, it takes a “match” structure
through which it can inform the caller about where matches occur. The iterator is
initialized with data that determines what it should loop over. The loop is handled
using a “next” function that returns true if there is another match (and if it does it will
have filled out match). If there are no more matches, and the loop terminates, then it
returns false. The iterator might contain allocated resources, so there should always be a
function for freeing those.
In an iterator for the BMH, we would keep the string, pattern, and table we build in
the preprocessing.
struct bmh_match_iter {
const uint8_t *x; uint32_t n;
const uint8_t *p; uint32_t m;
int jump_table[256];
uint32_t j;
};
struct match {
uint32_t pos;
};
void init_bmh_match_iter(
struct bmh_match_iter *iter,
const uint8_t *x, uint32_t n,
const uint8_t *p, uint32_t m
) {
// Preprocessing
iter->j = 0;
iter->x = x; iter->n = n;
iter->p = p; iter->m = m;
8
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
bool next_bmh_match(
struct bmh_match_iter *iter,
struct match *match
) {
const uint8_t *x = iter->x;
const uint8_t *p = iter->p;
uint32_t n = iter->n;
uint32_t m = iter->m;
int *jump_table = iter->jump_table;
// Searching
for (uint32_t j = iter->j;
j < n - m + 1;
j += jump_table[x[j + m - 1]]) {
int i = m - 1;
while (i > 0 && p[i] == x[j + i]) {
i--;
}
if (i == 0 && p[0] == x[j]) {
match->pos = j;
iter->j = j +
jump_table[x[j + m - 1]];
return true;
}
}
return false;
}
9
WOW! eBook
www.wowebook.org
Chapter 1 Introduction
We set up the loop with information from the iterator and search from there. If we
find an occurrence, we store the new loop information in the iterator and the match
information in the match structure and return true. If we reach the end of the loop, we
report false.
We have not allocated any resources when we initialized the iterator, so we do not
need to free anything.
void dealloc_bmh_match_iter(
struct bmh_match_iter *iter
) {
// Nothing to do here
}
Since the deallocation function doesn’t do anything, we could leave it out. Still,
consistency in the use of iterators helps avoid problems. Plus, should we at some point
add resources to the iterator, then it is easier to update one function than change all the
places in the code that should now call a deallocation function.
Iterators complicate the implementation of algorithms, especially if they are
recursive and the iterator needs to keep track of a stack. Still, they greatly simplify the
user interface to your algorithms, which makes it worthwhile to spend a little extra time
implementing them. In this book, I will use iterators throughout.
10
WOW! eBook
www.wowebook.org
CHAPTER 2
Classical algorithms
for exact search
We kick the book off by looking at classical algorithms for exact search, that is, finding
positions in a string where a pattern string matches precisely. This problem is so
fundamental that it received much attention in the very early days of computing, and by
now, there are tens if not hundreds of approaches. In this chapter, we see a few classics.
Recall that we use iterators whenever we have an algorithm that loops over results
that should be reported. All iterators must be initialized, and the resources they hold must
be deallocated when we no longer need the iterator. When we loop, we have a function
that returns true when there is something to report and false when the loop is done. The
values the iterator reports are put in a structure that we pass along to the function that
iterates to the next value to report. For the algorithms in this chapter, we initialize the
iterators with the string in which we search, the pattern we search for, and the lengths of
the two strings. Iterating over all occurrences of the pattern follows this structure:
When we report an occurrence, we get the position of the match, so the structure the
iterator use for reporting is this:
struct match {
uint32_t pos;
};
11
© Thomas Mailund 2020
T. Mailund, String Algorithms in C, https://doi.org/10.1007/978-1-4842-5920-7_2
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
N
aïve algorithm
The simplest way imaginable for exact search is to iteratively move through the string
x, with an index j that conceptually runs the pattern p along x, and at each index start
matching the pattern against the string using another index, i (see Figure 2-1). The
algorithm has two loops, one that iterates j through x and one that iterates i through
p, matching x[i + j] against p[i] along the way. We run the inner loop until we see a
mismatch or until we reach the end of the pattern. In the former case, we move p one
step forward and try matching again. In the second case, we report an occurrence at
position j and then increment the index so we can start matching at the next position. We
stop the outer loop when index j is greater than n − m. If it is, there isn’t room for a match
that doesn’t run past the end of x.
We terminate the comparison of x[i + j] and p[i] when we see a mismatch, so in the best
case, where the first character in p never matches a character in x, the algorithm runs in
time O(n) where n is the length of x. In the worst case, we match all the way to the end of p
at each position, and in that case, the running time is O(nm) where m is the length of p.
To implement the algorithm using an iterator, the iterator needs to remember
the string to search in and the pattern to search for—so we do not need to pass these
along each time we increment the iterator with potentials for errors if we use the wrong
strings—and we keep track of how far into the string we have searched.
struct naive_match_iter {
const uint8_t *x; uint32_t n;
const uint8_t *p; uint32_t m;
uint32_t current_index;
};
12
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
When we initialize the iterator, we remember the two strings and set the current
index to zero—before we start iterating we are at the beginning of the string.
void init_naive_match_iter(
struct naive_match_iter *iter,
const uint8_t *x, uint32_t n,
const uint8_t *p, uint32_t m
) {
iter->x = x; iter->n = n;
iter->p = p; iter->m = m;
iter->current_index = 0;
iter->current_index = 0;
}
When we increment the iterator, we follow the algorithm as described earlier except
that we start the outer loop at the index saved in the iterator. We search from this index in
an outer loop, and at each new index, we try to match the pattern with an inner loop. We
break the inner loop if we see a mismatching character, and if the inner loop reaches the
end, we have a match and report it. Before we return, we set the iterator index and store
the matching position in the match structure.
bool next_naive_match(
struct naive_match_iter *iter,
struct match *match
) {
uint32_t n = iter->n, m = iter->m;
const uint8_t *x = iter->x;
const uint8_t *p = iter->p;
13
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
if (i == m) {
iter->current_index = j + 1;
match->pos = j;
return true;
}
}
return false;
}
The code
makes sure that it is possible to match the pattern at all and that the pattern isn’t
empty. This is something we could also test when we initialize the iterator. However, we
do not have a way of reporting that we do not have a possible match there, so we put the
test in the “next” function.
We do not allocate any resources when we initialize the iterator, so we do not need to
do anything when deallocating it either. We still need the deallocator function, however,
so we always use the same design pattern when we use iterators. To make sure that if we,
at some point in the future, need to free something that we put in an iterator, then all
users of the iterator (should) have added code for this.
void dealloc_naive_match_iter(
struct naive_match_iter *iter
) {
// Nothing to do here...
}
14
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
We can make the following observation about borders and border arrays: The longest
border of x[0, i] is either the empty string or an extension of a border of x[0, i − 1]. If the
letter at x[i] is a, the border of x[0, i] is some string y followed by a. The y string must be
both at the beginning and end of x[0, i − 1] (see Figure 2-3), so it is a border of x[0, i − 1].
The longest border for x[0, i] is the longest border of x[0, i − 1] that is followed by a (which
may be the empty border if the string x begins with a) or the empty border if there is no
border we can extend with a.
Another observation is that if you have two borders to a string, then the shorter of the
two is a border of the longer; see Figure 2-4.
15
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
16
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
Figure 2-5. Searching for the longest border we can extend with letter a
An implementation of the border array construction algorithm can look like this:
ba[0] = 0;
for (uint32_t i = 1; i < m; ++i) {
uint32_t b = ba[i - 1];
while (b > 0 && x[i] != x[b])
b = ba[b - 1];
ba[i] = (x[i] == x[b]) ? b + 1 : 0;
}
17
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
An iterator that searches a string with this algorithm must contain the border
array of p, the index into x we have reached, and the b variable from the border array
construction algorithm.
struct border_match_iter {
const uint8_t *x; uint32_t n;
const uint8_t *p; uint32_t m;
uint32_t *border_array;
uint32_t i; uint32_t b;
};
18
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
When we initialize the iterator, we set its index to zero. That, after all, is where we
start searching in x. We also set the iterator’s b variable to zero. We imagine that we start
the search after a sentinel, so the longest border at the start index for x has length zero.
We then allocate and compute the border array.
void init_border_match_iter(
struct border_match_iter *iter,
const uint8_t *x, uint32_t n,
const uint8_t *p, uint32_t m
) {
iter->x = x; iter->n = n;
iter->p = p; iter->m = m;
iter->i = iter->b = 0;
Since we allocated the border array when we initialized the iterator, we need to free it
again when we deallocate it.
void dealloc_border_match_iter(
struct border_match_iter *iter
) {
free(iter->border_array);
}
19
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
bool next_border_match(
struct border_match_iter *iter,
struct match *match
) {
const uint8_t *x = iter->x;
const uint8_t *p = iter->p;
uint32_t *ba = iter->border_array;
uint32_t b = iter->b;
uint32_t m = iter->m;
uint32_t n = iter->n;
return false;
}
20
WOW! eBook
www.wowebook.org
Chapter 2 Classical algorithms for exact search
K
nuth-Morris-Pratt
The Knuth-Morris-Pratt (KMP) algorithm also uses borders to achieve a best- and worst-
case running time of O(n + m), but it uses the borders in a slightly different way. Before
we get to the algorithm, however, I want to convince you that we can, conceptually, move
the pattern p through x in two different ways; see Figure 2-7. We can let j be an index
into x and imagine p starting there. When we test if p matches there, we use a pointer
into p, i, and test x[ j + i] against p[i] for increasing i. To move p to another position in x,
we change j, for example, to slide p one position to the right we increment j by one.
Alternatively, we can imagine p aligned at position j − i for some index j in x and an
index i into p. If we change i, we move j − i so we move p. If, for example, we want
to move p one step to the right, we can decrement i by one. To understand how the
KMP algorithm works, it is useful to think about moving p in the second way. We will
increment the j and i indices when matching characters, but when we get a mismatch,
we move p by decrementing i.
The idea in KMP is to move p along x as we would in the naïve algorithm, but move
a little faster when we have a mismatch. We use index j to point into x and i to point
into p. We match x[ j] against p[i] as we scan along x and the pattern is aligned against
x at index j − i. We can move p’s position by modifying either i or j. Consider a place
in the algorithm where we have matched p[0, i] against x[ j − i, j] and see a mismatch.
In the naïve algorithm, we would move p one step to the right and start matching p
again at that position. We would set i to zero to start matching from the beginning of p,
and we would decrement j to j − i + 1 to match at the new position at which we would
match p. With KMP we will skip positions where we know that p cannot match, and we
use borders to do this.
21
WOW! eBook
www.wowebook.org
Discovering Diverse Content Through
Random Scribd Documents
The Project Gutenberg eBook of Suomen kansan
eläinkirja
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.
Language: Finnish
Kansansaduista sommitellut
SISÄLLYS:
»Suomen kansan eläinkirja», alkulause.
I. Eläimet tekevät tiet Metsolaan; kettu työpehtorina.
II. Ahkerat palkitaan; karhu ja muurahainen; pyyn tarina;
ketun palkka.
III. Laiskoja rangaistaan; päästäjäinen kirotaan;
käärmeen tarina.
IV. Samoin; palokärjen tarina, haukka ja hiiriäistuuppa;
käen tarina.
V. Samoin kyyhkynen ja sammakko, kyyhkynen ja metsäkana;
tiainen
rangaistaan; rastaan tarina; pääskysen tarina; yökön tarina.
VI. Karhun ja suden ahneus sekä Tapion rangaistus. — Koira
menee ihmisen luo.
VII. Kettu tutkii loukkaan ja houkuttelee siihen karhun, jonka
hiiri pelastaa.
VIII. Koira pelastaa Pohjolalle elämisen mahdollisuuden. — Mies
rakentaa pyydyksensä eläinten teille; hänen ja eläinten
sopimus molemminpuolisista
oikeuksista. — Karhun ja suden synty.
IX. Kettu houkuttelee suden kuoppaan. — Kettu ja teeri.
X. Variksen vala; sammakon tarina.
XI. Kettu ja varsa; korpin juusto; harakka ja suksipuun hakija;
kettu ja varis.
XII. Kettu ja jänis.
XIII. Kettu ja kurki.
XIV. Karhu ja jalopeura sekä heidän sotansa; mies vaimonpoika.
XV. Karhun eukon kuolema; itkijän ja lapsenpiian haku; kettu
itkijänä; tuulensuunnat; meden haku.
XVI. Suden kirjavoiminen; kolmen puun nimi; tamman alahuuli;
susi oppii lentämään.
XVII. Kettu ja suden akka; kettu pakenee muille maille; hänen
muinainen kataluutensa.
XVIII. Lähtö Ilmolaan pahantekoon; avulias akka palkitaan; kolme
oinasta; Horpon ruuna ja karhu; sormikoukun veto ja muut
karhun onnettomuudet; jänis ja paarma auttavat karhua.
XIX. Horpon isännän ja karhun yhteinen kaski. — Metson
isänmaanrakkaus. — Karhu rovastina.
XX. Piippolan sika ja päivän nousu; sian lersa kärsä. —
Immilän ylpeä kukko.
XXI. Immilän kukon esi-isän kuninkaissa käynti.
XXII. Kettu ja Immilän kukko; eläinten rauha; kukon ja kissan
talous.
XXIII. Kettu ja Piippolan vaarin kalanpyynti; karhu onkii hännällään
kaloja; kettu syö kirnuuksen; »Sairas tervettä kantaa»;
karhun kosto; kettu syö uudelleen viiliä.
XXIV. Susi Kääpälään; Merkin vanhuuden onnettomuudet; susi
auttaa Merkkiä; Merkki kertoo penikalle tarinan susista ja
kadonneesta pöytäkirjasta.
XXV. Susi, pukki ja pässi; susi ja räätäli; porsasten kastaminen;
pukin maan vakoaminen; pässien riidan ratkaiseminen;
lampaille veisaaminen; tamman passin katsominen.
XXVI. Merkki juottaa suden humalaan.
XXVII. Jänis ja pakkanen. — Kissan ja hiiren synty. — Jäniksen ja
kissan yhteinen talous. — Jäniksen pelastuskeinot. — Kissa
ja päästäjäinen. — Kissan vilja ja jäniksen vilja. — Kissan
ja koiran metsästys. — Jänis jättää kissan. — Kissan osuus
lehmän antiin.
XXVIII. Hiiren ja hänen sukunsa historia; hiiri ja sammakko; hiiri
räätälinä; katti kosiomiehenä; kello kissan kaulaan.
XXIX. Pukki ja Kääpälän vaari; pukki Katilan autiotuvassa ja hänen
karkoittajansa: karhu, hukka, peura ja kukko. — Jänis
takaisin Metsolaan.
XXXX. Horpon isäntä juottaa karhun humalaan. — Karhu
loukkaassa.
— »Maksan niinkuin maailma maksaa.» — Jänis, hevonen,
koira
ja kettu tuomareina. — Ketun palkka.
XXXI. Karhun, suden ja ketun kaskenkaato; Piippolan muorin
voipytty;
ketun ristiäisissä käynti; kettu karkotetaan.
XXXII. Kääpälän kissa ketulle rengiksi; kissan topakka suku.
XXXIII. Kettu kertoo karhulle ja sudelle uudesta rengistänsä; sen
kärpäset, nimet ja aseet; ja susi kärpäsiä katsomassa; karhu
ja susi herra Vinkkeliä katsomassa.
XXXIV. Karhu ja susi laittavat pidot herra Vinkkelille ja ketulle.
XXXV. Karhu ja susi hakevat herra Vinkkeliä ontosta hongasta.
XXXVI. Metsän eläimet ryhtyvät sotimaan kotieläimiä vastaan.
XXXVII. Kääpälän vaari joutuu kärpän saaliiksi; yön vietto.
XXXVIII. Yötä viettäessä tarinoidaan: Orava, neula ja kinnas.
XXXIX. Lisää tarinoita: Jalopeura, hevonen, susi ja karhu; kettu
houkuttelee karhun syömään suoliansa.
XL. Kevät; rastas ja Vänskän Aatami; metsäkana; Kirppu-Jussi
ja korppi.
XLI. Lintujen käräjät; pajulintu ja köyhän miehen kylvös;
pääskysen silkinvarkaus.
XLII. Pääskynen ja varpunen; varpunen ja raunioruntti; raunioruntti
ja kivenviha.
XLIII. Raunioruntti ja västäräkki; västäräkki, rastas ja koskikara;
västäräkki ja rantasipi.
XLIV. Tiltaltti, rastas, kyyhkynen ja varis; tiainen ja Musti;
käki ja närhi; rastas ja koppelo.
XLV. Karhu, susi ja kettu polttavat kaskensa.
XLVI. Immilän kukon oluenjuonti ja humala; eläinten retki
maailmanloppua pakoon.
XLVII. Ilmolan pelimanni opettaa karhua soittamaan.
XLVIII. Kiisken kirkko; kalojen käräjät Airistolla; kiiski kalojen
kuningas.
XLIX. Kerjäläisakka, karhu, susi, repo ja jänis.
L. Koiraseläimet ja Kääpälän kissa pakenevat Immilästä.
LI. Immilän rikkauden alku: mustan härän tarina.
LII. Outo vieras autiotuvalla ja sen karkoitus.
LIII. Karhu, susi ja kettu leikkuupellolla, riihessä ja myllyllä;
karhu ja susi maistavat ketun puuroa; karhun puuronkeitto;
suden kuolema.
LIV. Karhu tahtoo syödä Horpon isännän hevosen; ketun neuvot;
»Karhujen tappaja ja miesten sitoja»; karhun kuolema.
LV. Ketun palkka ja kuolema.
LVI. Tiet tehty Suomen ympäri, Metsolan eläimet karkoitettu.
Alkulause.
Osmo Iisalo.
I.
»Sinä karhu, kuomaseni, joka ylen väkevä mies olet, sinun pitää
vääntää pois kannot ja kivet…»
Karhu mörisi, että raskaimman työn sinä nyt hänelle annat, mutta
kettu siihen ehätti liukkaasti sanomaan:
Siitä toiset sanomaan, että kovin tulee mutkainen tie sillä tavalla,
mutta kettu määräsi: »Ei se haittaa mitään, olkoon vain mutkia
metsän teissä.»
»En minä viitsi tietä tehdä. Minä asun suolla enkä kankaalla.»
»Nyt kun on tiet tehty ja kaikki metsän viljan polut laitettu, käydään
ahkeria palkitsemaan, mutta laiskoja ja huolimattomia rankaisemaan.
Se on Tapion ja Mielikin tahto, jonka he ovat minulle tarkoin
selittäneet ja teille ilmoittaa käskeneet. Kuka teistä on nyt kovimman
työn tehnyt?»
*****
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
textbookfull.com