Skip to content

Commit c041ccd

Browse files
committed
Implement opt-in for MinimumRaggedness. Runs about 30x slower than the greedy algorithm but with much nicer looking results. Closes #2
1 parent d05a293 commit c041ccd

6 files changed

Lines changed: 593 additions & 9 deletions

File tree

benchmark_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,43 @@ func BenchmarkWrap120(b *testing.B) { benchmarkWrap(b, 120) }
2626
func BenchmarkWrap500(b *testing.B) { benchmarkWrap(b, 500) }
2727
func BenchmarkWrap1000(b *testing.B) { benchmarkWrap(b, 1000) }
2828
func BenchmarkWrap5000(b *testing.B) { benchmarkWrap(b, 5000) }
29+
30+
func benchmarkWrapOptimal(b *testing.B, limit int) {
31+
w := wrap.NewWrapper()
32+
w.MinimumRaggedness = true
33+
34+
b.ReportAllocs()
35+
for i := 0; i < b.N; i++ {
36+
w.Wrap(loremIpsums[0], limit)
37+
}
38+
}
39+
40+
func BenchmarkWrapOptimal10(b *testing.B) { benchmarkWrapOptimal(b, 10) }
41+
func BenchmarkWrapOptimal25(b *testing.B) { benchmarkWrapOptimal(b, 25) }
42+
func BenchmarkWrapOptimal80(b *testing.B) { benchmarkWrapOptimal(b, 80) }
43+
func BenchmarkWrapOptimal120(b *testing.B) { benchmarkWrapOptimal(b, 120) }
44+
func BenchmarkWrapOptimal500(b *testing.B) { benchmarkWrapOptimal(b, 500) }
45+
func BenchmarkWrapOptimal1000(b *testing.B) { benchmarkWrapOptimal(b, 1000) }
46+
47+
// BenchmarkGreedyVsOptimal compares greedy and optimal algorithms side by side.
48+
func BenchmarkGreedyVsOptimal(b *testing.B) {
49+
input := loremIpsums[0]
50+
limit := 80
51+
52+
b.Run("Greedy", func(b *testing.B) {
53+
w := wrap.NewWrapper()
54+
b.ReportAllocs()
55+
for i := 0; i < b.N; i++ {
56+
w.Wrap(input, limit)
57+
}
58+
})
59+
60+
b.Run("Optimal", func(b *testing.B) {
61+
w := wrap.NewWrapper()
62+
w.MinimumRaggedness = true
63+
b.ReportAllocs()
64+
for i := 0; i < b.N; i++ {
65+
w.Wrap(input, limit)
66+
}
67+
})
68+
}

example_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,31 @@ func ExampleWrapper_Wrap_prefix() {
236236
// // laoreet aliquet. Donec gravida congue massa, et sollicitudin turpis lacinia
237237
// // a. Fusce non tortor magna. Cras vel finibus tellus.
238238
}
239+
240+
func ExampleWrapper_Wrap_minimumRaggedness() {
241+
// This example demonstrates the difference between greedy and optimal wrapping.
242+
// The input is designed to show how optimal wrapping produces more balanced lines.
243+
var text = "a b c d e f g h i j k l m n o p"
244+
245+
fmt.Println("Greedy (default):")
246+
w := wrap.NewWrapper()
247+
w.StripTrailingNewline = true
248+
fmt.Println(w.Wrap(text, 9))
249+
250+
fmt.Println()
251+
fmt.Println("Optimal (MinimumRaggedness):")
252+
w.MinimumRaggedness = true
253+
fmt.Println(w.Wrap(text, 9))
254+
// Output:
255+
// Greedy (default):
256+
// a b c d e
257+
// f g h i j
258+
// k l m n o
259+
// p
260+
//
261+
// Optimal (MinimumRaggedness):
262+
// a b c d
263+
// e f g h
264+
// i j k l
265+
// m n o p
266+
}

fuzz_test.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,21 @@ func FuzzWrap(f *testing.F) {
102102
}
103103
}
104104
}
105+
106+
// Test with MinimumRaggedness
107+
w.CutLongWords = false
108+
w.MinimumRaggedness = true
109+
result4 := w.Wrap(input, limit)
110+
if !utf8.ValidString(result4) {
111+
t.Errorf("result with MinimumRaggedness is not valid UTF-8: %q", result4)
112+
}
113+
114+
// Test MinimumRaggedness with CutLongWords
115+
w.CutLongWords = true
116+
result5 := w.Wrap(input, limit)
117+
if !utf8.ValidString(result5) {
118+
t.Errorf("result with MinimumRaggedness+CutLongWords is not valid UTF-8: %q", result5)
119+
}
105120
})
106121
}
107122

@@ -146,18 +161,21 @@ func FuzzWrapWithOptions(f *testing.F) {
146161
t.Errorf("result is not valid UTF-8: %q", result)
147162
}
148163

149-
// Test all option combinations
164+
// Test all option combinations including MinimumRaggedness
150165
for _, strip := range []bool{false, true} {
151166
for _, cut := range []bool{false, true} {
152167
for _, includeLimit := range []bool{false, true} {
153-
w.StripTrailingNewline = strip
154-
w.CutLongWords = cut
155-
w.LimitIncludesPrefixSuffix = includeLimit
156-
157-
result := w.Wrap(input, limit)
158-
if !utf8.ValidString(result) {
159-
t.Errorf("result is not valid UTF-8 with strip=%v cut=%v includeLimit=%v: %q",
160-
strip, cut, includeLimit, result)
168+
for _, optimal := range []bool{false, true} {
169+
w.StripTrailingNewline = strip
170+
w.CutLongWords = cut
171+
w.LimitIncludesPrefixSuffix = includeLimit
172+
w.MinimumRaggedness = optimal
173+
174+
result := w.Wrap(input, limit)
175+
if !utf8.ValidString(result) {
176+
t.Errorf("result is not valid UTF-8 with strip=%v cut=%v includeLimit=%v optimal=%v: %q",
177+
strip, cut, includeLimit, optimal, result)
178+
}
161179
}
162180
}
163181
}

0 commit comments

Comments
 (0)