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