1
2
3 """
4 Interface to the U{qubidl<http://www.qub.buffalo.edu/src/qub-express/api/qubfast/qubidl.h.html>} module (in the qubfast library)
5 for efficiently handling sequences of dwells.
6
7 An Idealization assigns each sample to one of a limited number of Classes,
8 then condenses this sequence of sampled Classes into a sequence of Dwells:
9 { which class; start sample index; end sample index (inclusive) }
10 Classes are numbered contiguously starting with 0.
11 This module keeps track of each Class's {mean amplitude; standard deviation}.
12
13 Previously, Idealizations had been stored as parallel arrays:
14
15 >>> int class[]
16 >>> int first[]
17 >>> int last[]
18 >>> (and/or float duration[])
19
20
21 There were problems with this approach:
22 - editing a portion could change the number of dwells,
23 requiring shifts and/or reallocation
24 - to read the dwells for samples k through m,
25 we had to search for the first dwell,
26 since dwell index doesn't correlate with sample index
27
28 To address these, we use data structures with log(N) edits and seeks
29 (at the core, we use the STL map),
30 and move "chunks" of idealization between storage levels:
31 - in STL maps: efficient editing and seeking; biggest memory footprint
32 - in arrays: no editing beyond full replacement; smaller memory footprint
33
34 The idealization is divided into chunks of QUB_IDLSEG_CHUNKSIZE samples:
35 - for lookup: dwells for sample k are in chunk (k >> QUB_IDLSEG_CHUNKSHIFT)
36 - for map traversal and editing: N events take N log N time,
37 but since there are only so many events per map,
38 it becomes N log M, where log M has a constant upper bound
39 - for multiple storage levels: only the relevant chunks need to be loaded
40 for reading, or upgraded to "map" for editing.
41
42 The chunk size has been experimentally tuned to handle fast data smoothly,
43 but maybe wants adjustment for newer hardware?;
44 adjust QUB_IDLSEG_CHUNKSHIFT (log2(_CHUNKSIZE)) in qubidl.cpp.
45
46 The IdlMaps object manages one or more chunks.
47
48 Every sample is said to belong to a class. Un-idealized samples belong to dwells
49 of class -1.
50
51 .begin(), .find(), and .end() return iterator objects. They don't necessarily iterate
52 over entire dwells; long dwells are broken at chunk boundaries, resulting in a string
53 of dwells of the same class -- to the user they should be represented as one dwell.
54
55 Copyright 2008-2012 Research Foundation State University of New York
56 This file is part of QUB Express.
57
58 QUB Express is free software; you can redistribute it and/or modify
59 it under the terms of the GNU General Public License as published by
60 the Free Software Foundation, either version 3 of the License, or
61 (at your option) any later version.
62
63 QUB Express is distributed in the hope that it will be useful,
64 but WITHOUT ANY WARRANTY; without even the implied warranty of
65 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66 GNU General Public License for more details.
67
68 You should have received a copy of the GNU General Public License,
69 named LICENSE.txt, in the QUB Express program directory. If not, see
70 <http://www.gnu.org/licenses/>.
71
72
73 """
74
75
76
77 from ctypes import cdll, c_void_p, c_int, c_uint, c_double, c_float, POINTER, byref
78 from qubx.fast.fast_utils import qubfast as qubidl
79 c_int_p = POINTER(c_int)
80 c_uint_p = POINTER(c_uint)
81 c_float_p = POINTER(c_float)
82 c_double_p = POINTER(c_double)
83
84 import numpy
85 import sys
86 from itertools import izip
87
88 qubidl.qubidl_create.argtypes = (c_double,)
89 qubidl.qubidl_create.restype = c_void_p
90 qubidl.qubidl_destroy.argtypes = (c_void_p,)
91 qubidl.qubidl_destroy.restype = None
92
93 qubidl.qubidl_get_sampling.argtypes = (c_void_p,)
94 qubidl.qubidl_get_sampling.restype = c_double
95 qubidl.qubidl_set_sampling.argtypes = (c_void_p, c_double)
96 qubidl.qubidl_set_sampling.restype = None
97
98 qubidl.qubidl_get_seg_count.argtypes = (c_void_p,)
99 qubidl.qubidl_get_seg_count.restype = c_int
100 qubidl.qubidl_get_seg_first.argtypes = (c_void_p, c_int)
101 qubidl.qubidl_get_seg_first.restype = c_int
102 qubidl.qubidl_get_seg_last.argtypes = (c_void_p, c_int)
103 qubidl.qubidl_get_seg_last.restype = c_int
104
105 qubidl.qubidl_count_dwells.argtypes = (c_void_p, c_int, c_int, c_int)
106 qubidl.qubidl_count_dwells.restype = c_int
107 qubidl.qubidl_get_dwells.argtypes = (c_void_p, c_int, c_int, c_int, c_int_p, c_int_p, c_int_p, c_float_p)
108 qubidl.qubidl_get_dwells.restype = c_int
109 qubidl.qubidl_set_dwells.argtypes = (c_void_p, c_int, c_int_p, c_int_p, c_int_p)
110 qubidl.qubidl_set_dwells.restype = None
111 qubidl.qubidl_sample_dwells.argtypes = (c_void_p, c_int, c_int, c_double_p, c_int, c_float_p, c_float_p)
112 qubidl.qubidl_sample_dwells.restype = None
113 qubidl.qubidl_sample_dwells_classes.argtypes = (c_void_p, c_int, c_int, c_double_p, c_int, c_float_p, c_float_p, c_uint_p)
114 qubidl.qubidl_sample_dwells_classes.restype = None
115 qubidl.qubidl_sample_dwells_std.argtypes = (c_void_p, c_int, c_int, c_float_p, c_float_p, c_int, c_float_p, c_float_p)
116 qubidl.qubidl_sample_dwells_std.restype = None
117 qubidl.qubidl_count_dwells_and_gaps.argtypes = (c_void_p, c_int, c_int)
118 qubidl.qubidl_count_dwells_and_gaps.restype = c_int
119 qubidl.qubidl_get_dwells_and_gaps.argtypes = (c_void_p, c_int, c_int, c_int_p, c_int_p, c_int_p, c_float_p)
120 qubidl.qubidl_get_dwells_and_gaps.restype = c_int
121
122 qubidl.qubidl_clear.argtypes = (c_void_p,)
123 qubidl.qubidl_clear.restype = None
124 qubidl.qubidl_add_seg.argtypes = (c_void_p, c_int, c_int)
125 qubidl.qubidl_add_seg.restype = None
126 qubidl.qubidl_erase.argtypes = (c_void_p, c_int, c_int)
127 qubidl.qubidl_erase.restype = None
128 qubidl.qubidl_join.argtypes = (c_void_p, c_int, c_int)
129 qubidl.qubidl_join.restype = None
130
131 qubidl.qubidl_begin.argtypes = (c_void_p,)
132 qubidl.qubidl_begin.restype = c_void_p
133 qubidl.qubidl_end.argtypes = (c_void_p,)
134 qubidl.qubidl_end.restype = c_void_p
135 qubidl.qubidl_find.argtypes = (c_void_p, c_int)
136 qubidl.qubidl_find.restype = c_void_p
137 qubidl.qubidl_iter_clone.argtypes = (c_void_p,)
138 qubidl.qubidl_iter_clone.restype = c_void_p
139 qubidl.qubidl_iter_destroy.argtypes = (c_void_p,)
140 qubidl.qubidl_iter_destroy.restype = None
141 qubidl.qubidl_iter_valid.argtypes = (c_void_p,)
142 qubidl.qubidl_iter_valid.restype = c_int
143 qubidl.qubidl_iter_equals.argtypes = (c_void_p, c_void_p)
144 qubidl.qubidl_iter_equals.restype = c_int
145 qubidl.qubidl_iter_segindex.argtypes = (c_void_p,)
146 qubidl.qubidl_iter_segindex.restype = c_int
147 qubidl.qubidl_iter_segfirst.argtypes = (c_void_p,)
148 qubidl.qubidl_iter_segfirst.restype = c_int
149 qubidl.qubidl_iter_read.argtypes = (c_void_p, c_int_p, c_int_p, c_int_p)
150 qubidl.qubidl_iter_read.restype = None
151 qubidl.qubidl_iter_next.argtypes = (c_void_p,)
152 qubidl.qubidl_iter_next.restype = None
153 qubidl.qubidl_iter_prev.argtypes = (c_void_p,)
154 qubidl.qubidl_iter_prev.restype = None
155
156
159 """Creates a new zero-length Idealization.
160 @param sampling: shortest measured duration, usually in milliseconds
161 """
162 self.idl = qubidl.qubidl_create(sampling)
164 qubidl.qubidl_destroy(self.idl)
166 """Returns the class at sample key."""
167 iter = self.find(int(key))
168 if iter:
169 return iter.read()[2]
170 else:
171 return -1
173 """Returns sampling, usually in milliseconds."""
174 return qubidl.qubidl_get_sampling(self.idl)
176 """Sets sampling, usually in milliseconds."""
177 qubidl.qubidl_set_sampling(self.idl, sampling)
178 sampling = property(lambda self: self.get_sampling(), lambda self, x: self.set_sampling(x))
180 """Returns the number of segments."""
181 return qubidl.qubidl_get_seg_count(self.idl)
183 """Returns the first sample index in segment i."""
184 return qubidl.qubidl_get_seg_first(self.idl, i)
186 """Returns the last sample index in segment i."""
187 return qubidl.qubidl_get_seg_last(self.idl, i)
189 """
190 Returns the numer of dwells between sample f and sample l, inclusive.
191 If (not fragments): doesn't count dwells which extend beyond the bounds.
192 """
193 return qubidl.qubidl_count_dwells(self.idl, f, l, fragments)
194 - def get_dwells_into(self, f, l, fragments, firsts, lasts, classes, durations):
195 """Copies the dwells between samples f and l, inclusive, into numpy arrays.
196
197 @type firsts: numpy.array(dtype=int32)
198 @type lasts: numpy.array(dtype=int32)
199 @type classes: numpy.array(dtype=int32)
200 @type durations: numpy.array(dtype=float32) or None
201 """
202 qubidl.qubidl_get_dwells(self.idl, f, l, fragments, firsts, lasts, classes, durations)
203 - def get_dwells(self, f, l, fragments, get_durations=False):
204 """Returns the dwells between samples f and l, inclusive, as numpy arrays
205 @return: firsts, lasts, classes[, durations]
206 """
207 count = self.count_dwells(f, l, fragments)
208 firsts = numpy.zeros(shape=(count,), dtype='int32')
209 lasts = numpy.zeros(shape=(count,), dtype='int32')
210 classes = numpy.zeros(shape=(count,), dtype='int32')
211 if get_durations:
212 durations = numpy.zeros(shape=(count,), dtype='float32')
213 self.get_dwells_into(f, l, fragments, firsts.ctypes.data_as(c_int_p),
214 lasts.ctypes.data_as(c_int_p),
215 classes.ctypes.data_as(c_int_p),
216 durations.ctypes.data_as(c_float_p))
217 return firsts, lasts, classes, durations
218 else:
219 self.get_dwells_into(f, l, fragments, firsts.ctypes.data_as(c_int_p),
220 lasts.ctypes.data_as(c_int_p),
221 classes.ctypes.data_as(c_int_p),
222 c_float_p())
223 return firsts, lasts, classes
225 """
226 Returns the numer of dwells between sample f and sample l, inclusive.
227 """
228 return qubidl.qubidl_count_dwells_and_gaps(self.idl, f, l)
230 """Copies the dwells between samples f and l, inclusive, into numpy arrays; cls=-1 between events.
231
232 @type firsts: numpy.array(dtype=int32)
233 @type lasts: numpy.array(dtype=int32)
234 @type classes: numpy.array(dtype=int32)
235 @type durations: numpy.array(dtype=float32) or None
236 """
237 qubidl.qubidl_get_dwells_and_gaps(self.idl, f, l, firsts, lasts, classes, durations)
239 """Returns the dwells between samples f and l, inclusive, as numpy arrays
240 @return: firsts, lasts, classes[, durations]
241 """
242 count = self.count_dwells_and_gaps(f, l)
243 firsts = numpy.zeros(shape=(count,), dtype='int32')
244 lasts = numpy.zeros(shape=(count,), dtype='int32')
245 classes = numpy.zeros(shape=(count,), dtype='int32')
246 if get_durations:
247 durations = numpy.zeros(shape=(count,), dtype='float32')
248 self.get_dwells_and_gaps_into(f, l, firsts.ctypes.data_as(c_int_p),
249 lasts.ctypes.data_as(c_int_p),
250 classes.ctypes.data_as(c_int_p),
251 durations.ctypes.data_as(c_float_p))
252 return firsts, lasts, classes, durations
253 else:
254 self.get_dwells_and_gaps_into(f, l, firsts.ctypes.data_as(c_int_p),
255 lasts.ctypes.data_as(c_int_p),
256 classes.ctypes.data_as(c_int_p),
257 c_float_p())
258 return firsts, lasts, classes
259 - def set_dwells(self, count, firsts, lasts, classes):
260 """Writes dwells to the idealization."""
261 qubidl.qubidl_set_dwells(self.idl, count, firsts.ctypes.data_as(c_int_p),
262 lasts.ctypes.data_as(c_int_p), classes.ctypes.data_as(c_int_p))
263 - def sample_dwells(self, f, l, amps, Nsample, lows, highs, class_bits=None):
264 """
265 Renders the dwells between samples f and l, inclusive, into resampled arrays lo and hi, suitable for display.
266 Samples with no dwells (all class < 0) are not modified, so if you fill lo or hi with a sentry value, you can skip blank samples.
267 class_bits[i] & (1 << c) if class c occurs during sample i
268 """
269 qubidl.qubidl_sample_dwells_classes(self.idl, f, l, amps.ctypes.data_as(c_double_p),
270 Nsample, lows.ctypes.data_as(c_float_p), highs.ctypes.data_as(c_float_p),
271 (not (class_bits is None)) and class_bits.ctypes.data_as(c_uint_p) or None)
273 """
274 Renders the dwells between samples f and l, inclusive, into resampled arrays lo and hi, suitable for display.
275 Samples with no dwells (all class < 0) are not modified, so if you fill lo or hi with a sentry value, you can skip blank samples.
276 """
277 qubidl.qubidl_sample_dwells_std(self.idl, f, l, amps.ctypes.data_as(c_float_p), stds.ctypes.data_as(c_float_p),
278 Nsample, lows.ctypes.data_as(c_float_p), highs.ctypes.data_as(c_float_p))
280 """
281 Returns the numer of dwells between sample f and sample l, inclusive;
282 unlike count_dwells, counts each un-idealized region as a dwell (of class -1).
283 @param exclusion: an L{Idealization} with the same segmentation
284 @param excl_class: class in exclusion marking where to overwrite output with class -2
285 """
286 count = 0
287 for ef, el, ec in izip(*exclusion.get_dwells(f, l, True)):
288 if ec != excl_class:
289 count += qubidl.qubidl_count_dwells_and_gaps(self.idl, ef, el)
290 else:
291 count += 1
292 return count
294 """
295 Copies the dwells between samples f and l, inclusive, into numpy arrays;
296 unlike get_dwells_into, outputs un-idealized regions as class -1.
297
298 @param exclusion: an L{Idealization} with the same segmentation
299 @param excl_class: class in exclusion marking where to overwrite output with class -2
300
301 @type firsts: numpy.array(dtype=int32)
302 @type lasts: numpy.array(dtype=int32)
303 @type classes: numpy.array(dtype=int32)
304 @type durations: numpy.array(dtype=float32) or None
305 """
306 count = 0
307 for ef, el, ec in izip(*exclusion.get_dwells(f, l, True)):
308 if ec != excl_class:
309 if not (durations is None):
310 durs = durations[count:].ctypes.data_as(c_float_p)
311 else:
312 durs = c_float_p()
313 count += qubidl.qubidl_get_dwells_and_gaps(self.idl, ef, el,
314 firsts[count:].ctypes.data_as(c_int_p),
315 lasts[count:].ctypes.data_as(c_int_p),
316 classes[count:].ctypes.data_as(c_int_p),
317 durs)
318 else:
319 firsts[count] = ef
320 lasts[count] = el
321 classes[count] = -2
322 count += 1
323 if not (durations is None):
324 durations[:] = self.get_sampling() * (lasts - firsts + 1)
326 """
327 Returns the dwells between samples f and l, inclusive, as numpy arrays;
328 unlike get_dwells, outputs un-idealized regions as class -1.
329
330 @param exclusion: an L{Idealization} with the same segmentation
331 @param excl_class: class in exclusion marking where to overwrite output with class -2
332 @return: firsts, lasts, classes[, durations]
333 """
334 count = self.count_dwells_excluding(exclusion, excl_class, f, l)
335 firsts = numpy.zeros(shape=(count,), dtype='int32')
336 lasts = numpy.zeros(shape=(count,), dtype='int32')
337 classes = numpy.zeros(shape=(count,), dtype='int32')
338 if get_durations:
339 durations = numpy.zeros(shape=(count,), dtype='float32')
340 self.get_dwells_excluding_into(exclusion, excl_class, f, l, firsts, lasts, classes, durations)
341 return firsts, lasts, classes, durations
342 else:
343 self.get_dwells_excluding_into(exclusion, excl_class, f, l, firsts, lasts, classes)
344 return firsts, lasts, classes
346 """Overwrites the idealization between samples f and l, inclusive, with class c."""
347 F = c_int(f)
348 L = c_int(l)
349 C = c_int(c)
350 qubidl.qubidl_set_dwells(self.idl, 1, byref(F), byref(L), byref(C))
352 """Clears all segments' idealization to class -1."""
353 qubidl.qubidl_clear(self.idl)
355 """Adds a segment, with inclusive sample bounds. For best results, use f = total sample count so far; l = f+seg_length-1."""
356 qubidl.qubidl_add_seg(self.idl, f, l)
358 """Clears all dwells (or fragments) between samples f and l inclusive, by setting them to class -1."""
359 qubidl.qubidl_erase(self.idl, f, l)
360 - def join(self, f, l):
361 """Extends the class at sample f through to sample l."""
362 qubidl.qubidl_join(self.idl, f, l)
364 """Returns an L{IdlIter} pointing to the first (partial) dwell."""
365 return IdlIter(qubidl.qubidl_begin(self.idl))
367 """Returns an L{IdlIter} pointing past the last (partial) dwell."""
368 return IdlIter(qubidl.qubidl_end(self.idl))
370 """Returns an L{IdlIter} pointing to the (partial) dwell at sample f."""
371 return IdlIter(qubidl.qubidl_find(self.idl, f))
372
374 __slots__ = ['iter', 'readinto']
376 """Creates an IdlIter; If iter is an IdlIter, this creates a clone."""
377 if isinstance(iter, IdlIter):
378 self.iter = qubidl.qubidl_iter_clone(iter.iter)
379 else:
380 self.iter = iter
381 self.readinto = None
383 qubidl.qubidl_iter_destroy(self.iter)
385 """bool(iter) is True if iter points to a (partial) dwell, not past the end."""
386 return qubidl.qubidl_iter_valid(self.iter)
388 """Returns 0 if other iterator points to the same (partial) dwell."""
389 if qubidl.qubidl_iter_equals(self.iter, other.iter):
390 return 0
391 else:
392 return -1
394 """Returns the segment index of the current dwell."""
395 return qubidl.qubidl_iter_segindex(self.iter)
397 """Returns the first sample index in the current dwell's segment."""
398 return qubidl.qubidl_iter_segfirst(self.iter)
400 """Returns the first, last, and class indices of the current (partial) dwell."""
401 if self.readinto is None:
402 f = c_int()
403 l = c_int()
404 c = c_int()
405 self.readinto = (f, l, c)
406 else:
407 f, l, c = self.readinto
408 qubidl.qubidl_iter_read(self.iter, byref(f), byref(l), byref(c))
409 return (f.value, l.value, c.value)
411 """Moves the iterator to the next (partial) dwell."""
412 qubidl.qubidl_iter_next(self.iter)
414 """Moves the iterator to the previous (partial) dwell."""
415 qubidl.qubidl_iter_prev(self.iter)
416
426 - def clear(self, seg_ix=None):
427 if seg_ix is None:
428 for i in xrange(len(self.segmeans)):
429 self.clear(i)
430 else:
431 self.set_dwell(self.get_seg_first(seg_ix), self.get_seg_last(seg_ix), -1)
432 self.segmeans[seg_ix] = numpy.zeros(shape=(1,), dtype='float32')
433 self.segstds[seg_ix] = numpy.zeros(shape=(1,), dtype='float32')
442 - def set_fit(self, firsts, lasts, means, stds=None):
443 if len(firsts) == 0:
444 return
445 seg_ix = self.segmentation[firsts[0]]
446 if seg_ix != self.segmentation[lasts[-1]]:
447 raise ValueError('All points must be in the same segment')
448 base = len(self.segmeans[seg_ix])
449 self.segmeans[seg_ix] = numpy.hstack((self.segmeans[seg_ix], means))
450 if not (stds is None):
451 self.segstds[seg_ix] = numpy.hstack((self.segstds[seg_ix], stds))
452 else:
453 self.segstds[seg_ix] = numpy.hstack((self.segstds[seg_ix], numpy.zeros(shape=(len(means),), dtype='float32')))
454 classes = numpy.arange(base, base+len(firsts), dtype='int32')
455 self.set_dwells(len(firsts), firsts, lasts, classes)
456 - def get_samples_into(self, first, last, arr, cls_values=None, arr_stds=None, cls_stds=None):
457 seg_ix = self.segmentation[first]
458 if seg_ix != self.segmentation[last]:
459 raise ValueError('All points must be in the same segment')
460 ff, ll, cc = self.get_dwells(first, last, True, False)
461 if not (cls_values is None):
462 means = cls_values
463 else:
464 means = self.segmeans[seg_ix]
465 if cls_stds is None:
466 stds = self.segstds[seg_ix]
467 else:
468 stds = cls_stds
469 for f, l, c in izip(ff, ll, cc):
470 arr[f-first:l-first+1] = means[c]
471 if not (arr_stds is None):
472 arr_stds[f-first:l-first+1] = stds[c]
473
474
477 for k,v in kw.iteritems():
478 self.__dict__[k] = v
479
481 try:
482 return self.__dict__[name]
483 except:
484 return None
485
488 self.idl = Idealization(sampling)
489 self.__construct_record = construct_record
490 self.__next = 0
491 self.__last = -1
492 self.records = {-1 : construct_record()}
493
494 sampling = property(lambda self: self.get_sampling(), lambda self, x: self.set_sampling(x))
496 return self.records[self.idl[key]]
506 ff, ll, cc = self.idl.get_dwells(f, l, fragments)
507 valid = [i for i,c in enumerate(cc) if (c >= 0)]
508 records = [self.records[cc[v]] for v in valid]
509 return records, [ff[v] for v in valid], [ll[v] for v in valid]
511 self.idl.add_seg(f, l)
512 self.__last = l
514 ix = self.__next
515 while ix in self.records:
516 ix = (ix + 1) % 2147483648
517 self.__next = (ix + 1) % 2147483648
518 rec = self.__construct_record()
519 self.records[ix] = rec
520 self.idl.set_dwell(f, l, ix)
521 return rec
523 self.idl.clear()
524 self.records = {}
525 self.__next = 0
526 - def erase(self, f=0, l=None):
539
541 - def __init__(self, iter, idl_recs=None):
564 f, l, c = self.iter.read()
565 self.record = self.idl_recs.records[c]
566 return self.record
567
568 if __name__ == '__main__':
569 import traceback
570 import linecache
571 import sys
572
573
575 if event == "line":
576 lineno = frame.f_lineno
577 filename = frame.f_globals["__file__"]
578 if filename == "<stdin>":
579 filename = "<stdin> "
580 if (filename.endswith(".pyc") or
581 filename.endswith(".pyo")):
582 filename = filename[:-1]
583 name = frame.f_globals["__name__"]
584 line = linecache.getline(filename, lineno)
585 tracelog.write("%s:%s: %s\n" % (name, lineno, line.rstrip()))
586 return traceit
587
588
589 idl = Idealization(0.1)
590 idl.add_seg(0, 255)
591 idl.set_dwell(10, 20, 2)
592 iter = idl.begin()
593 while iter:
594 print iter.read()
595 iter.next()
596 print
597
598 excl = Idealization(0.1)
599 excl.add_seg(0, 255)
600 excl.set_dwell(0, 255, 1)
601 print
602 for f, l, c, d in izip(*idl.get_dwells_excluding(excl, 0, 0, 255, get_durations=True)):
603 print "%10i%10i%5i%16.9g" % (f,l,c,d)
604 print
605 excl.set_dwell(15, 24, 0)
606 for f, l, c, d in izip(*idl.get_dwells_excluding(excl, 0, 0, 255, get_durations=True)):
607 print "%10i%10i%5i%16.9g" % (f,l,c,d)
608
609 del excl
610 del iter
611 del idl
612