word_wrapper 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +11 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/lib/greedy.rb +33 -0
- data/lib/minimum_raggedness.rb +108 -0
- data/lib/word_wrapper.rb +44 -0
- data/samples/before-the-law.txt +1 -0
- data/samples/getty-long.txt +3 -0
- data/samples/getty-med.txt +1 -0
- data/samples/getty-small.txt +1 -0
- data/samples/oliver-twist.txt +119 -0
- data/test/word_wrapper_bench.rb +38 -0
- data/test/word_wrapper_test.rb +80 -0
- metadata +71 -0
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright © 2013 Stephen Pike. All Rights Reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
4
|
+
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
|
9
|
+
3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
|
10
|
+
|
11
|
+
THIS SOFTWARE IS PROVIDED BY [LICENSOR] "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Ruby word wrapping implementation
|
2
|
+
============================================================
|
3
|
+
|
4
|
+
This is a pure ruby implementation of word wrapping. Currently supports the Greedy algorithm as well as Knuth's [Minimum Raggedness](http://en.wikipedia.org/wiki/Word_wrap#Knuth.27s_algorithm).
|
5
|
+
|
6
|
+
Install:
|
7
|
+
|
8
|
+
gem install word_wrapper
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
|
12
|
+
require 'word_wrapper'
|
13
|
+
|
14
|
+
> text = "Before the law sits a gatekeeper. To this gatekeeper comes a man"
|
15
|
+
|
16
|
+
> puts WordWrapper::MinimumRaggedness.new(30, text).wrap
|
17
|
+
|
18
|
+
Before the law sits a
|
19
|
+
gatekeeper. To this
|
20
|
+
gatekeeper comes a man
|
21
|
+
|
22
|
+
> puts WordWrapper::Greedy.new(30, text).wrap
|
23
|
+
|
24
|
+
Before the law sits a
|
25
|
+
gatekeeper. To this gatekeeper
|
26
|
+
comes a man
|
27
|
+
|
28
|
+
Contributing:
|
29
|
+
|
30
|
+
You can run the tests if you have the Minitest gem installed with:
|
31
|
+
|
32
|
+
rake test
|
data/Rakefile
ADDED
data/lib/greedy.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class WordWrapper
|
2
|
+
class Greedy < WordWrapper
|
3
|
+
def wrap
|
4
|
+
words = @input.split
|
5
|
+
ans = ""
|
6
|
+
while words.any?
|
7
|
+
line = words.shift
|
8
|
+
while words.any? and (line.length + words[0].length) <= @width-1 # room for " "
|
9
|
+
line << " " << words.shift
|
10
|
+
end
|
11
|
+
ans << line << "\n"
|
12
|
+
end
|
13
|
+
@output = ans
|
14
|
+
end
|
15
|
+
|
16
|
+
def cost
|
17
|
+
compute_wrapping unless @cost
|
18
|
+
@cost
|
19
|
+
end
|
20
|
+
|
21
|
+
def compute_wrapping
|
22
|
+
@output = wrap
|
23
|
+
@cost = total_cost(@output)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if $0 == __FILE__ # run from cl
|
28
|
+
g = Greedy.new
|
29
|
+
g.compute_wrapping
|
30
|
+
puts g.output
|
31
|
+
puts "Greedy costs #{g.cost}"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
class WordWrapper
|
2
|
+
class MinimumRaggedness < WordWrapper
|
3
|
+
attr_accessor :splits
|
4
|
+
|
5
|
+
# Return the c
|
6
|
+
# @param [Array<String]] words in the text
|
7
|
+
# @param [Integer] i left bound of words to check between
|
8
|
+
# @param [Integer] j right bound of words to check between
|
9
|
+
# @return [Integer] cost of a line containing the words from i to j
|
10
|
+
def cost_between(words, i, j)
|
11
|
+
@c ||= {}
|
12
|
+
@c[[i,j]] ||=
|
13
|
+
begin
|
14
|
+
# Special case for single words that are longer than @width.
|
15
|
+
# Mark their cost as 0 so they get their own line without
|
16
|
+
# messing up the algorithm
|
17
|
+
if j == i and words[j-1].length >= @width
|
18
|
+
cost = 0
|
19
|
+
else
|
20
|
+
cost = @width -
|
21
|
+
((j - i) * OneSpaceWidth ) -
|
22
|
+
words[i-1..j-1].inject(0){ |acc, w| acc + w.length } # 0 indexed
|
23
|
+
cost = cost >= 0 ? cost**2 : Infinity
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Use dynamic programming to computer the optimal cost of this text.
|
29
|
+
# Recursively calls itself, keeping track of costs found as well as the
|
30
|
+
# array of splits required to give that cost (so we can actually generate
|
31
|
+
# the optimal text at the end).
|
32
|
+
#
|
33
|
+
# @param [Array<String>] words to split
|
34
|
+
# @param [Intger] j index to compute cost up through, goes from 1..length of words as we
|
35
|
+
# recursively compute costs.
|
36
|
+
#
|
37
|
+
# The evaluation looks like this (o is shorthand for optimal_cost):
|
38
|
+
#
|
39
|
+
# -- o(1, j) if c(1,j) < Inf
|
40
|
+
# o(j) = -
|
41
|
+
# -- min[ 1 <= k < j ] ( o(k) + c(k+1, j) ) if c(1,j) == Inf
|
42
|
+
#
|
43
|
+
# @return [Array<Integer, Array<String>>] (cost, [chain of splits that gives cost])
|
44
|
+
def optimal_cost(words, j)
|
45
|
+
@o ||= {}
|
46
|
+
@o[j] ||=
|
47
|
+
begin
|
48
|
+
ks = []
|
49
|
+
cost = cost_between words, 1, j
|
50
|
+
if cost == Infinity and j > 1
|
51
|
+
ks = (1..j-1)
|
52
|
+
candidates = {}
|
53
|
+
ks.collect do |k|
|
54
|
+
o = optimal_cost words, k
|
55
|
+
c = cost_between words, k + 1, j
|
56
|
+
# store both the chain of the child call and k
|
57
|
+
candidates[c + o[0]] = [o[1], k]
|
58
|
+
end
|
59
|
+
if candidates.any?
|
60
|
+
cost = candidates.keys.min
|
61
|
+
# ks is the chain of splits for this line of recursion
|
62
|
+
ks = candidates[cost][0] + [candidates[cost][1]]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# cost of this line, chain of splits that result in this cost
|
66
|
+
[cost,ks]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def cost
|
71
|
+
compute_wrapping unless @cost
|
72
|
+
@cost
|
73
|
+
end
|
74
|
+
|
75
|
+
def wrap
|
76
|
+
compute_wrapping unless @splits
|
77
|
+
prev = 0
|
78
|
+
ans = ""
|
79
|
+
@splits.each do |s|
|
80
|
+
ans << @words[prev..s-1].join(" ") << "\n"
|
81
|
+
prev = s
|
82
|
+
end
|
83
|
+
ans << @words[prev..@words.length].join(" ") << "\n"
|
84
|
+
ans
|
85
|
+
end
|
86
|
+
|
87
|
+
def compute_wrapping
|
88
|
+
@words = @input.split
|
89
|
+
@cost, @splits = optimal_cost(@words.dup, @words.length)
|
90
|
+
end
|
91
|
+
|
92
|
+
def solution?
|
93
|
+
cost != Infinity
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
if $0 == __FILE__ # from cl
|
98
|
+
m = MinimumRaggedness.new
|
99
|
+
m.compute_wrapping
|
100
|
+
if m.solution?
|
101
|
+
puts m.wrap
|
102
|
+
puts "Minimum Raggedness costs #{m.cost}"
|
103
|
+
else
|
104
|
+
puts "Couldn't wrap the input to #{m.width} characters"
|
105
|
+
puts "Possible issues: #{m.illegal_words.join(', ')}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/word_wrapper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Implementation of word wrappign in Ruby. See README for usage.
|
2
|
+
class WordWrapper
|
3
|
+
OneSpaceWidth = 1
|
4
|
+
Infinity = 1/0.0
|
5
|
+
Width = 100
|
6
|
+
|
7
|
+
attr_accessor :width, :words, :output, :cost
|
8
|
+
def initialize(width = Width, text = nil)
|
9
|
+
@width = width
|
10
|
+
if text
|
11
|
+
@input = text
|
12
|
+
elsif ARGV[0]
|
13
|
+
begin
|
14
|
+
@input = File.read(ARGV[0])
|
15
|
+
rescue Errno::ENOENT => e
|
16
|
+
@input = ARGV[0]
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise "You need to supply an input string or file"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Calculate the cost of a line. Cost is defined as
|
24
|
+
# [ Trailing whitespace ] ^ 2
|
25
|
+
# @param [String] line to compute cost for
|
26
|
+
def line_cost(line)
|
27
|
+
(@width - line.strip.length)**2 # no lines will ever start with whitespace
|
28
|
+
end
|
29
|
+
|
30
|
+
# The total cost of a block of text
|
31
|
+
# @param [String] text
|
32
|
+
def total_cost(text)
|
33
|
+
text.split("\n").inject(0){ |acc, line| acc + line_cost(line) }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Any words in the text longer than the width of the output
|
37
|
+
# @return [Array<String>] illegal words
|
38
|
+
def illegal_words
|
39
|
+
@words.select{ |word| word.length > @width }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require "minimum_raggedness"
|
44
|
+
require "greedy"
|
@@ -0,0 +1 @@
|
|
1
|
+
Before the law sits a gatekeeper. To this gatekeeper comes a man from the country who asks to gain entry into the law. But the gatekeeper says that he cannot grant him entry at the moment. The man thinks about it and then asks if he will be allowed to come in sometime later on. “It is possible,” says the gatekeeper, “but not now.” The gate to the law stands open, as always, and the gatekeeper walks to the side, so the man bends over in order to see through the gate into the inside. When the gatekeeper notices that, he laughs and says: “If it tempts you so much, try going inside in spite of my prohibition. But take note. I am powerful. And I am only the most lowly gatekeeper. But from room to room stand gatekeepers, each more powerful than the other. I cannot endure even one glimpse of the third.” The man from the country has not expected such difficulties: the law should always be accessible for everyone, he thinks, but as he now looks more closely at the gatekeeper in his fur coat, at his large pointed nose and his long, thin, black Tartar’s beard, he decides that it would be better to wait until he gets permission to go inside. The gatekeeper gives him a stool and allows him to sit down at the side in front of the gate. There he sits for days and years. He makes many attempts to be let in, and he wears the gatekeeper out with his requests. The gatekeeper often interrogates him briefly, questioning him about his homeland and many other things, but they are indifferent questions, the kind great men put, and at the end he always tells him once more that he cannot let him inside yet. The man, who has equipped himself with many things for his journey, spends everything, no matter how valuable, to win over the gatekeeper. The latter takes it all but, as he does so, says, “I am taking this only so that you do not think you have failed to do anything.” During the many years the man observes the gatekeeper almost continuously. He forgets the other gatekeepers, and this first one seems to him the only obstacle for entry into the law. He curses the unlucky circumstance, in the first years thoughtlessly and out loud; later, as he grows old, he only mumbles to himself. He becomes childish and, since in the long years studying the gatekeeper he has also come to know the fleas in his fur collar, he even asks the fleas to help him persuade the gatekeeper. Finally his eyesight grows weak, and he does not know whether things are really darker around him or whether his eyes are merely deceiving him. But he recognizes now in the darkness an illumination which breaks inextinguishably out of the gateway to the law. Now he no longer has much time to live. Before his death he gathers in his head all his experiences of the entire time up into one question which he has not yet put to the gatekeeper. He waves to him, since he can no longer lift up his stiffening body. The gatekeeper has to bend way down to him, for the great difference has changed things considerably to the disadvantage of the man. “What do you still want to know now?” asks the gatekeeper. “You are insatiable.” “Everyone strives after the law,” says the man, “so how is it that in these many years no one except me has requested entry?” The gatekeeper sees that the man is already dying and, in order to reach his diminishing sense of hearing, he shouts at him, “Here no one else can gain entry, since this entrance was assigned only to you. I’m going now to close it.”
|
@@ -0,0 +1,3 @@
|
|
1
|
+
Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.
|
2
|
+
Now we are engaged in a great civil war, testing whether that nation, or any nation, so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.
|
3
|
+
But, in a larger sense, we can not dedicate, we can not consecrate, we can not hallow this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us—that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion—that we here highly resolve that these dead shall not have died in vain—that this nation, under God, shall have a new birth of freedom—and that government of the people, by the people, for the people, shall not perish from the earth.
|
@@ -0,0 +1 @@
|
|
1
|
+
Four score and seven years ago our fathers brought forth upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal
|
@@ -0,0 +1 @@
|
|
1
|
+
Four score and seven years ago our fathers brought forth upon this continent a new nation
|
@@ -0,0 +1,119 @@
|
|
1
|
+
CHAPTER I
|
2
|
+
|
3
|
+
TREATS OF THE PLACE WHERE OLIVER TWIST WAS BORN AND OF THE
|
4
|
+
CIRCUMSTANCES ATTENDING HIS BIRTH
|
5
|
+
|
6
|
+
Among other public buildings in a certain town, which for many reasons
|
7
|
+
it will be prudent to refrain from mentioning, and to which I will
|
8
|
+
assign no fictitious name, there is one anciently common to most towns,
|
9
|
+
great or small: to wit, a workhouse; and in this workhouse was born; on
|
10
|
+
a day and date which I need not trouble myself to repeat, inasmuch as
|
11
|
+
it can be of no possible consequence to the reader, in this stage of
|
12
|
+
the business at all events; the item of mortality whose name is
|
13
|
+
prefixed to the head of this chapter.
|
14
|
+
|
15
|
+
For a long time after it was ushered into this world of sorrow and
|
16
|
+
trouble, by the parish surgeon, it remained a matter of considerable
|
17
|
+
doubt whether the child would survive to bear any name at all; in which
|
18
|
+
case it is somewhat more than probable that these memoirs would never
|
19
|
+
have appeared; or, if they had, that being comprised within a couple of
|
20
|
+
pages, they would have possessed the inestimable merit of being the
|
21
|
+
most concise and faithful specimen of biography, extant in the
|
22
|
+
literature of any age or country.
|
23
|
+
|
24
|
+
Although I am not disposed to maintain that the being born in a
|
25
|
+
workhouse, is in itself the most fortunate and enviable circumstance
|
26
|
+
that can possibly befall a human being, I do mean to say that in this
|
27
|
+
particular instance, it was the best thing for Oliver Twist that could
|
28
|
+
by possibility have occurred. The fact is, that there was considerable
|
29
|
+
difficulty in inducing Oliver to take upon himself the office of
|
30
|
+
respiration,--a troublesome practice, but one which custom has rendered
|
31
|
+
necessary to our easy existence; and for some time he lay gasping on a
|
32
|
+
little flock mattress, rather unequally poised between this world and
|
33
|
+
the next: the balance being decidedly in favour of the latter. Now,
|
34
|
+
if, during this brief period, Oliver had been surrounded by careful
|
35
|
+
grandmothers, anxious aunts, experienced nurses, and doctors of
|
36
|
+
profound wisdom, he would most inevitably and indubitably have been
|
37
|
+
killed in no time. There being nobody by, however, but a pauper old
|
38
|
+
woman, who was rendered rather misty by an unwonted allowance of beer;
|
39
|
+
and a parish surgeon who did such matters by contract; Oliver and
|
40
|
+
Nature fought out the point between them. The result was, that, after
|
41
|
+
a few struggles, Oliver breathed, sneezed, and proceeded to advertise
|
42
|
+
to the inmates of the workhouse the fact of a new burden having been
|
43
|
+
imposed upon the parish, by setting up as loud a cry as could
|
44
|
+
reasonably have been expected from a male infant who had not been
|
45
|
+
possessed of that very useful appendage, a voice, for a much longer
|
46
|
+
space of time than three minutes and a quarter.
|
47
|
+
|
48
|
+
As Oliver gave this first proof of the free and proper action of his
|
49
|
+
lungs, the patchwork coverlet which was carelessly flung over the iron
|
50
|
+
bedstead, rustled; the pale face of a young woman was raised feebly
|
51
|
+
from the pillow; and a faint voice imperfectly articulated the words,
|
52
|
+
'Let me see the child, and die.'
|
53
|
+
|
54
|
+
The surgeon had been sitting with his face turned towards the fire:
|
55
|
+
giving the palms of his hands a warm and a rub alternately. As the
|
56
|
+
young woman spoke, he rose, and advancing to the bed's head, said, with
|
57
|
+
more kindness than might have been expected of him:
|
58
|
+
|
59
|
+
'Oh, you must not talk about dying yet.'
|
60
|
+
|
61
|
+
'Lor bless her dear heart, no!' interposed the nurse, hastily
|
62
|
+
depositing in her pocket a green glass bottle, the contents of which
|
63
|
+
she had been tasting in a corner with evident satisfaction.
|
64
|
+
|
65
|
+
'Lor bless her dear heart, when she has lived as long as I have, sir,
|
66
|
+
and had thirteen children of her own, and all on 'em dead except two,
|
67
|
+
and them in the wurkus with me, she'll know better than to take on in
|
68
|
+
that way, bless her dear heart! Think what it is to be a mother,
|
69
|
+
there's a dear young lamb do.'
|
70
|
+
|
71
|
+
Apparently this consolatory perspective of a mother's prospects failed
|
72
|
+
in producing its due effect. The patient shook her head, and stretched
|
73
|
+
out her hand towards the child.
|
74
|
+
|
75
|
+
The surgeon deposited it in her arms. She imprinted her cold white
|
76
|
+
lips passionately on its forehead; passed her hands over her face;
|
77
|
+
gazed wildly round; shuddered; fell back--and died. They chafed her
|
78
|
+
breast, hands, and temples; but the blood had stopped forever. They
|
79
|
+
talked of hope and comfort. They had been strangers too long.
|
80
|
+
|
81
|
+
'It's all over, Mrs. Thingummy!' said the surgeon at last.
|
82
|
+
|
83
|
+
'Ah, poor dear, so it is!' said the nurse, picking up the cork of the
|
84
|
+
green bottle, which had fallen out on the pillow, as she stooped to
|
85
|
+
take up the child. 'Poor dear!'
|
86
|
+
|
87
|
+
'You needn't mind sending up to me, if the child cries, nurse,' said
|
88
|
+
the surgeon, putting on his gloves with great deliberation. 'It's very
|
89
|
+
likely it _will_ be troublesome. Give it a little gruel if it is.' He
|
90
|
+
put on his hat, and, pausing by the bed-side on his way to the door,
|
91
|
+
added, 'She was a good-looking girl, too; where did she come from?'
|
92
|
+
|
93
|
+
'She was brought here last night,' replied the old woman, 'by the
|
94
|
+
overseer's order. She was found lying in the street. She had walked
|
95
|
+
some distance, for her shoes were worn to pieces; but where she came
|
96
|
+
from, or where she was going to, nobody knows.'
|
97
|
+
|
98
|
+
The surgeon leaned over the body, and raised the left hand. 'The old
|
99
|
+
story,' he said, shaking his head: 'no wedding-ring, I see. Ah!
|
100
|
+
Good-night!'
|
101
|
+
|
102
|
+
The medical gentleman walked away to dinner; and the nurse, having once
|
103
|
+
more applied herself to the green bottle, sat down on a low chair
|
104
|
+
before the fire, and proceeded to dress the infant.
|
105
|
+
|
106
|
+
What an excellent example of the power of dress, young Oliver Twist
|
107
|
+
was! Wrapped in the blanket which had hitherto formed his only
|
108
|
+
covering, he might have been the child of a nobleman or a beggar; it
|
109
|
+
would have been hard for the haughtiest stranger to have assigned him
|
110
|
+
his proper station in society. But now that he was enveloped in the
|
111
|
+
old calico robes which had grown yellow in the same service, he was
|
112
|
+
badged and ticketed, and fell into his place at once--a parish
|
113
|
+
child--the orphan of a workhouse--the humble, half-starved drudge--to
|
114
|
+
be cuffed and buffeted through the world--despised by all, and pitied
|
115
|
+
by none.
|
116
|
+
|
117
|
+
Oliver cried lustily. If he could have known that he was an orphan,
|
118
|
+
left to the tender mercies of church-wardens and overseers, perhaps he
|
119
|
+
would have cried the louder.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
include Benchmark
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
5
|
+
require 'word_wrapper'
|
6
|
+
|
7
|
+
samples_dir = File.join(File.dirname(__FILE__), '..', 'samples')
|
8
|
+
@oliver_twist = File.read(File.join(samples_dir, "oliver-twist.txt"))
|
9
|
+
@before_the_law = File.read(File.join(samples_dir, "before-the-law.txt"))
|
10
|
+
@gettysburg = File.read(File.join(samples_dir, "getty-long.txt"))
|
11
|
+
|
12
|
+
def run_greedy(text, times=10000)
|
13
|
+
g = WordWrapper::Greedy.new(100, text)
|
14
|
+
times.times do
|
15
|
+
g.compute_wrapping
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_mr(text, times=100)
|
20
|
+
g = WordWrapper::MinimumRaggedness.new(100, text)
|
21
|
+
times.times do
|
22
|
+
g.compute_wrapping
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
puts "Greedy algorithm"
|
27
|
+
Benchmark.bm do |x|
|
28
|
+
x.report("#{@gettysburg.split.length} words x 10000:") { run_greedy(@gettysburg) }
|
29
|
+
x.report("#{@before_the_law.split.length} words x 10000:") { run_greedy(@before_the_law) }
|
30
|
+
x.report("#{@oliver_twist.split.length} words x 10:") { run_greedy(@oliver_twist, 10) }
|
31
|
+
end
|
32
|
+
|
33
|
+
puts "\nMinimum Raggedness algorithm"
|
34
|
+
Benchmark.bm do |x|
|
35
|
+
x.report("#{@gettysburg.split.length} words x 10000:") { run_mr(@gettysburg) }
|
36
|
+
x.report("#{@before_the_law.split.length} words x 10000:") { run_mr(@before_the_law) }
|
37
|
+
x.report("#{@oliver_twist.split.length} words x 10:") { run_mr(@oliver_twist, 10) }
|
38
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'word_wrapper'
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class WordWrapperTest < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@short_text = "Four score and seven years ago our fathers brought forth upon this continent a new nation"
|
9
|
+
@med_text = "Four score and seven years ago our fathers brought forth upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal"
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_greedy_costs
|
13
|
+
assert_equal 90, WordWrapper::Greedy.new(13, @short_text).cost
|
14
|
+
assert_equal 264, WordWrapper::Greedy.new(13, @med_text).cost
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_min_raggedness_costs
|
18
|
+
assert_equal 74, WordWrapper::MinimumRaggedness.new(13, @short_text).cost
|
19
|
+
assert_equal 204, WordWrapper::MinimumRaggedness.new(13, @med_text).cost
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_optimal_word
|
23
|
+
assert_equal 0, WordWrapper::Greedy.new(13, "A"*13 ).cost
|
24
|
+
assert_equal 0, WordWrapper::MinimumRaggedness.new(13, "A"*13 ).cost
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_greedy_output
|
28
|
+
assert_equal """Four score
|
29
|
+
and seven
|
30
|
+
years ago our
|
31
|
+
fathers
|
32
|
+
brought forth
|
33
|
+
upon this
|
34
|
+
continent a
|
35
|
+
new nation,
|
36
|
+
conceived in
|
37
|
+
liberty and
|
38
|
+
dedicated to
|
39
|
+
the
|
40
|
+
proposition
|
41
|
+
that all men
|
42
|
+
are created
|
43
|
+
equal
|
44
|
+
""", WordWrapper::Greedy.new(13,@med_text).wrap
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_minimum_raggedness_output
|
48
|
+
assert_equal """Four score
|
49
|
+
and seven
|
50
|
+
years ago
|
51
|
+
our fathers
|
52
|
+
brought forth
|
53
|
+
upon this
|
54
|
+
continent a
|
55
|
+
new nation,
|
56
|
+
conceived in
|
57
|
+
liberty and
|
58
|
+
dedicated
|
59
|
+
to the
|
60
|
+
proposition
|
61
|
+
that all
|
62
|
+
men are
|
63
|
+
created equal
|
64
|
+
""", WordWrapper::MinimumRaggedness.new(13,@med_text).wrap
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_minimum_raggedness_with_large_word
|
68
|
+
assert_equal """Before the
|
69
|
+
law sits a
|
70
|
+
gatekeeper.
|
71
|
+
To this
|
72
|
+
gatekeeper
|
73
|
+
comes
|
74
|
+
a man
|
75
|
+
""", WordWrapper::MinimumRaggedness
|
76
|
+
.new(10,
|
77
|
+
"Before the law sits a gatekeeper. To this gatekeeper comes a man")
|
78
|
+
.wrap
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: word_wrapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stephen Pike
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest
|
16
|
+
requirement: &70312798021560 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70312798021560
|
25
|
+
description: Word wrapping implementation in ruby. Includes a naive greedy algorithm
|
26
|
+
(fast) and Knuth's minimum raggedness algorithm from TeX (slower for long texts).
|
27
|
+
email: scpike@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- lib/greedy.rb
|
33
|
+
- lib/minimum_raggedness.rb
|
34
|
+
- lib/word_wrapper.rb
|
35
|
+
- test/word_wrapper_bench.rb
|
36
|
+
- test/word_wrapper_test.rb
|
37
|
+
- samples/before-the-law.txt
|
38
|
+
- samples/getty-long.txt
|
39
|
+
- samples/getty-med.txt
|
40
|
+
- samples/getty-small.txt
|
41
|
+
- samples/oliver-twist.txt
|
42
|
+
- Rakefile
|
43
|
+
- README.md
|
44
|
+
- LICENSE
|
45
|
+
homepage: https://github.com/scpike/word-wrapping/tree/master/ruby
|
46
|
+
licenses:
|
47
|
+
- bsd
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.8.15
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Pure ruby word wrapping
|
70
|
+
test_files: []
|
71
|
+
has_rdoc:
|