File Coverage

lib/Text/KnuthPlass.xs
Criterion Covered Total %
statement 145 161 90.0
branch 122 178 68.5
condition n/a
subroutine n/a
pod n/a
total 267 339 78.7


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4            
5             #include "ppport.h"
6            
7             #define isPenalty(sv) (SvROK(sv) && sv_derived_from(sv, "Text::KnuthPlass::Penalty"))
8             #define ivHash(hv, key) (IV)SvIV(*hv_fetch((HV*)hv, key, strlen(key), TRUE))
9             #define nvHash(hv, key) (NV)SvNVx(*hv_fetch((HV*)hv, key, strlen(key), TRUE))
10             #define debug(x)
11            
12             typedef SV * Text_KnuthPlass;
13            
14             struct Breakpoint_s {
15             struct Breakpoint_s * prev;
16             struct Breakpoint_s * next;
17             struct Breakpoint_s * previous;
18             struct Breakpoint_s * active; /* Just for candidates */
19             NV demerits;
20             NV ratio;
21             IV line;
22             IV position;
23             IV fitness_class;
24             HV* totals;
25             };
26            
27             typedef struct Breakpoint_s Breakpoint;
28            
29             typedef struct LinkedList_s {
30             Breakpoint* head;
31             Breakpoint* tail;
32             IV list_size;
33             AV* to_free;
34             } LinkedList;
35            
36 1364           NV _compute_cost(Text_KnuthPlass self, IV start, IV end, Breakpoint* a,
37             IV current_line, AV* nodes) {
38 1364 50         IV infinity = ivHash(self, "infinity");
39 1364           HV* sum = (HV*)SvRV(*hv_fetch((HV*)self, "sum", 3, FALSE));
40 1364           HV* totals = a->totals;
41 1364 100         NV width = nvHash(sum, "width") - nvHash(totals,"width");
    50          
42 1364           AV* linelengths = (AV*)SvRV(*hv_fetch((HV*)self, "linelengths", 11, FALSE));
43             /* ll is index of LAST element of linelengths, NOT the size (length) */
44 1364           I32 ll = av_len(linelengths);
45             NV stretch = 0;
46             NV shrink = 0;
47 1364 50         NV linelength = SvNV(*av_fetch(linelengths, current_line <= ll ? current_line-1 : ll, 0));
    100          
    50          
    50          
48             /*warn("Computing cost, ll=%i current_line=%i linelength=%i\n", ll, current_line, linelength);*/
49            
50             debug(warn("Computing cost from %i to %i\n", start, end));
51             debug(warn("Sum width: %f\n", nvHash(sum, "width")));
52             debug(warn("Total width: %f\n", nvHash(totals, "width")));
53 1364 50         if (isPenalty(*av_fetch(nodes, end, 0))) {
    100          
54             debug(warn("Adding penalty width\n"));
55 394 100         width += nvHash(SvRV(*av_fetch(nodes,end, 0)),"width");
56             }
57             debug(warn("Width %f, linelength %f\n", width, linelength));
58 1364 100         if (width < linelength) {
59 1316 100         stretch = nvHash(sum, "stretch") - nvHash(totals, "stretch");
    50          
60             debug(warn("Stretch %f\n", stretch));
61 1316 100         if (stretch > 0) {
62 1104           return (linelength - width) / stretch;
63             } else {
64 212           return infinity;
65             }
66 48 100         } else if (width > linelength) {
67             debug(warn("Shrink %f\n", shrink));
68 25 50         shrink = nvHash(sum, "shrink") - nvHash(totals, "shrink");
    50          
69 25 50         if (shrink > 0) {
70 25           return (linelength - width) / shrink;
71             } else {
72 0           return infinity;
73             }
74             } else { return 0; }
75             }
76            
77 157           HV* _compute_sum(Text_KnuthPlass self, IV index, AV* nodes) {
78 157           HV* result = newHV();
79 157           HV* sum = (HV*)SvRV(*hv_fetch((HV*)self, "sum", 3, FALSE));
80 157 50         IV infinity = ivHash(self, "infinity");
81 157 50         NV width = nvHash(sum, "width");
82 157 50         NV stretch = nvHash(sum, "stretch");
83 157 50         NV shrink = nvHash(sum, "shrink");
84 157           I32 len = av_len(nodes);
85 157           I32 i = index;
86            
87 311 100         while (i < len) {
88 304           SV* e = *av_fetch(nodes, i, 0);
89 304 100         if (sv_derived_from(e, "Text::KnuthPlass::Glue")) {
90 122 100         width += nvHash(SvRV(e), "width");
91 122 100         stretch += nvHash(SvRV(e), "stretch");
92 122 100         shrink += nvHash(SvRV(e), "shrink");
93 182 100         } else if (sv_derived_from(e, "Text::KnuthPlass::Box") ||
    50          
94 32 50         (isPenalty(e) && ivHash(SvRV(e), "penalty") == -infinity
    50          
    50          
95 0 0         && i > index)) {
96             break;
97             }
98 154           i++;
99             }
100            
101 157           hv_stores(result, "width", newSVnv(width));
102 157           hv_stores(result, "stretch", newSVnv(stretch));
103 157           hv_stores(result, "shrink", newSVnv(shrink));
104 157           return result;
105             }
106            
107 317           Breakpoint* _new_breakpoint (void) {
108             Breakpoint* dummy;
109 317           HV* totals = newHV();
110 317           Newxz(dummy, 1, Breakpoint);
111 317           dummy->prev = dummy->next = dummy->previous = dummy->active = NULL;
112 317           dummy->demerits = dummy->ratio = 0;
113 317           dummy->line = dummy->position = dummy->fitness_class = 0;
114 317           hv_stores(totals, "width", newSVnv(0));
115 317           hv_stores(totals, "stretch", newSVnv(0));
116 317           hv_stores(totals, "shrink", newSVnv(0));
117 317           dummy->totals = totals;
118 317           return dummy;
119             }
120            
121 0           void free_breakpoint(Breakpoint* b) {
122 0 0         while (b) {
123 0           Breakpoint* p = b->previous;
124 0 0         if (SvROK(b->totals)) {
125 0           sv_free((SV*)b->totals);
126 0           Safefree(b);
127             }
128             b = p;
129             }
130 0 0         if (b && b->totals) sv_free((SV*)b->totals);
    0          
131 0 0         if (b) Safefree(b);
132 0           }
133            
134 157           void _unlinkKP(LinkedList* list, Breakpoint* a) {
135 157 100         if (!a->prev) { list->head = a->next; } else { a->prev->next = a->next; }
136 157 100         if (!a->next) { list->tail = a->prev; } else { a->next->prev = a->prev; }
137 157           list->list_size--;
138 157           av_push(list->to_free, newSViv((IV)a));
139 157           }
140            
141             MODULE = Text::KnuthPlass PACKAGE = Text::KnuthPlass
142            
143             void
144             _init_nodelist(self)
145             Text_KnuthPlass self
146            
147             CODE:
148             LinkedList* activelist;
149 3           Newxz(activelist, 1, LinkedList);
150 3           activelist->head = activelist->tail = _new_breakpoint();
151 3           activelist->list_size = 1;
152 3           activelist->to_free = newAV();
153 3           hv_stores((HV*)self, "activeNodes", ((IV)activelist));
154            
155             void _active_to_breaks(self)
156             Text_KnuthPlass self
157            
158             PREINIT:
159             LinkedList* activelist;
160             Breakpoint* b;
161             Breakpoint* best = NULL;
162            
163             PPCODE:
164 3           activelist = (LinkedList*)(*hv_fetch((HV*)self, "activeNodes", 11, FALSE));
165            
166 6 100         for (b = activelist->head; b; b = b->next) {
167 3 50         if (!best || b->demerits < best->demerits) best = b;
    0          
168             }
169 18 100         while (best) {
170 15           HV* posnode = newHV();
171 15           hv_stores(posnode, "position", newSViv(best->position));
172 15           hv_stores(posnode, "ratio", newSVnv(best->ratio));
173 15 50         XPUSHs(sv_2mortal(newRV((SV*)posnode)));
174 15           best = best->previous;
175             }
176            
177             void _cleanup(self)
178             Text_KnuthPlass self
179            
180             CODE:
181             Breakpoint* b;
182             LinkedList* activelist;
183 3           activelist = (LinkedList*)(*hv_fetch((HV*)self, "activeNodes", 11, FALSE));
184             return;
185             /* Free nodes on the activelist */
186             b = activelist->head;
187             while (b) {
188             Breakpoint* n = b->next;
189             free_breakpoint(b);
190             b = n;
191             }
192             /* Shut down the activelist itself */
193             while (av_len(activelist->to_free)) {
194             SV* pointer = av_shift(activelist->to_free);
195             if ((Breakpoint*)SvIV(pointer))
196             free_breakpoint((Breakpoint*)SvIV(pointer));
197             sv_free(pointer);
198             }
199             sv_free((SV*)activelist->to_free);
200             //Safefree(activelist);
201            
202             void
203             _mainloop(self, node, index, nodes)
204             Text_KnuthPlass self
205             SV* node
206             IV index
207             AV* nodes
208            
209             CODE:
210 102           LinkedList* activelist = (LinkedList*)(*hv_fetch((HV*)self, "activeNodes", 11, FALSE));
211 102 50         IV tolerance = ivHash(self, "tolerance");
212 102 50         IV infinity = ivHash(self, "infinity");
213 102           SV* demerits_r = *hv_fetch((HV*)self, "demerits", 8, FALSE);
214             NV ratio = 0;
215             IV nodepenalty = 0;
216             NV demerits = 0;
217             IV linedemerits = 0, flaggeddemerits = 0, fitnessdemerits = 0;
218             Breakpoint* candidates[4];
219             NV badness;
220             IV current_line = 0;
221             HV* tmpsum;
222             IV current_class = 0;
223 102           Breakpoint* active = activelist->head;
224             Breakpoint* next;
225            
226 102 50         if (demerits_r && SvRV(demerits_r)) {
    50          
227 102 50         linedemerits = ivHash(SvRV(demerits_r), "line");
228 102 50         flaggeddemerits = ivHash(SvRV(demerits_r), "flagged");
229 102 50         fitnessdemerits = ivHash(SvRV(demerits_r), "fitness");
230             } else {
231 0           croak("Demerits hash not properly set!");
232             }
233            
234 102 50         if (isPenalty(node)) {
    100          
235 21 50         nodepenalty = SvIV(*hv_fetch((HV*)SvRV(node), "penalty", 7, TRUE));
236             }
237            
238 219 100         while (active) {
239             int t;
240 117           candidates[0] = NULL; candidates[1] = NULL;
241 117           candidates[2] = NULL; candidates[3] = NULL;
242             debug(warn("Outer\n"));
243 1364 50         while (active) {
244 1364           next = active->next;
245 1364           IV position = active->position;
246            
247             debug(warn("Inner loop\n"));
248            
249 1364           current_line = 1+ active->line;
250             /*warn("_mainloop, current_line=%i\n", current_line);*/
251 1364           ratio = _compute_cost(self, position, index, active, current_line, nodes);
252             debug(warn("Got a ratio of %f\n", ratio));
253 1364 100         if (ratio < 1 || (isPenalty(node) && nodepenalty == -infinity))
    50          
    100          
    100          
254 157           _unlinkKP(activelist, active);
255            
256 1364 100         if (-1 <= ratio && ratio <= tolerance) {
    100          
257 835           SV* nodeAtPos = *av_fetch(nodes, position, FALSE);
258 835           badness = 100 * ratio * ratio * ratio;
259             debug(warn("Badness is %f\n", badness));
260 835 50         if (isPenalty(node) && nodepenalty > 0) {
    100          
    100          
261 204           demerits = linedemerits + badness + nodepenalty;
262 631 50         } else if (isPenalty(node) && nodepenalty != -infinity) {
    100          
    50          
263 0           demerits = linedemerits + badness - nodepenalty;
264             } else {
265 631           demerits = linedemerits + badness;
266             }
267 835           demerits = demerits * demerits;
268 835 50         if (isPenalty(node) && isPenalty(SvRV(nodeAtPos))) {
    100          
    50          
    0          
269 0           demerits = demerits + (flaggeddemerits *
270 0 0         ivHash(node, "flagged") *
271 0 0         ivHash(SvRV(nodeAtPos), "flagged"));
272             }
273            
274 835 100         if (ratio < -0.5) current_class = 0;
275 834 100         else if (ratio <= 0.5) current_class = 1;
276 724 100         else if (ratio <= 1) current_class = 2;
277             else current_class = 3;
278            
279 835 100         if (abs(current_class - active->fitness_class) > 1)
280 242           demerits += fitnessdemerits;
281            
282 835           demerits += active->demerits;
283            
284 835 100         if (!candidates[current_class] ||
    100          
285 678           demerits < candidates[current_class]->demerits) {
286             debug(warn("Setting c %i\n", current_class));
287 295 100         if (!candidates[current_class])
288 157           candidates[current_class] = _new_breakpoint();
289 295           candidates[current_class]->active = active;
290 295           candidates[current_class]->demerits = demerits;
291 295           candidates[current_class]->ratio = ratio;
292             }
293             }
294             active = next;
295 1364 100         if (!active || active->line >= current_line)
    100          
296             break;
297             }
298             debug(warn("Post inner loop\n"));
299            
300            
301 687 100         for (t = 0; t <= 3; t++) {
302 468 100         if (candidates[t]) {
303 157           Breakpoint* newnode = _new_breakpoint();
304 157           HV* tmpsum = _compute_sum(self, index, nodes);
305 157           newnode->position = index;
306 157           newnode->fitness_class = t;
307 157           newnode->totals = tmpsum;
308             debug(warn("Setting previous to %p\n", candidates[t]->active));
309 157           newnode->previous = candidates[t]->active;
310 157           newnode->demerits = candidates[t]->demerits;
311 157           newnode->ratio = candidates[t]->ratio;
312 157           newnode->line = candidates[t]->line + 1;
313 157 100         if (active) {
314             debug(warn("Before\n"));
315 15           newnode->prev = active->prev;
316 15           newnode->next = active;
317 15 100         if (!active->prev) { activelist->head = newnode; }
318 12           else { active->prev->next = newnode; }
319 15           active->prev = newnode;
320 15           activelist->list_size++;
321             } else {
322             debug(warn("After\n"));
323 142 100         if (!activelist->head) {
324 3           activelist->head = activelist->tail = newnode;
325 3           newnode->prev = newnode->next = NULL;
326             } else {
327 139           newnode->prev = activelist->tail;
328 139           newnode->next = NULL;
329 139           activelist->tail->next = newnode;
330 139           activelist->tail = newnode;
331 139           activelist->list_size++;
332             }
333             }
334 157           sv_free((SV*)candidates[t]->totals);
335 157           Safefree(candidates[t]);
336             }
337             }
338             }