3 (:export
#:with-test
#:report-test-status
#:*failures
*
4 #:really-invoke-debugger
5 #:*break-on-failure
* #:*break-on-expected-failure
*
8 #:make-kill-thread
#:make-join-thread
11 #:map-optimization-quality-combinations
12 #:map-optimize-declarations
14 ;; CHECKED-COMPILE and friends
15 #:checked-compile
#:checked-compile-and-assert
16 #:checked-compile-capturing-source-paths
17 #:checked-compile-condition-source-paths
19 #:runtime
#:split-string
#:shuffle
))
21 (in-package :test-util
)
23 (defvar *test-count
* 0)
24 (defvar *test-file
* nil
)
25 (defvar *failures
* nil
)
26 (defvar *break-on-failure
* nil
)
27 (defvar *break-on-expected-failure
* nil
)
29 (defvar *threads-to-kill
*)
30 (defvar *threads-to-join
*)
32 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
35 (sb-posix:putenv
(format nil
"SBCL_MACHINE_TYPE=~A" (machine-type)))
36 (sb-posix:putenv
(format nil
"SBCL_SOFTWARE_TYPE=~A" (software-type)))
39 (defun make-kill-thread (&rest args
)
40 (let ((thread (apply #'sb-thread
:make-thread args
)))
41 #-win32
;; poor thread interruption on safepoints
42 (when (boundp '*threads-to-kill
*)
43 (push thread
*threads-to-kill
*))
47 (defun make-join-thread (&rest args
)
48 (let ((thread (apply #'sb-thread
:make-thread args
)))
49 (when (boundp '*threads-to-join
*)
50 (push thread
*threads-to-join
*))
53 (defun log-msg (&rest args
)
54 (apply #'format
*trace-output
* "~&::: ~@?~%" args
)
55 (force-output *trace-output
*))
57 (defun log-msg/non-pretty
(&rest args
)
58 (let ((*print-pretty
* nil
))
59 (apply #'log-msg args
)))
61 (defun run-test (test-function name fails-on
)
63 (let (#+sb-thread
(threads (sb-thread:list-all-threads
))
64 (*threads-to-join
* nil
)
65 (*threads-to-kill
* nil
))
66 (handler-bind ((error (lambda (error)
67 (if (expected-failure-p fails-on
)
68 (fail-test :expected-failure name error
)
69 (fail-test :unexpected-failure name error
))
70 (return-from run-test
))))
71 ;; Non-pretty is for cases like (with-test (:name (let ...)) ...
72 (log-msg/non-pretty
"Running ~S" name
)
73 (funcall test-function
)
75 (let ((any-leftover nil
))
76 (dolist (thread *threads-to-join
*)
77 (ignore-errors (sb-thread:join-thread thread
)))
78 (dolist (thread *threads-to-kill
*)
79 (ignore-errors (sb-thread:terminate-thread thread
)))
80 (setf threads
(union (union *threads-to-kill
*
83 #+(and sb-safepoint-strictly
(not win32
))
84 (dolist (thread (sb-thread:list-all-threads
))
85 (when (typep thread
'sb-thread
:signal-handling-thread
)
86 (ignore-errors (sb-thread:join-thread thread
))))
87 (dolist (thread (sb-thread:list-all-threads
))
88 (unless (or (not (sb-thread:thread-alive-p thread
))
89 (eql (the sb-thread
:thread thread
)
90 sb-thread
:*current-thread
*)
91 (member thread threads
)
92 (sb-thread:thread-ephemeral-p thread
))
93 (setf any-leftover thread
)
95 (ignore-errors (sb-thread:terminate-thread thread
))))
97 (fail-test :leftover-thread name any-leftover
)
98 (return-from run-test
)))
99 (if (expected-failure-p fails-on
)
100 (fail-test :unexpected-success name nil
)
101 ;; Non-pretty is for cases like (with-test (:name (let ...)) ...
102 (log-msg/non-pretty
"Success ~S" name
)))))
104 (defmacro with-test
((&key fails-on broken-on skipped-on name
)
106 (flet ((name-ok (x y
)
109 (symbol (let ((package (symbol-package x
)))
111 (eql package
(find-package "CL"))
112 (eql package
(find-package "KEYWORD"))
113 (eql (mismatch "SB-" (package-name package
)) 3))))
115 (unless (tree-equal name name
:test
#'name-ok
)
116 (error "test name must be all-keywords: ~S" name
)))
118 ((broken-p broken-on
)
121 (fail-test :skipped-broken
',name
"Test broken on this platform")))
122 ((skipped-p skipped-on
)
125 (fail-test :skipped-disabled
',name
"Test disabled for this combination of platform and features")))
127 `(run-test (lambda ()
132 (defun report-test-status ()
133 (with-standard-io-syntax
134 (with-open-file (stream "test-status.lisp-expr"
136 :if-exists
:supersede
)
137 (format stream
"~s~%" *failures
*))))
140 (unless (eq *test-file
* *load-pathname
*)
141 (setf *test-file
* *load-pathname
*)
142 (setf *test-count
* 0))
145 (defun really-invoke-debugger (condition)
146 (with-simple-restart (continue "Continue")
147 (let ((*invoke-debugger-hook
* *invoke-debugger-hook
*))
149 (invoke-debugger condition
))))
151 (defun fail-test (type test-name condition
)
152 (if (stringp condition
)
153 (log-msg "~@<~A ~S ~:_~A~:>"
154 type test-name condition
)
155 (log-msg "~@<~A ~S ~:_due to ~S: ~4I~:_\"~A\"~:>"
156 type test-name
(type-of condition
) condition
))
157 (push (list type
*test-file
* (or test-name
*test-count
*))
159 (unless (stringp condition
)
160 (when (or (and *break-on-failure
*
161 (not (eq type
:expected-failure
)))
162 *break-on-expected-failure
*)
163 (really-invoke-debugger condition
))))
165 (defun expected-failure-p (fails-on)
166 (sb-impl::featurep fails-on
))
168 (defun broken-p (broken-on)
169 (sb-impl::featurep broken-on
))
171 (defun skipped-p (skipped-on)
172 (sb-impl::featurep skipped-on
))
174 ;;;; MAP-{OPTIMIZATION-QUALITY-COMBINATIONS,OPTIMIZE-DECLARATIONS}
176 (sb-int:defconstant-eqx
+optimization-quality-names
+
177 '(speed safety debug compilation-speed space
) #'equal
)
179 (sb-int:defconstant-eqx
+optimization-quality-keywords
+
180 '(:speed
:safety
:debug
:compilation-speed
:space
) #'equal
)
182 (deftype optimization-quality-range-designator
()
183 '(or (eql nil
) ; skip quality
184 (integer 0 3) ; one value
185 (cons (or (eql nil
) (integer 0 3)) list
) ; list of values, nil means skip
186 (eql t
))) ; all values
188 ;;; Call FUNCTION with the specified combinations of optimization
191 ;;; MAP-OPTIMIZATION-QUALITY-COMBINATIONS calls FUNCTION with keyword
192 ;;; argument thus expecting a lambda list of the form
194 ;;; (&key speed safety debug compilation-speed space)
196 ;;; or any subset compatible with the generated combinations.
198 ;;; MAP-OPTIMIZE-DECLARATIONS calls FUNCTION with a list intended to
199 ;;; be spliced into a DECLARE form like this:
201 ;;; (lambda (quality-values)
202 ;;; `(declare (optimize ,@quality-values)))
204 ;;; The set of combinations is controlled via keyword arguments
206 ;;; :FILTER FILTER-FUNCTION
207 ;;; A function that should be called with optimization quality
208 ;;; keyword arguments and whose return value controls whether
209 ;;; FUNCTION should be called for the given combination.
211 ;;; (:SPEED | :SAFETY | :DEBUG | :COMPILATION-SPEED | :SPACE) SPEC
212 ;;; Specify value range for the given optimization quality. SPEC
216 ;;; Omit the quality.
220 ;;; Use the specified value for the quality.
222 ;;; (NIL | (INTEGER 0 3))*
223 ;;; Generate the specified values. A "value" of NIL omits the
224 ;;; quality from the combination.
227 ;;; Generate all values (0, 1, 2, 3) for the quality.
228 (declaim (ftype (function #.
`(function
230 ,@(mapcar #'list
+optimization-quality-keywords
+
231 '#1=(optimization-quality-range-designator .
#1#))
233 map-optimization-quality-combinations
234 map-optimize-declarations
))
235 (defun map-optimization-quality-combinations
236 (function &key
(speed t
) (safety t
) (debug t
) (compilation-speed t
) (space t
)
238 (labels ((map-quantity-values (values thunk
)
241 (dotimes (i 4) (funcall thunk i
)))
243 (map nil thunk values
))
245 (funcall thunk values
))))
246 (one-quality (qualities specs values
)
247 (let ((quality (first qualities
))
248 (spec (first specs
)))
251 (when (or (not filter
) (apply filter values
))
252 (apply function values
)))
254 (one-quality (rest qualities
) (rest specs
) values
))
259 (one-quality (rest qualities
) (rest specs
)
261 (list* quality value values
)
263 (one-quality +optimization-quality-keywords
+
264 (list speed safety debug compilation-speed space
)
267 (defun map-optimize-declarations
269 &key speed safety debug compilation-speed space filter
)
270 (declare (ignore speed safety debug compilation-speed space filter
))
271 (apply #'map-optimization-quality-combinations
272 (lambda (&rest args
&key
&allow-other-keys
)
273 (funcall function
(loop for name in
+optimization-quality-names
+
274 for keyword in
+optimization-quality-keywords
+
275 for value
= (getf args keyword
)
276 when value collect
(list name value
))))
279 (defun expand-optimize-specifier (specifier)
284 '(:speed nil
:safety nil
:debug nil
:compilation-speed nil
:space nil
))
286 '(:speed
1 :safety
1 :debug
1 :compilation-speed
1 :space
1))
287 ((eql :maximally-safe
)
288 (list :filter
(lambda (&key safety
&allow-other-keys
)
291 (list :filter
(lambda (&key speed safety
&allow-other-keys
)
292 (and (> safety
0) (>= safety speed
)))))
294 '(:compilation-speed
1 :space
1))
295 ((eql :quick
/incomplete
)
296 '(:compilation-speed nil
:space nil
))
300 (defun map-optimization-quality-combinations* (function specifier
)
301 (apply #'map-optimization-quality-combinations
302 function
(expand-optimize-specifier specifier
)))
304 (defun map-optimize-declarations* (function specifier
)
305 (apply #'map-optimize-declarations
306 function
(expand-optimize-specifier specifier
)))
310 (defun prepare-form (thing &key optimize
)
313 (error "~@<~S is a function, not a form.~@:>" thing
))
316 ((typep thing
'(cons (eql sb-int
:named-lambda
)))
317 `(,@(subseq thing
0 3)
318 (declare (optimize ,@optimize
))
320 ((typep thing
'(cons (eql lambda
)))
321 `(,(first thing
) ,(second thing
)
322 (declare (optimize ,@optimize
))
325 (error "~@<Cannot splice ~A declaration into forms other than ~
326 ~{~S~#[~; and ~:;, ~]~}: ~S.~@:>"
327 'optimize
'(lambda sb-int
:named-lambda
) thing
))))
329 (defun compile-capturing-output-and-conditions
330 (form &key name condition-transform
)
334 (compiler-errors '())
335 (error-output (make-string-output-stream)))
336 (flet ((maybe-transform (condition)
337 (if condition-transform
338 (funcall condition-transform condition
)
340 (handler-bind ((sb-ext:compiler-note
342 (push (maybe-transform condition
) notes
)
343 (muffle-warning condition
)))
346 (push (maybe-transform condition
) style-warnings
)
347 (muffle-warning condition
)))
350 (push (maybe-transform condition
) warnings
)
351 (muffle-warning condition
)))
354 (push (maybe-transform condition
) compiler-errors
))))
355 (multiple-value-bind (function warnings-p failure-p
)
356 (let ((*error-output
* error-output
))
358 (values function warnings-p failure-p
359 warnings style-warnings notes compiler-errors
362 (defun print-form-and-optimize (stream form-and-optimize
&optional colonp atp
)
363 (declare (ignore colonp atp
))
364 (destructuring-bind (form . optimize
) form-and-optimize
365 (format stream
"~@:_~@:_~2@T~S~@:_~@:_~
367 default optimization policy~
369 ~:*~@:_~@:_~2@T~S~@:_~@:_~
374 (defun print-signaled-conditions (stream conditions
&optional colonp atp
)
375 (declare (ignore colonp atp
))
376 (format stream
"~{~@:_~@:_~{~/sb-ext:print-symbol-with-prefix/: ~A~}~}"
377 (mapcar (lambda (condition)
378 (list (type-of condition
) condition
))
381 ;;; Compile FORM capturing and muffling all [style-]warnings and notes
382 ;;; and return six values: 1) the compiled function 2) a Boolean
383 ;;; indicating whether compilation failed 3) a list of warnings 4) a
384 ;;; list of style-warnings 5) a list of notes 6) a list of
385 ;;; SB-C:COMPILER-ERROR conditions.
387 ;;; An error can be signaled when COMPILE indicates failure as well as
388 ;;; in case [style-]warning or note conditions are signaled. The
389 ;;; keyword parameters
390 ;;; ALLOW-{FAILURE,[STYLE-]WARNINGS,NOTES,COMPILER-ERRORS} control
391 ;;; this behavior. All but ALLOW-NOTES default to NIL.
394 ;;; ALLOW-{FAILURE,[STYLE-]WARNINGS,NOTES,COMPILER-ERRORS} keyword
395 ;;; parameters are interpreted as type specifiers restricting the
396 ;;; allowed conditions of the respective kind.
398 ;;; When supplied, the value of CONDITION-TRANSFORM has to be a
399 ;;; function of one argument, the condition currently being
400 ;;; captured. The returned value is captured and later returned in
401 ;;; place of the condition.
402 (defun checked-compile (form
409 (allow-compiler-errors allow-failure
)
412 (sb-int:binding
* ((prepared-form (prepare-form form
:optimize optimize
))
413 ((function nil failure-p
414 warnings style-warnings notes compiler-errors
416 (compile-capturing-output-and-conditions
417 prepared-form
:name name
:condition-transform condition-transform
)))
418 (labels ((fail (kind conditions
&optional allowed-type
)
419 (error "~@<Compilation of~/test-util::print-form-and-optimize/ ~
420 signaled ~A~P:~/test-util::print-signaled-conditions/~
421 ~@[~@:_~@:_Allowed type is ~
422 ~/sb-ext:print-type-specifier/.~]~@:>"
423 (cons form optimize
) kind
(length conditions
) conditions
425 (check-conditions (kind conditions allow
)
428 (let ((offenders (remove-if (lambda (condition)
429 (typep condition allow
))
432 (fail kind offenders allow
))))
434 (fail kind conditions
)))))
436 (when (and (not allow-failure
) failure-p
)
437 (let ((output (get-output-stream-string error-output
)))
438 (error "~@<Compilation of~/test-util::print-form-and-optimize/ ~
439 failed~@[ with output~
440 ~@:_~@:_~2@T~@<~@;~A~:>~@:_~@:_~].~@:>"
441 (cons form optimize
) (when (plusp (length output
)) output
))))
443 (check-conditions "warning" warnings allow-warnings
)
444 (check-conditions "style-warning" style-warnings allow-style-warnings
)
445 (check-conditions "note" notes allow-notes
)
446 (check-conditions "compiler-error" compiler-errors allow-compiler-errors
)
448 ;; Since we may have prevented warnings from being taken
449 ;; into account for FAILURE-P by muffling them, adjust the
450 ;; second return value accordingly.
451 (values function
(when (or failure-p warnings
) t
)
452 warnings style-warnings notes compiler-errors
))))
454 (defun print-arguments (stream arguments
&optional colonp atp
)
455 (declare (ignore colonp atp
))
459 with arguments~@:_~@:_~
460 ~2@T~@<~{~S~^~@:_~}~:>~@:_~@:_~
464 (defun call-capturing-values-and-conditions (function &rest args
)
468 (handler-bind ((condition (lambda (condition)
469 (push condition conditions
)
472 (muffle-warning condition
))
475 (setf values
(multiple-value-list (apply function args
)))))
476 (values values
(nreverse conditions
))))
478 (defun %checked-compile-and-assert-one-case
479 (form optimize function args-thunk expected test allow-conditions
)
480 (let ((args (multiple-value-list (funcall args-thunk
))))
481 (flet ((failed-to-signal (expected-type)
482 (error "~@<Calling the result of compiling~
483 ~/test-util::print-form-and-optimize/ ~
484 ~/test-util::print-arguments/~
485 returned normally instead of signaling a ~
487 ~/sb-ext:print-type-specifier/.~@:>"
488 (cons form optimize
) args expected-type
))
489 (signaled-unexpected (conditions)
490 (error "~@<Calling the result of compiling~
491 ~/test-util::print-form-and-optimize/ ~
492 ~/test-util::print-arguments/~
493 signaled unexpected condition~P~
494 ~/test-util::print-signaled-conditions/~
496 (cons form optimize
) args
(length conditions
) conditions
))
497 (returned-unexpected (values expected test
)
498 (error "~@<Calling the result of compiling~
499 ~/test-util::print-form-and-optimize/ ~
500 ~/test-util::print-arguments/~
501 returned values~@:_~@:_~
502 ~2@T~<~{~S~^~@:_~}~:>~@:_~@:_~
503 with is not ~S to~@:_~@:_~
504 ~2@T~<~{~S~^~@:_~}~:>~@:_~@:_~
506 (cons form optimize
) args
507 (list values
) test
(list expected
))))
508 (multiple-value-bind (values conditions
)
509 (apply #'call-capturing-values-and-conditions function args
)
511 ((cons (eql condition
) (cons t null
))
512 (let* ((expected-condition-type (second expected
))
513 (unexpected (remove-if (lambda (condition)
515 expected-condition-type
))
517 (expected (set-difference conditions unexpected
)))
520 (signaled-unexpected unexpected
))
522 (failed-to-signal expected-condition-type
)))))
524 (let ((expected (funcall expected
)))
527 (not (and allow-conditions
528 (every (lambda (condition)
529 (typep condition allow-conditions
))
531 (signaled-unexpected conditions
))
532 ((not (funcall test values expected
))
533 (returned-unexpected values expected test
))))))))))
535 (defun %checked-compile-and-assert-one-compilation
536 (form optimize other-checked-compile-args cases
)
537 (let ((function (apply #'checked-compile form
539 (list* :optimize optimize
540 other-checked-compile-args
)
541 other-checked-compile-args
))))
542 (loop for
(args-thunk values test allow-conditions
) in cases
543 do
(%checked-compile-and-assert-one-case
544 form optimize function args-thunk values test allow-conditions
))))
546 (defun %checked-compile-and-assert
(form checked-compile-args cases
)
547 (let ((optimize (getf checked-compile-args
:optimize
))
548 (other-args (loop for
(key value
) on checked-compile-args by
#'cddr
549 unless
(eq key
:optimize
)
550 collect key and collect value
)))
551 (map-optimize-declarations*
552 (lambda (&optional optimize
)
553 (%checked-compile-and-assert-one-compilation
554 form optimize other-args cases
))
557 ;;; Compile FORM using CHECKED-COMPILE, then call the resulting
558 ;;; function with arguments and assert expected return values
559 ;;; according to CASES.
561 ;;; Elements of CASES are of the form
563 ;;; ((&rest ARGUMENT-FORMS) VALUES-FORM &key TEST ALLOW-CONDITIONS)
565 ;;; where ARGUMENT-FORMS are evaluated to produce the arguments for
566 ;;; one call of the function and VALUES-FORM is evaluated to produce
567 ;;; the expected return values for that function call.
569 ;;; TEST is used to compare a list of the values returned by the
570 ;;; function call to the list of values obtained by calling
573 ;;; If supplied, the value of ALLOW-CONDITIONS is a type-specifier
574 ;;; indicating which conditions should be allowed (and ignored) during
575 ;;; the function call.
577 ;;; If VALUES-FORM is of the form
579 ;;; (CONDITION CONDITION-TYPE)
581 ;;; the function call is expected to signal the designated condition
582 ;;; instead of returning values. CONDITION-TYPE is evaluated.
584 ;;; The OPTIMIZE keyword parameter controls the optimization policies
585 ;;; (or policy) used when compiling FORM. The argument is interpreted
586 ;;; as described for MAP-OPTIMIZE-DECLARATIONS*.
588 ;;; The other keyword parameters, NAME and
589 ;;; ALLOW-{WARNINGS,STYLE-WARNINGS,NOTES}, behave as with
591 (defmacro checked-compile-and-assert
((&key name
597 (flet ((make-case-form (case)
598 (destructuring-bind (args values
&key
(test ''equal testp
)
601 (let ((conditionp (typep values
'(cons (eql condition
) (cons t null
)))))
602 (when (and testp conditionp
)
603 (sb-ext:with-current-source-form
(case)
604 (error "~@<Cannot use ~S with ~S ~S.~@:>"
606 `(list (lambda () (values ,@args
))
608 `(list 'condition
,(second values
))
609 `(lambda () (multiple-value-list ,values
)))
611 ,allow-conditions
)))))
612 `(%checked-compile-and-assert
613 ,form
(list :name
,name
614 :allow-warnings
,allow-warnings
615 :allow-style-warnings
,allow-style-warnings
616 :allow-notes
,allow-notes
618 (list ,@(mapcar #'make-case-form cases
)))))
620 ;;; Like CHECKED-COMPILE, but for each captured condition, capture and
621 ;;; later return a cons
623 ;;; (CONDITION . SOURCE-PATH)
625 ;;; instead. SOURCE-PATH is the path of the source form associated to
627 (defun checked-compile-capturing-source-paths (form &rest args
)
628 (labels ((context-source-path ()
629 (let ((context (sb-c::find-error-context nil
)))
630 (sb-c::compiler-error-context-original-source-path
632 (add-source-path (condition)
633 (cons condition
(context-source-path))))
634 (apply #'checked-compile form
:condition-transform
#'add-source-path
637 ;;; Similar to CHECKED-COMPILE, but allow compilation failure and
638 ;;; warnings and only return source paths associated to those
640 (defun checked-compile-condition-source-paths (form)
641 (let ((source-paths '()))
642 (labels ((context-source-path ()
643 (let ((context (sb-c::find-error-context nil
)))
644 (sb-c::compiler-error-context-original-source-path
646 (push-source-path (condition)
647 (declare (ignore condition
))
648 (push (context-source-path) source-paths
)))
649 (checked-compile form
652 :allow-style-warnings t
653 :condition-transform
#'push-source-path
))
654 (nreverse source-paths
)))
656 ;;; Repeat calling THUNK until its cumulated runtime, measured using
657 ;;; GET-INTERNAL-RUN-TIME, is larger than PRECISION. Repeat this
658 ;;; REPETITIONS many times and return the time one call to THUNK took
659 ;;; in seconds as a float, according to the minimum of the cumulated
660 ;;; runtimes over the repetitions.
661 ;;; This allows to easily measure the runtime of expressions that take
662 ;;; much less time than one internal time unit. Also, the results are
663 ;;; unaffected, modulo quantization effects, by changes to
664 ;;; INTERNAL-TIME-UNITS-PER-SECOND.
665 ;;; Taking the minimum is intended to reduce the error introduced by
666 ;;; garbage collections occurring at unpredictable times. The inner
667 ;;; loop doubles the number of calls to THUNK each time before again
668 ;;; measuring the time spent, so that the time measurement overhead
669 ;;; doesn't distort the result if calling THUNK takes very little time.
670 (defun runtime* (thunk repetitions precision
)
671 (loop repeat repetitions
673 (loop with start
= (get-internal-run-time)
675 for n
= 1 then
(* n
2)
676 for total-runs
= n then
(+ total-runs n
)
677 for gc-start
= *gc-run-time
*
680 (setf duration
(- (get-internal-run-time) start
681 (- *gc-run-time
* gc-start
)))
682 when
(> duration precision
)
683 return
(/ (float duration
)
685 into min-internal-time-units-per-call
686 finally
(return (/ min-internal-time-units-per-call
687 (float internal-time-units-per-second
)))))
689 (defmacro runtime
(form &key
(repetitions 5) (precision 30))
690 `(runtime* (lambda () ,form
) ,repetitions
,precision
))
692 (defun split-string (string delimiter
)
693 (loop for begin
= 0 then
(1+ end
)
694 for end
= (position delimiter string
) then
(position delimiter string
:start begin
)
695 collect
(subseq string begin end
)
698 (defun shuffle (sequence)
701 (coerce (shuffle (coerce sequence
'vector
)) 'list
))
702 (vector ; destructive
703 (let ((vector sequence
))
704 (loop for lim from
(1- (length vector
)) downto
0
705 for chosen
= (random (1+ lim
))
706 unless
(= chosen lim
)
707 do
(rotatef (aref vector chosen
) (aref vector lim
)))