;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;

;; Some examples using the new vdsp: and veclib: functions

;;

;; C arrays are very useful because (a) they're fast a (b) many libraries use them.

;; The easiest way to interact with C arrays in Impromptu is to use NSData/NSMutableData objects.

;; Basically NSData/NSMutableData objects are wrappers for malloc'd memory.

;;

;; Calling (objc:data:make 512) allocates 512 bytes of memory

;; and returns this "memory" as an objc object.  When the objc object goes out of scope

;; the memory is automatically released (i.e. you don't need to do anything special).

;;

;; Many impromptu functions expect nsdata/nsmutabledata objects as arguments. in particular

;; the new vector libraries vdsp: and veclib: expect nsdata/nsmutabledata objects.

;; In this example file any reference to a 'vector' actually means NSData or NSMutableData

;; NOT a scheme vector.

;;

;; For a full list of functions tab complete on vDSP: or veclib:

;;

;; Note that when creating nsmutabledata objects all lengths are in bytes (think malloc)

;; but the vdsp and veclib functions take length and count arguments in number of

;; elements (think array access). The examples below should give you a reasonable idea about

;; how all this fits together

;;

;; WARNING: working with these functions (indeed any impromptu functions using nsdata) means

;; directly manipulating memory.  For efficiency reasons I'm not checking everything you do

;; so be careful with your lengths counts strides etc..

;; In other words - don't access elements in NSData objects beyond your allocated memory!!

;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;; a couple of utility functions that we'll use later

(define print-vectorsd

   (lambda args

      (print '------------------)

      (let ((size (/ (objc:call (car args) "length") 8)))

         (dotimes (i size)

            (map (lambda (v)

                    (print (objc:data:get-double v i)))

                 args)))))


(define print-vectorsf

   (lambda args

      (print '------------------)

      (let ((size (/ (objc:call (car args) "length") 4)))

         (dotimes (i size)

            (map (lambda (v)

                    (print (objc:data:get-float v i)))

                 args)))))



;; create a vector (i.e. NSMutableData object) of double precision floats (i.e. Float64) zeroed out

(define v1 (objc:data:make (* 10 8)))

;; create a vector (i.e. NSMutableData object) of single precision floats (i.e. Float32) zeroed out

(define v2 (objc:data:make (* 10 4)))

;; create a vector of single precision floats (i.e. Float32) zeroed out

(define v3 (objc:data:make (* 10 4)))

;; create a vector of single precision floats (i.e. Float32) zeroed out

(define v4 (objc:data:make (* 10 4)))



;; assign random values (0.0-1.0) to every 2nd element in v1

(vdsp:vrandd v1 2 10)

(print-vectorsd v1)

;; reverse v1

(vdsp:vrvrsd v1 1 10)

(print-vectorsd v1)


;; assign the value 5.125 to each element of v2

(vdsp:vfill 5.125 v2 1 10)

(print-vectorsf v2)

;; assign a series starting at 0 incrementing by pi to v3

(vdsp:vramp 0.0 3.141592 v3 1 10)

(print-vectorsf v3)

;; assign random numbers (0.0-1.0) to v4

(vdsp:vrand v4 1 10)

(print-vectorsf v4)

;; scale v4 by 256.0 and put result in v2

(vdsp:vsmul v4 1 256.0 v2 1 10)

(print-vectorsf v2)

;; sum v2

(print 'sum-of-v2 (vdsp:sve v2 1 10))



;; let's see how fast this stuff is.  one million float32 elements

(define v5 (objc:data:make (* 1000000 4)))

;; lets ramp from 0.0 to 999999.0

(let ((t (now)))

   (vdsp:vramp 0.0 1.0 v5 1 1000000)

   (print 'time-taken: (- (now) t)))


;; the answer is VERY FAST

;; don't print the whole vector because that will take FOREVER!

;; instead we'll print the first, last and a random internal element

(print (objc:data:get-float v5 0)

       (objc:data:get-float v5 409193)

       (objc:data:get-float v5 999999))


;; now let's do something cool - like convolution!

;; first create a 4 tap filter [4 3 2 1]

(define v-filter (objc:data:make (* 4 4)))

(vdsp:vramp 4.0 -1.0 v-filter 1 4)

;; then create a constant signal

(define v-signal (objc:data:make (* 4 16)))

(vdsp:vfill 1.0 v-signal 1 16)

(vdsp:vfill 0.0 v-signal 1 4)

;; now convolve the signal with the filter dumping the result into output

(define v-output (objc:data:make (* 4 16)))

(vdsp:conv v-signal 1 v-filter -1 v-output 1 16 4)

;; and print

(print-vectorsf v-output)


;; You can also create a reference into a data object.

;; This can be useful for working on subsections of a vector

;; note that this is a ref not a copy so you MUST NOT let the original fall out of scope

;; you can however let the refence fall out of scope without danger.

(define original-v (objc:data:make (* 9 4)))

;; subrange ref takes an offset in bytes and a length in bytes

;; and returns an NSData reference to the original

(define reference-v (objc:data:subref original-v (* 3 4) (* 3 4)))

(print-vectorsf original-v)

(print-vectorsf reference-v)

(vdsp:vfill 1.0 original-v 1 9)

(vdsp:vfill 2.0 reference-v 1 3)

(print-vectorsf original-v)

(print-vectorsf reference-v)


;; dont' forget the veclib: functions

(define new-sqrt-data (objc:data:make (* 9 4)))

(veclib:vvsqrtf new-sqrt-data original-v 9)

(print-vectorsf new-sqrt-data)


;; Note that we can get and set individual elements of vectors (i.e. NSMutableData objects)

;; but we must use the correct type (i.e. float uint8 uint32 double etc..)

;; however, bare in mind that this is slow - try to use the vdsp and veclib functions

(print (objc:data:get-float original-v 0))

(objc:data:set-float original-v 0 22.125)

(objc:data:set-float original-v 8 22.125)

(print (objc:data:get-float original-v 8))

(print-vectorsf original-v)