/* Matlab-interface Copyright (c) 2002 Sebastian Seidel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact me by mail: Sebastian.H.Seidel@med.uni-giessen.de */ /* This file - primitive.c - provides the core procedures of matlab-interface. Use matlab-interface to control matlab (www.mathworks.com) via interprocess-communication from within the fantastic open-source scheme-interpreters/compilers mzscheme and drscheme that are distributed by plt (www.plt-scheme.org). */ /* primitive.c * ----------- * * First-edition: 2002-02-20 SSeidel * Latest-revision: 2002-05-20 SSeidel * * (This file is best viewed with emacs which is available from www.gnu.org ;^) * * Tested with PLT-Scheme version: 200 201 202 * ------------------------------- * * Modules? * -------- * yes -> compile with #define PLTMOD set below, standard setup. * no -> compile with #define PLTRAW set below, difficult setup. * use (load-extension ) to load the interface. * * Usage-example: * -------------- * (current-namespace (make-namespace)) * (require (lib "primitive.ss" "matlab-interface")) * (require (lib "highlevel.ss" "matlab-interface")) * (engine-open) * (display (engine-eval "2 + 2")) * (engine-close) * * Scheme-primitives, defined below: * --------------------------------- * vector of fixnums * vector of fixnums * symbol, one of '(cell char double int32) * default: 'double * symbol, one of '(real complex) * default: 'real * nested vectors, that implement multidimensional * array in scheme * * Make Array * (make-cell-array [object]) -> array * (make-char-array [object]) -> array * (make-array [type] [complexity] [object]) -> array * (array-clone ) -> array * * array-name -rank -size -shape -set! -ref and -fill! * (array-name [string]) -> string * (array-rank ) -> integer * (array-size ) -> integer * (array-shape ) -> shape * (array-set! ) -> unspecified * (array-ref value * (array-fill! ) -> unspecified * * Array-type and complexity * (array-type ) -> symbol * (array-complexity ) -> symbol * * Predicates * (array? ) -> boolean * (empty-array? ) -> boolean * (real-array? ) -> boolean * (complex-array? ) -> boolean * (cell-array? ) -> boolean * (char-array? ) -> boolean * (double-array? ) -> boolean * (int32-array? ) -> boolean * (numeric-array? ) -> boolean * * Communicate with matlab-'engine'-process * (engine-open) -> unspecified * (engine-close) -> unspecified * (engine-eval ) -> string | void * (engine-put [string]) -> unspecified * (engine-get ) -> array * * Convert string<->array vector<->array object<->array * (array->string ) -> string * (string->array ) -> array * (array->object ) -> object * (object->array [type] [complexity]) -> array * (array->vector ) -> vector * (vector->array [type] [complexity]) -> array * (array->matrix ) -> matrix * (matrix->array [type] [complexity]) -> array * * Info * (print-matlab-array-info) -> unspecified * * Additional Scheme-Primitives in debug-mode, defined very below: * --------------------------------------------------------------- * * Debug * (array-index->offset ) -> integer * (array-index->elnum ) -> integer * (array-bytes-per-element ) -> integer * (array-element-ref ) -> value * (array-element-set! ) -> unspecified * (matlab-eps) -> flonum * (compatible-array? ) -> boolean * (type-symbol->id ) -> fixnum * (complexity-symbol->id ) -> fixnum * (array-type-id ) -> integer * (array-type-string ) -> string * (array-complexity-id ) -> integer * (array-complexity-string ) -> string * (matrix-shape ) -> vector * * Playground * (playground-eat-memory ) -> unspecified * * Exceptions in matlab-environment: * --------------------------------- * Exceptions in matlab-environment do _not_ interrupt scheme: * (begin (engine-eval "value = ") * "continued") * => "continued" * Interruption provides for better error-checking and can be * implemented by verifying assignement, like this: * (begin (engine-eval "clear value; value = ;") * (engine-get "value") * "continued") * => eng_get_array: * No mxArray of name 'value' exists in matlab-environment. * * Anatomy of scheme-representation of mxArray: * -------------------------------------------- * * - homogenous: all elements are of the same type. * - stored in one (if real) or two (if complex) long vectors in memory. * - characterized by shape, complexity and element-type. * * Shape is represented by a vector. * - matlab-strings are 2-dimensional 1-by-n row-vectors of mxChar * - matlab-vectors are 2-dimensional n-by-1 column-vectors. * * Complexity is specified with a symbol, complexity-symbol, that * internally corresponds to a bool ,complexity-id: * * complexity-symbol complexity-id * ----------------- ------------- * real 0 * complex 1 * * Type is specified with a symbol, type-name, that internally * corresponds to an int, type-id. Currently, values of those (+) * matlab-types are accessible in Scheme: * * type-id type-name stored-data real complex * ------- --------- ----------- ---- ------- * 1 cell mxArray* + non-existent * 4 char mxChar + non-existent * 6 double double + + * 12 int32 int + - * * * Array in memory: * ---------------- * * Picture 1: 24 28 32 * ---------- /25 29 /3 * / 26 30/34 * / 27 3/ 35 * / / / / * / / / / * 12 16 20 / * /13 17 /1 / Picture 2: 3rd dimension * / 14 18/22 / ---------- / * / 15 1/ 23/ / * / / / / /________ 2nd dimension: row * / / / / | * 00 04 08 / | * 01 05 09 / | * 02 06 10 / | * 03 07 11/ 1st dimension: column * * * Picture 3: * ---------- * * ----------------------------------------------------------------- * | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | ... | 33 | 34 | 35 | * ----------------------------------------------------------------- * * Picture 1 shows a 3-dimensionsal-#(4 3 3)-array consisting of 36 * elements. * * Picture 3 shows that in memory, all elements are stored in one * long vector. The first element in memory is 00, followed by 01, * 02, ... . * * Picture 2 shows how picture 1 corresponds to column, row and * third dimension. For example: * * Element 00 can be addressed #(0 0 0) --> col: 0, row: 0, 3rd: 0 * 07 #(3 1 0) --> 3 1 0 * 35 #(3 2 2) --> 3 2 2 * * Conclusion: * * -> Column-elements are stored together: * (00 01 02 03) (04 05 06 07) (16 17 18 19) * * -> If you want all elements of one row, you have to jump a bit: * (00 04 08) (01 05 09) (24 28 32) (14 18 22) * * -> If you want all elements of one third dimension, you must jump * even wider: (00 12 24) (05 17 29) (11 23 35) * * -> 4th, 5th, ... dimension: Elements are further away. To get them * you must jump wider. * * Abbreviations: * -------------- * mx matrix * ss Scheme, Mzscheme * mxArray Matlab-array-type. Many matlab-procedures work on * instances of mxArray. All mxArrays always have at least * 2 dimensions: * (array-rank (make-char-array #(1 1 1 1 1))) * => 2 !!! This is poor behaviour of matlab. * ssArray Scheme-array-type. Represented by a c-struct. * Encapsulates the mxArray. Is a Scheme_Object, * characterized by its type: ssArray_type. * Inside scheme, ssArrays are just called 'array'. * mx_array Pointer on an instance of mxArray. * ss_array Pointer on an instance of ssArray. * Like (all?) other Scheme_Objects, it is a first-class * scheme-value, that is: it may be given to and returned * from scheme-procedures. * arrayel Array-element. Precisely: struct that contains pointers * to the real and (optional: imaginary) value of one * particular element of an mx_array. * Pass the access-to-a-particular-array-element around * between procedures by passing around the arrayel. * elnum Element-number. For a particular array-element: * number of storage-places that its storage-place comes * after the storage-place of the first element of the * array. * offset For a particular array-element: number of bytes that its * storage-place comes after the storage-place of the * first element of the array. * offset = bytes-per-element * elnum; * dimarray Array of int with each int beeing the number of * possible different indexes in one particular dimension. * dimarray [0] = 5; says that there are 5 different * possibilities to index the first dimension, for * example. * dimarray as a whole corresponds to the SHAPE of an * array. * idxarray Array of int with each int specifying the index of a * value in a particular dimension. * idxarray [0] = 1; says that - in the first dimension - * the value has index 1, for example. * idxarray as a whole corresponds to particular VALUE * inside of the array. * * Supported: * ---------- * Multidimensional non-sparse non-complex arrays with * those storage-classes: * cell - Each element of a cell-array is a mxArray. * char - char char * double - double double * int32 - int32 32-bit signed int * Multidimensional non-sparse complex arrays with * those storage-classes: * double * IEEE inf (infinity) and NaN (Not-a-Number) - numbers: * Natively supported by charming Miss Scheme. * Empty arrays. Supported because r5rs allows empty vectors creatable * with (make-vector 0) and matlab allows empty arrays. Useful??? * Standard initializing after creation. Newly created mxArrays are * initialized like this: * * Array-type Initial-value-of-each-element * ---------- ----------------------------- * cell: no-initialization! * char: #\nul * int32: 0 * double: 0.0 * complex-double: 0.0+0.0i * * Not supported: * -------------- * Sparse arrays. Should be supported someday, if there is any need. * Special logical arrays. Its just a flag. Use zero/nonzero * int32-matrices instead and flag them logical/nonlogical inside * matlab-environment. * Structure arrays. No structures needed in true scheme. * No structures in r5rs, either. * Variable origin. Bad idea! * Array-reshaping. Bad idea! * Arrays of more than max_number_of_dimensions - currently 1024 - * dimensions require recompilation. * Arbitrary initializing after creation. * * Multibyte <-> unibyte - character-conversion: * --------------------------------------------- * Matlab uses 2-byte-characters of type mxChar. * No multibyte-character-support in plt-scheme. * Thus, the first byte of mxChar is converted to scheme-character-data * whereas the second byte of mxChar is ignored. * Since the entire ascii-character-set maps to the first characters * in unicode/ucs, they should be correctly converted. * Same with iso-8859-1, synonym: latin-1. See [1]. * * Ideas concerning definition of arrays in scheme: * ------------------------------------------------ * * Problem and motivation: * Arrays of arbitrary number of dimensions (rank) are painfully * missing from scheme-standard. Following some thoughts about how a * definition of scheme-arrays might look like. * * Should multidimensional arrays be part of scheme-standard at all? * Contra: * Arrays can be build from vectors. Scheme defines primitive * datatypes. We do not have lists neither, only pairs. * We do not need arrays as a generalisation of pairs! * We do not need generalisations of anything. * See discussion about operator-overloading in case of * generalised set! . * See that strings are not specialised vectors. * See that pairs are not specialised vectors. * Arrays with more than 2 dimensions are rarely needed, more * than 3 dimensions almost never! * There are too many different possibilities of defining an * array-type. This shows that there is no such thing like a * 'general' multidimensional array. * What about sparse-arrays? They just make it impossible to * define *one* general array-type, do they not? * The current scheme-standard would have to be fundamentally * changed to get arrays without sacrificing consistency. * Old programs would not run anymore. There is no way of * dealing with arrays like a simple add-on to the current * standard. * Pro: * Convinience. Why should everybody invent his own little * special-purpose array-language ever and ever again? * Better interfacing to foreign array-based languages and * libraries like Matlab. * Array is the natural abstraction of many things including * vectors, strings and pairs. Pairs could be special-purpose * length-2-vectors and 1-dimensional #(2)-Arrays. * Backward compatibility is a problem but consistency and keeping * up with the 'state-of-the-art' is a nobler goal to long for * and makes scheme a computer-language not only of the past, * but also adept at dealing with the future. * Neither: * Since the primary goal should be consistency: what is the * philosophy behind the current setup of scheme-types? * Is this philosophy not damaged by the existence of an * independent string-type, anyway? * Is this philosophy simplifying or complicating things? * Is the existence of arrays coupled with the discussion about * argument-types in scheme? In which way? * * Variable origin: * Specify origin to move array through space. Enable negative * indexing. Bad idea? * Pro: * If negative indexing is allowed one can have funny index-spaces. * Contra: * Adds confusion. Origin-zero should be something * to depend on, like index-zero of scheme-vectors is always * the first element. * * Incremental real indexing: * Contra: * The incremental factor could be specified through a constant * factor, a function or a set of vectors. This means that * the indexes would contain information that would have to * be stored somewhere. * In fact they'd represent just another dimension: * length-x-vector with incremental indexing equals * #(x 2)-plane. * * Automatic adapting shape: * Instead of an error if array-set! or array-ref is called on * an element that is outside the array, the array-shape could adjust. * Same for dimensions. Bad idea? * Pro: * Convenient because the number of elements or dimensions might * not been known in advance. * Convenient because array-shape must not be specified when * creating an array. * Automatic reshaping makes tedious manual reshaping superfluous. * -> Many ugly work-arounds are rooted out from the beginning. * Arrays might shrink, too, if there were procedures that * delete elements/dimensions/areas. * Contra: * Array operations like addition require arrays of the same shape. * This might be handled in at least three ways: * -> the arrays are automatically resized / redimensioned in * a way that in the end they have the same shape. * -> the arrays have to be reshaped by the programmer. * Therefore the shapes of *both* arrays eventually have * to be adjusted. A simple procedure that assigns the * shape of the one to the other won't work in all possible * cases. * -> an error is signaled. * Arrays would only grow and never shrink. Always true? * No (see above). * Abuse of normal arrays as sparse arrays would be easy. * Array-boundaries should be respected just like * scheme-vectors do not allow over/under-indexing. * -> Better error-checking. * -> If the array is able to grow in all directions, * negative indexing (see above) would be required. * Some cases of use of automatic shape-adaption may be resolved * by lists of arrays. * Neither: * What is easier: to implement shape-adapting arrays on top * of boundary-checking ones or vice-versa? * What is more useful: * boundary-checking or automatic-shape-adaption? * What is more efficient? Depends on combination of * problem-space and implementation. Tendency? * Automatic-shape-adaption requires specification (automatic or * manual) of a value which is assigned to newly allocated * elements. * What about mathematics? Mathematica? * * Shape dense, sparse, ragged ... * Should dense and sparse arrays be different types? * * Types * Arrays are abstraction of vectors? * Vectors be 1-dimensional column-arrays. * Arrays are vectors: No? * Vectors are arrays: Yes? * Primitive array-operations must work on vectors? Yes? * Primitive vector-operations must work on 1-dimensional arrays? Maybe? * Primitive array-operations return 1-dimensional array or vector, * 0-dimensional array or scalar? Arrays in both cases? * Arrays are abstraction of strings? * Strings be row-arrays of multibyte-characters. * -> typed arrays * r5rs-disjointness-of-types: * Each of these types is different from each other: * (boolean pair symbol number char string vector port procedure) * -> Vector and string are not of the same type. * Why is a string not a specialised vector? * -> Arrays be disjoint or generalisations of string and vector? * * Arrays passed by reference? * Arrays and parts of arrays are passed by reference? by value? when? * * Indexing * Array bounds included or excluded? * Lower bound included, upper bound excluded. * * Operating on arrays: share array-data. * * Operating on arrays: * - Loops and indexing / operating-on individual array-elements * corrupt parralel processing (and elegance). * Facilitate parallel processing of data by avoiding loops. * Use high-level-operations like map, map! and for-each instead. * No chance to avoid loops if information flows from one element to * another next element? Wrong. See array-max, array-min, array-all, * array-any, array-sum. * No chance if the order in which the elements are processed is * important? * Loop-killers: * Elementwise data-parallel operations: * for-each -> specify subarray, shared-array * map -> dito * array-addition, - subtraction, number-to-array-addition ... * elementwise array-multiplication, -division * Reductive data-parallel operations: * add, min, max, all, any, sum, ... * One reduction-operator is enough. It may take a binary * procedure and the array as argument. Also, the dimension(s) * where the reduction should take place has/have to be * specified. * Good practice to create a multidimensional array by means of * elementwise operations: * Create a vector 1:n and map a function on the result. * Flood the array vector-1:n-like and map a function on the result. * How to create such a vector in scheme without loops? * Lazy-evaluation? Lazy-arrays? * * Shared arrays should be immutable? * * Operator - possible short names: * Allocate: make-array array-clone -copy * Inquire: array-shape -rank -size * Selection / Referencing: array-ref * Single mutation: array-set! * General mutation: array-spread -pack -reshape * Operate elementwise: array-map array-map! -for-each * Operate reductive: array-max -min -std -all -any -or -and * Sharing data: array-share -use * * Operator overloading? * + - * / min max set! ref * * Index-object * Vector or rank-1-array or both? Since a rank-1-array is a * genaralisation of a vector, maybe specifying a vector, * which may also be a rank-1-array, is better because a string would * not be possible (but is a rank-1-array, too). * * Strings are rank-1 or rank-2 - array? * Rank-1 * because they are a sequence of characters, arranged in one * dimension. * Rank-2 * because they have to be row-arrays and rows are the second * dimension of an array. * * Strings are vectors with specialised type? * * Ideas concerning the interface: * ------------------------------- * * Possible next-generation-wrapper: * Make it possible to call EVERY Matlab-function from within Scheme. * Wrap int, double, char, ... and the function-pointers. * C-function-pointers be bound to scheme-symbols, saved in an * array where they are accessible from within c by index. * Hash tables bind symbol-names to function-pointer. * Look at eli barzilays ffi for inspiration. * MzScheme has a FFI, too? * ==> Too much work for now. * * Implementation of ... * Sparse arrays, some premature thoughts: * Design sparse-maker, -set!-ter and -ref-erencer as usual. * Invent scheme-representation of multidimensional * sparse matrices. Associative lists? * Design scheme-sparse <-> scheme-matlab-sparse - mappers. * Conversion? * Logical arrays, some premature thoughts: * Special conversion between #f/#t - scheme-matrices and logical * matlab-arrays could be implemented if anybody needed. * * Max number-of-elements * Configurable parameter in scheme. Purpose: do not allow bigger * matrices because they result in errors because the necessary memory * can not be allocated. * * Efficiency * Array_Element global. * * Add new type: * ------------- * Type-maker: * Type-converter and converter-switch: * Type-checker-Macro: * is_compatible_mxarray * highlevel.ss * * Add new primitive: * ------------------ * Document at the top of this file. * Declare. * Deposit in scheme-environment -> scheme_reload. * Define. * * 16-bit-systems, long and int * ---------------------------- * Maybe, long<->int - type-conversion-errors may be hidden because * they don't show up in a 32-bit-system, where long and int are * stored alike. How to find out? * * Type-conversion scheme <-> C == Matlab * Safety vs. Efficiency - Examples * -------------------------------------- * * Integer-conversion / Scheme->C * c-integer: 31-bits and 1 sign-bit * scheme-integer: 30-bits and 1 sign-bit and ?? * efficient: scheme_make_integer * safe: scheme_make_integer_value * * Integer-conversion / C->Scheme * scheme-integer may be fixnum that always fits or bignum that * maybe fits (murphy: 'propably not.'). * efficient: scheme_int_val * safe: get_int_val * * References: * ----------- * [0] Explanation of how mxArrays are stored in memory: * matlab-help-desk/external-interfaces/API-reference/mxCalcSingleSubscript * [1] UTF-8 and Unicode FAQ for Unix/Linux by Markus Kuhn: * http://www.cl.cam.ac.uk/~mgk25/unicode.html * [2] Interesting discussion about array-primitives in scheme: * http://srfi.schemers.org/srfi-25/ * [3] Jim Weigang's apl-information: * http://www.chilton.com/~jimw/ * * Cell-arrays: referencing vs. copying * ------------------------------------ * array-ref should return reference to element, * not a newly allocated copy of the element. * array-set! should set element to reference of array, * not to a newly allocated copy of array. * ---------------------------------------------------------- * Rationale: Copying is inefficient. * Problem: Due to garbage-collection, implementation seems difficult. * If referencing is implemented, cell-array must not be * allocated atomic since it contains pointers to gc-managed * memory-blocks (mxArray*). * How may I register the array of mxArray* with the gc? * Testing: If the following works with arrays, too, * it has been done right: * (Tested in plt-scheme and petite-chez-scheme) * 1 (define con (vector 1 2 3)) * con ;; ==> #3(1 2 3) * (define vec (vector 4 5 6)) * (vector-set! con 2 vec) * con ;; ==> #3(1 2 #3(4 5 6)) * (vector-set! vec 2 666) * con ;; ==> #3(1 2 #3(4 5 666)) * 2 (define con (vector 1 2 (vector 4 5 6))) * con ;; ==> #3(1 2 #3(4 5 6)) * (vector-set! (vector-ref con 2) 2 666) * con ;; ==> #3(1 2 #3(4 5 666)) * Is this behaviour of scheme something to depend on? * What about r5rs? * Ergo: Take care of that later. * Change: FIXME-REF. * Read: Implementation of vectors in scheme. vector_ref * * To do: * ------ * make-cell-array: Initialize to something? Currently initialized to * NULL, so (array-ref ) could either result in an * error or return scheme_void, or scheme_false. * New name: matlab-remote ? * Thread safe? See globals dimarray and idxarray. * engine-open: If it works on unix/linux, make communication with remote * hosts possible. How to check for os-type from within the extension. * Shape-comparison. matrix-shape. * * To do 2002-10-27: * ----------------- * attach engine-session to current custodian * must end with drscheme/execute * multiple sessions? * complain to mathworks * a second call to engOpen * does not start a new engine * returnes a new handle to the already opened engine * sometimes - as if by miracle - engOpen * starts a new engine! * returns a handle to that new engine! * enables one to * evaluate expressions in a certain engine * close a certain engine * one must call engClose on all handles to kill the engine-process. */ /* PLT-with-modules?: yes -> PLTRAW or no -> PLTMOD */ #define PLTMOD /* Debug-mode includes additional functions and array-offset-boundary-check that is neccessary when referencing array-elements by raw memory offset rather than by index. */ /* #define DEBUG */ #undef DEBUG #include #include "escheme.h" /* access to MzScheme-types and -functions */ #include "engine.h" /* access to Matlab-engine-types and -functions */ #define MAX_NUMBER_OF_DIMENSIONS 1024 #define STRING_BUFFER_SIZE 4096 static int dimarray [MAX_NUMBER_OF_DIMENSIONS]; static int idxarray [MAX_NUMBER_OF_DIMENSIONS]; static char stringbuf [STRING_BUFFER_SIZE]; static Engine* ep = NULL; /* Encapsulate mxArray in a Scheme_Object. */ typedef struct { Scheme_Type type; mxArray *mx_array; } ssArray; /* Not a Scheme_Object. Use this struct to pass mxArray-elements of various types around between extension-internal functions. When set to an array-element, it contains all information needed to: - convert the value of the array-element from C to Scheme. - convert a Scheme-value, so that it will fit into the mxArray. - set the mxArray-Element to a different value. */ typedef struct { mxClassID storage_class; bool complex_p; void* real; void* imag; } Array_Element; static Scheme_Type ssArray_type = 0; static Scheme_Type ssZombieArray_type = 0; /* mxArray-Pointer: Return pointer on mxArray. */ #define MXARRAY_PTR(obj) ((mxArray*) (((ssArray*) obj)->mx_array)) /* mxArray-Predicate: True if obj contains mxArray. */ #define MXARRAY_P(obj) (\ (SCHEME_TYPE (obj) == ssArray_type) && \ (MXARRAY_PTR (obj) != NULL)) /* True, if an mx_array of certain type and complexity may be created or imported. True, if the conversion to and from scheme-values is supported by the interface. */ #define IS_COMPATIBLE_MXARRAY(id,complex_p) ((\ (((complex_p) == 1) && ((id) == mxDOUBLE_CLASS)) ||\ (((complex_p) == 0) && (\ ((id) == mxCELL_CLASS) ||\ ((id) == mxCHAR_CLASS) ||\ ((id) == mxDOUBLE_CLASS) ||\ ((id) == mxINT32_CLASS))))? 1 : 0) /***** Create or terminate ss_array. =================================== */ static mxClassID type_symbol_to_id (Scheme_Object *type); static bool complexity_symbol_to_id (Scheme_Object *complexity); static mxArray* create_numeric_array (int ndim, int *dimarray, mxClassID type_id, bool complexity_id); static Scheme_Object* create_ss_array (mxArray *mx_array); static void terminate_ss_array (Scheme_Object *o); static void finalise_ss_array (void *o, void *d); static void engine_atexit (Scheme_Object *o, Scheme_Close_Custodian_Client *f, void *d); /* fixme20021105 */ /***** make-array array-set! and array-ref c-helpers =================== */ static int dimarray_set (Scheme_Object *dimensions); static int dimarray_take_matrix_shape (Scheme_Object *matrix); static int dimarray_take_matrix_shape_rec (int n, Scheme_Object *matrix); static void dimarray_reverse (int n); static void idxarray_set_and_check (Scheme_Object *index, int ndim, const int* dims); static int mxarray_index_to_offset (mxArray* mx_array, Scheme_Object *index); static int mxarray_index_to_elnum (mxArray* mx_array, Scheme_Object *index); static void arrayel_get_with_offset (mxArray* mx_array, Array_Element* element, int offset); static void arrayel_get_with_index (mxArray* ss_array, Array_Element* element, Scheme_Object* index); /***** Convert scheme->matlab ========================================== */ typedef void (*ss_to_mx_setter) (void*, void*, Scheme_Object*); static ss_to_mx_setter ss_to_mx_setter_switch (int storage_class, int complex_p); static void arrayel_set_convert (Array_Element* element, Scheme_Object* val); static void mxarray_set_with_vector (mxArray* mx_array, Scheme_Object* vector); static void mxarray_fill_with_object (mxArray* mx_array, Scheme_Object* object); static void scalar_mxarray_set_with_object (mxArray* mx_array, Scheme_Object* object); static void mxarray_set_with_matrix (mxArray* mx_array, Scheme_Object* matrix, int ndim); static int mxarray_set_with_matrix_rec (mxArray* mx_array, ss_to_mx_setter setter, Scheme_Object* matrix, int n, int elnum); /***** Convert matlab->scheme ========================================== */ typedef Scheme_Object* (*mx_to_ss_converter) (void*, void*); static mx_to_ss_converter mx_to_ss_converter_switch (int storage_class, int complex_p); static Scheme_Object* convert_arrayel_ref (Array_Element* element); static Scheme_Object* mxarray_to_vector (mxArray *mx_array); static Scheme_Object* mxarray_to_object (mxArray *mx_array); static Scheme_Object* mxarray_to_matrix (mxArray *mx_array); static int mxarray_to_matrix_rec (mxArray *mx_array, mx_to_ss_converter converter, Scheme_Object **vector, int n, int elnum); /***** Scheme-Primitives: make-array ================================== */ static Scheme_Object* mx_create_cell_array (int argc, Scheme_Object *argv []); static Scheme_Object* mx_create_char_array (int argc, Scheme_Object *argv []); static Scheme_Object* mx_create_numeric_array (int argc, Scheme_Object *argv []); static Scheme_Object* make_double_array (int argc, Scheme_Object *argv []); static Scheme_Object* mx_duplicate_array (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: array-name -shape -set! and -ref ============= */ static Scheme_Object* mx_get_or_set_name (int argc, Scheme_Object *argv []); static Scheme_Object* mx_get_number_of_dimensions (int argc, Scheme_Object *argv []); static Scheme_Object* mx_get_number_of_elements (int argc, Scheme_Object *argv []); static Scheme_Object* mx_get_dimensions (int argc, Scheme_Object *argv []); static Scheme_Object* array_set (int argc, Scheme_Object *argv []); static Scheme_Object* array_ref (int argc, Scheme_Object* argv []); static Scheme_Object* array_fill (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: array-type and complexity ==================== */ static Scheme_Object* mx_get_class_symbol (int argc, Scheme_Object *argv []); static Scheme_Object* mx_get_class_id (int argc, Scheme_Object *argv []); static Scheme_Object* mx_get_class_name (int argc, Scheme_Object *argv []); static Scheme_Object* mx_complexity_symbol (int argc, Scheme_Object *argv []); static Scheme_Object* mx_complexity_id (int argc, Scheme_Object *argv []); static Scheme_Object* mx_complexity_name (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: predicates =================================== */ static Scheme_Object* mx_is_array (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_complex (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_real (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_numeric (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_cell (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_char (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_int32 (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_double (int argc, Scheme_Object *argv []); static Scheme_Object* mx_is_empty (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: communicate with matlab-'engine'-process ===== */ static Scheme_Object* eng_open (int argc, Scheme_Object *argv []); static Scheme_Object* eng_close (int argc, Scheme_Object *argv []); static Scheme_Object* eng_eval_string (int argc, Scheme_Object *argv []); static Scheme_Object* eng_put_array (int argc, Scheme_Object *argv []); static Scheme_Object* eng_get_array (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: convert string<->array vector<->array object<->array */ static Scheme_Object* mx_array_to_string (int argc, Scheme_Object *argv []); static Scheme_Object* mx_create_string (int argc, Scheme_Object *argv []); static Scheme_Object* array_to_vector (int argc, Scheme_Object *argv []); static Scheme_Object* vector_to_array (int argc, Scheme_Object *argv []); static Scheme_Object* vector_to_double_array (int argc, Scheme_Object *argv []); static Scheme_Object* array_to_object (int argc, Scheme_Object *argv []); static Scheme_Object* mx_create_scalar_double (int argc, Scheme_Object *argv []); static Scheme_Object* object_to_array (int argc, Scheme_Object *argv []); static Scheme_Object* matrix_to_double_array (int argc, Scheme_Object *argv []); static Scheme_Object* matrix_to_array (int argc, Scheme_Object *argv []); static Scheme_Object* array_to_matrix (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: info ========================================= */ static Scheme_Object* my_apply (char proc [], int argc, Scheme_Object *argv []); static Scheme_Object* print_matlab_array_info (int argc, Scheme_Object *argv []); #ifdef DEBUG /***** Scheme-Primitives: debug ======================================== */ static Scheme_Object* array_index_to_offset (int argc, Scheme_Object *argv []); static Scheme_Object* array_index_to_elnum (int argc, Scheme_Object *argv []); static Scheme_Object* array_bytes_per_element (int argc, Scheme_Object *argv []); static Scheme_Object* array_element_ref (int argc, Scheme_Object *argv []); static Scheme_Object* array_element_set (int argc, Scheme_Object *argv []); static Scheme_Object* matlab_eps (int argc, Scheme_Object *argv []); static Scheme_Object* is_compatible_array (int argc, Scheme_Object *argv []); static Scheme_Object* debug_type_symbol_to_id (int argc, Scheme_Object *argv []); static Scheme_Object* debug_complexity_symbol_to_id (int argc, Scheme_Object *argv []); static Scheme_Object* matrix_shape (int argc, Scheme_Object *argv []); /***** Scheme-Primitives: playground =================================== */ static void playground_visible_finaliser (void* obj, void* nbytes); static Scheme_Object* playground_eat_memory (int argc, Scheme_Object *argv []); #endif /**** INITIALIZE SCHEME ================================================ */ Scheme_Object* scheme_initialize (Scheme_Env *env) { ssArray_type = scheme_make_type (""); ssZombieArray_type = scheme_make_type (""); /* fixme20021105 Just a practical hack to kill any open matlab-engine-process when kissing mzscheme goodbye. */ scheme_add_atexit_closer (engine_atexit); return scheme_reload (env); } Scheme_Object* scheme_reload (Scheme_Env *env) { Scheme_Env* environment = NULL; #ifdef PLTMOD environment = scheme_primitive_module (scheme_intern_symbol ("primitive"), env); #endif #ifdef PLTRAW environment = env; #endif /***** SCHEME-PRIMITIVES: MAKE-ARRAY ================================= */ scheme_add_global ("make-cell-array", scheme_make_prim_w_arity (mx_create_cell_array, "create-cell-array", 1, 2), environment); scheme_add_global ("make-char-array", scheme_make_prim_w_arity (mx_create_char_array, "create-char-array", 1, 2), environment); scheme_add_global ("make-array", scheme_make_prim_w_arity (mx_create_numeric_array, "make-array", 1, 4), environment); scheme_add_global ("array-clone", scheme_make_prim_w_arity (mx_duplicate_array, "array-clone", 1, 1), environment); /***** SCHEME-PRIMITIVES: ARRAY-NAME -RANK -SIZE -SHAPE -SET! AND -REF */ scheme_add_global ("array-name", scheme_make_prim_w_arity (mx_get_or_set_name, "array-name", 1, 2), environment); scheme_add_global ("array-rank", scheme_make_prim_w_arity (mx_get_number_of_dimensions, "array-rank", 1, 1), environment); scheme_add_global ("array-size", scheme_make_prim_w_arity (mx_get_number_of_elements, "array-size", 1, 1), environment); scheme_add_global ("array-shape", scheme_make_prim_w_arity (mx_get_dimensions, "array-shape", 1, 1), environment); scheme_add_global ("array-set!", scheme_make_prim_w_arity (array_set, "array-set!", 3, 3), environment); scheme_add_global ("array-ref", scheme_make_prim_w_arity (array_ref, "array-ref", 2, 2), environment); scheme_add_global ("array-fill!", scheme_make_prim_w_arity (array_fill, "array-fill!", 2, 2), environment); /***** SCHEME-PRIMITIVES: ARRAY-TYPE AND COMPLEXITY ================== */ scheme_add_global ("array-type", scheme_make_prim_w_arity (mx_get_class_symbol, "array-type", 1, 1), environment); scheme_add_global ("array-complexity", scheme_make_prim_w_arity (mx_complexity_symbol, "array-complexity", 1, 1), environment); /***** SCHEME-PRIMITIVES: PREDICATES ================================= */ scheme_add_global ("array?", scheme_make_prim_w_arity (mx_is_array, "array?", 1, 1), environment); scheme_add_global ("complex-array?", scheme_make_prim_w_arity (mx_is_complex, "complex-array?", 1, 1), environment); scheme_add_global ("real-array?", scheme_make_prim_w_arity (mx_is_real, "real-array?", 1, 1), environment); scheme_add_global ("numeric-array?", scheme_make_prim_w_arity (mx_is_numeric, "numeric-array?", 1, 1), environment); scheme_add_global ("cell-array?", scheme_make_prim_w_arity (mx_is_cell, "cell-array?", 1, 1), environment); scheme_add_global ("char-array?", scheme_make_prim_w_arity (mx_is_char, "char-array?", 1, 1), environment); scheme_add_global ("int32-array?", scheme_make_prim_w_arity (mx_is_int32, "int32-array?", 1, 1), environment); scheme_add_global ("double-array?", scheme_make_prim_w_arity (mx_is_double, "double-array?", 1, 1), environment); scheme_add_global ("empty-array?", scheme_make_prim_w_arity (mx_is_empty, "empty-array?", 1, 1), environment); /***** SCHEME-PRIMITIVES: COMMUNICATE WITH MATLAB-'ENGINE'-PROCESS === */ scheme_add_global ("engine-open", scheme_make_prim_w_arity (eng_open, "engine-open", 0, 0), environment); scheme_add_global ("engine-close", scheme_make_prim_w_arity (eng_close, "engine-close", 0, 0), environment); scheme_add_global ("engine-eval", scheme_make_prim_w_arity (eng_eval_string, "engine-eval", 1, 1), environment); scheme_add_global ("engine-put", scheme_make_prim_w_arity (eng_put_array, "engine-put", 1, 2), environment); scheme_add_global ("engine-get", scheme_make_prim_w_arity (eng_get_array, "engine-get", 1, 1), environment); /***** SCHEME-PRIMITIVES: CONVERT STRING<->ARRAY VECTOR<->ARRAY OBJECT<->ARRAY */ scheme_add_global ("array->string", scheme_make_prim_w_arity (mx_array_to_string, "array->string", 1, 1), environment); scheme_add_global ("string->array", scheme_make_prim_w_arity (mx_create_string, "string->array", 1, 1), environment); scheme_add_global ("array->vector", scheme_make_prim_w_arity (array_to_vector, "array->vector", 1, 1), environment); scheme_add_global ("vector->array", scheme_make_prim_w_arity (vector_to_array, "vector->array", 1, 3), environment); scheme_add_global ("array->object", scheme_make_prim_w_arity (array_to_object, "array->object", 1, 1), environment); scheme_add_global ("object->array", scheme_make_prim_w_arity (object_to_array, "object->array", 1, 3), environment); scheme_add_global ("matrix->array", scheme_make_prim_w_arity (matrix_to_array, "matrix->array", 1, 3), environment); scheme_add_global ("array->matrix", scheme_make_prim_w_arity (array_to_matrix, "array->matrix", 1, 1), environment); /***** SCHEME-PRIMITIVES: INFO ======================================= */ scheme_add_global ("print-matlab-array-info", scheme_make_prim_w_arity (print_matlab_array_info, "print-matlab-array-info", 0, 0), environment); #ifdef DEBUG /***** DEBUG ========================================================= */ scheme_add_global ("array-index->offset", scheme_make_prim_w_arity (array_index_to_offset, "array-index->offset", 2, 2), environment); scheme_add_global ("array-index->elnum", scheme_make_prim_w_arity (array_index_to_elnum, "array-index->elnum", 2, 2), environment); scheme_add_global ("array-bytes-per-element", scheme_make_prim_w_arity (array_bytes_per_element, "array-bytes-per-element", 1, 1), environment); scheme_add_global ("array-element-ref", scheme_make_prim_w_arity (array_element_ref, "array-element-ref", 2, 2), environment); scheme_add_global ("array-element-set!", scheme_make_prim_w_arity (array_element_set, "array-element-set!", 3, 3), environment); scheme_add_global ("matlab-eps", scheme_make_prim_w_arity (matlab_eps, "matlab-eps", 0, 0), environment); scheme_add_global ("compatible-array?", scheme_make_prim_w_arity (is_compatible_array, "compatible-array?", 2, 2), environment); scheme_add_global ("type-symbol->id", scheme_make_prim_w_arity (debug_type_symbol_to_id, "type-symbol->id", 1, 1), environment); scheme_add_global ("complexity-symbol->id", scheme_make_prim_w_arity (debug_complexity_symbol_to_id, "complexity-symbol->id", 1, 1), environment); scheme_add_global ("array-type-id", scheme_make_prim_w_arity (mx_get_class_id, "array-type-id", 1, 1), environment); scheme_add_global ("array-type-string", scheme_make_prim_w_arity (mx_get_class_name, "array-type-string", 1, 1), environment); scheme_add_global ("array-complexity-id", scheme_make_prim_w_arity (mx_complexity_id, "array-complexity-id", 1, 1), environment); scheme_add_global ("array-complexity-string", scheme_make_prim_w_arity (mx_complexity_name, "array-complexity-string", 1, 1), environment); scheme_add_global ("matrix-shape", scheme_make_prim_w_arity (matrix_shape, "matrix-shape", 1, 1), environment); /***** PLAYGROUND ==================================================== */ scheme_add_global ("playground-eat-memory", scheme_make_prim_w_arity (playground_eat_memory, "playground-eat-memory", 1, 1), environment); #endif #ifdef PLTMOD scheme_finish_primitive_module (environment); #endif return scheme_void; } Scheme_Object* scheme_module_name () { #ifdef PLTMOD return scheme_intern_symbol ("primitive"); #endif #ifdef PLTRAW return scheme_void; #endif } /***** CREATE OR TERMINATE SCHEME-ARRAY ================================ */ /* (type-symbol-to-id ) -> int */ static mxClassID type_symbol_to_id (Scheme_Object *type) { if (scheme_eq (type, scheme_intern_symbol ("cell"))) return mxCELL_CLASS; else if (scheme_eq (type, scheme_intern_symbol ("char"))) return mxCHAR_CLASS; else if (scheme_eq (type, scheme_intern_symbol ("double"))) return mxDOUBLE_CLASS; else if (scheme_eq (type, scheme_intern_symbol ("int32"))) return mxINT32_CLASS; /* Unknown type, but this must be checked safely elsewhere. */ else return mxUNKNOWN_CLASS; } /* (complexity-symbol-to-id ) -> bool */ static bool complexity_symbol_to_id (Scheme_Object *complexity) { if (scheme_eq (complexity, scheme_intern_symbol ("real"))) return mxREAL; else if (scheme_eq (complexity, scheme_intern_symbol ("complex"))) return mxCOMPLEX; /* Unknown complexity, but this must be checked safely elsewhere. */ else return -1; } /* (create_numeric_array ) -> mx_array */ static mxArray* create_numeric_array (int ndim, int *dimarray, mxClassID type_id, bool complexity_id) { mxArray *mx_array; if (! IS_COMPATIBLE_MXARRAY (type_id, complexity_id)) scheme_signal_error ("create_numeric_array: " "arrays with type-id %d and complexity-id %d " "are not yet interfaced.", type_id, complexity_id); /* mxArray-CREATOR */ mx_array = mxCreateNumericArray (ndim, dimarray, type_id, complexity_id); if (mx_array == NULL) scheme_signal_error ("create_numeric_array: " "Not enough memory to allocate mxArray."); return mx_array; } /* (create_ss_array ) Allocate and return ssArray that contains . */ static Scheme_Object* create_ss_array (mxArray* mx_array) { Scheme_Object *o; mxClassID mx_type; bool mx_complexity; /* Finale Qualitaetssicherung. Reject interface-uncompatible mxArrays here. */ if (mx_array == NULL) scheme_signal_error ("create_ss_array: " "called with void array."); mx_type = mxGetClassID (mx_array); mx_complexity = mxIsComplex (mx_array); if (! IS_COMPATIBLE_MXARRAY (mx_type, mx_complexity)) scheme_signal_error ("create_ss_array: " "called with uncompatible array."); o = (Scheme_Object*) scheme_malloc_fail_ok (scheme_malloc_atomic, sizeof (ssArray)); o->type = ssArray_type; ((ssArray*) o)->mx_array = mx_array; scheme_register_finalizer (o, finalise_ss_array, NULL, NULL, NULL); return o; } /* (terminate_ss_array ) Before ssArray is garbage-collected, free memory of mxArray in . */ static void terminate_ss_array (Scheme_Object *o) { if (MXARRAY_P (o)) { #ifdef DEBUG scheme_warning ("terminate_ss_array: Free mxArray."); #endif mxDestroyArray (MXARRAY_PTR (o)); MXARRAY_PTR (o) = NULL; o->type = ssZombieArray_type; } else scheme_warning ("terminate_ss_array: " "Attempt to finalise non-existent mxArray."); } /* (finalise_ss_array ) General finaliser: Pass ssArray on to terminate_ss_array. */ static void finalise_ss_array (void *o, void *d) { terminate_ss_array ((Scheme_Object *) o); } /* (engine_atexit) fixme20021105 */ void engine_atexit (Scheme_Object *o, Scheme_Close_Custodian_Client *f, void *d) { if (ep != NULL) { engClose (ep); ep = NULL; } } /***** MAKE-ARRAY ARRAY-SET! AND ARRAY-REF C-HELPERS =================== */ /* (dimarray_set ) -> - Convert each element of to an int and save the result in the extension-global variable dimarray. Return number-of-dimensions. is a vector of scheme-integers. Signal an error if: - (1) contains more elements than MAX_NUMBER_OF_DIMENSIONS. or for any element of : - (2) the element is not an integer. - (3) the conversion is not possible. - (4) the result of the conversion is lesser than one. */ static int dimarray_set (Scheme_Object *shape) { int i, ndim; Scheme_Object **ss_dims; if (! SCHEME_VECTORP (shape)) scheme_wrong_type ("dimarray_set", "vector", 0, 1, &shape); ndim = SCHEME_VEC_SIZE (shape); ss_dims = SCHEME_VEC_ELS (shape); /* (1) */ if (ndim > MAX_NUMBER_OF_DIMENSIONS) scheme_signal_error ("dimarray_set: Wanted %d dimensions. " "Not more than %d dimensions possible. " "You may want to adjust 'MAX_NUMBER_OF_DIMENSIONS' " "and recompile.", ndim, MAX_NUMBER_OF_DIMENSIONS); for (i = 0; i < ndim; i++) { /* (2) (3) */ if (! SCHEME_INTP (ss_dims [i])) scheme_wrong_type ("dimarray_set", "Fixnum-Integer.", 0, 1, &shape); dimarray [i] = SCHEME_INT_VAL (ss_dims [i]); /* (4) */ if (dimarray [i] < 0) scheme_signal_error ("dimarray_set: Dimension %d negative.", i + 1); } return ndim; } /* (dimarray_take_matrix_shape ) -> integer Set dimarray to the shape of and return number-of-dimensions of . is a nesting of vectors that implements a multidimensional matrix in scheme. The innermost vector corresponds to the first dimension. Nesting-depth must not exceed MAX_NUMBER_OF_DIMENSIONS. Vectors of one dimension must be of the same length. */ static int dimarray_take_matrix_shape (Scheme_Object *matrix) { if (! SCHEME_VECTORP (matrix)) scheme_wrong_type ("dimarray_take_matrix_shape", "vector", 0, 1, &matrix); return dimarray_take_matrix_shape_rec (0, matrix); } /* Recursive helper-function of 'dimarray_take_matrix_shape'. */ static int dimarray_take_matrix_shape_rec (int n, Scheme_Object *matrix) { if (SCHEME_VECTORP (matrix)) if (0 <= n < MAX_NUMBER_OF_DIMENSIONS) { dimarray [n] = SCHEME_VEC_SIZE (matrix); return dimarray_take_matrix_shape_rec (n + 1, SCHEME_VEC_ELS (matrix) [0]); } else scheme_signal_error ("dimarray_take_matrix_shape_rec: " "Not more than %d dimensions possible. " "You may want to adjust 'MAX_NUMBER_OF_DIMENSIONS' " "and recompile.", MAX_NUMBER_OF_DIMENSIONS); dimarray_reverse (n); return n; } /* (dimarray_reverse ) Reverse the first integers of dimarray. */ static void dimarray_reverse (int n) { int rising = 0, falling = n - 1, tmp = 0; while (rising < falling) { tmp = dimarray [rising]; dimarray [rising] = dimarray [falling]; dimarray [falling] = tmp; ++ rising; -- falling; } } /* (idxarray_set_and_check ) -> - Convert each element of to an int and save the result in the extension-global variable idxarray. The result must not be equal-to or greater-than the corresponding element of . is a vector of scheme-integers. is an int. is a vector of int. Signal an error if: - (1a) does not contain elements. or for any element of : - (1b) contains more elements than MAX_NUMBER_OF_DIMENSIONS. - (2) the element is not an integer. - (3) the conversion is not possible. - (4) the result of the conversion is lesser-than zero or equal-to or greater-than the corresponding element of . */ static void idxarray_set_and_check (Scheme_Object *index, int ndim, const int* dims) { int i, nidx; Scheme_Object **ss_idxv; if (! SCHEME_VECTORP (index)) scheme_wrong_type ("idxarray_set_and_check", "vector", 0, 1, &index); nidx = SCHEME_VEC_SIZE (index); ss_idxv = SCHEME_VEC_ELS (index); /* (1a) */ if (nidx != ndim) scheme_signal_error ("idxarray_set_and_check: " "Number of index-dimensions: %d is not equal to " "number of array-dimensions: %d.", nidx, ndim); /* (1b) */ if (nidx > MAX_NUMBER_OF_DIMENSIONS) scheme_signal_error ("idxarray_set_and_check: Wanted %d dimensions. " "Not more than %d dimensions possible. " "You may want to adjust 'MAX_NUMBER_OF_DIMENSIONS' " "and recompile.", nidx, MAX_NUMBER_OF_DIMENSIONS); for (i = 0; i < nidx; i++) { /* (2) (3) */ if (! SCHEME_INTP (ss_idxv [i])) scheme_wrong_type ("idxarray_set_and_check: ", "Fixnum-Integer", 0, 1, &index); idxarray [i] = SCHEME_INT_VAL (ss_idxv [i]); /* (4) */ if (idxarray [i] < 0 || dims [i] <= idxarray [i]) scheme_signal_error ("idxarray_set_and_check: " "Dimension %d: Index outside array-boundaries. " "Index: %d. Legal index-range: 0 ... %d.", i + 1, idxarray [i], dims [i] - 1); } } /* (mxarray_index_to_offset ) Offset: Number of storage-bytes in memory that the ed element of is away from the first element of . */ static int mxarray_index_to_offset (mxArray* mx_array, Scheme_Object *index) { int ndim = mxGetNumberOfDimensions (mx_array); idxarray_set_and_check (index, ndim, mxGetDimensions (mx_array)); return mxGetElementSize (mx_array) * mxCalcSingleSubscript (mx_array, ndim, idxarray); } /* (mxarray_index_to_elnum ) Elnum: Number of storage-places in memory that the ed element of is away from the first element of . */ static int mxarray_index_to_elnum (mxArray* mx_array, Scheme_Object *index) { int ndim = mxGetNumberOfDimensions (mx_array); idxarray_set_and_check (index, ndim, mxGetDimensions (mx_array)); return mxCalcSingleSubscript (mx_array, ndim, idxarray); } /* (arrayel_get_with_offset ) -> - Set pointers of to real and (optional: imaginary) values of the element of that - in memory - is storage-bytes away from the first element of . Then, any mutation of the references of these pointers will result in a mutation of the element itself. */ static void arrayel_get_with_offset (mxArray* mx_array, Array_Element* element, int offset) { #ifdef DEBUG /* Access by index: idxarray_set_and_check is secure enough. */ /* This check is needed for access by raw memory-offset, only */ if (offset < 0 || mxGetElementSize (mx_array) * mxGetNumberOfElements (mx_array) <= offset) scheme_signal_error ("arrayel_get_with_offset: " "Value outside of array-storage. " "Offset of value: %d. " "Offset of last array-element: %d.", offset, mxGetElementSize (mx_array) * (mxGetNumberOfElements (mx_array) - 1)); #endif /* SET POINTERS HERE */ element->real = (void*) ((long) mxGetData (mx_array) + offset); if (mxIsComplex (mx_array)) element->imag = (void*) ((long) mxGetImagData (mx_array) + offset); else element->imag = NULL; element->storage_class = mxGetClassID (mx_array); element->complex_p = mxIsComplex (mx_array); } /* (arrayel_get_with_index ) -> - Set pointers of to real and (optional: imaginary) values of the ed element of . Then, any mutation of the references of these pointers will result in a mutation of the element itself. */ static void arrayel_get_with_index (mxArray* mx_array, Array_Element* element, Scheme_Object* index) { arrayel_get_with_offset (mx_array, element, mxarray_index_to_offset (mx_array, index)); } /***** CONVERT SCHEME->MATLAB ========================================== */ void set_dummy (void* dum, void* dumm, Scheme_Object* dummy) {} /* (set_(complexity)_(type) ) Switcher checks complexity and type of matlab-array and returns one of the following converter-setters which convert a to a the real and (optionally) complex part of a matlab-value which are then injected into the array. */ void set_real_cell (void* real, void* imag, Scheme_Object* value) { if (SCHEME_COMPLEXP (value)) scheme_wrong_type ("set_real_cell", "non-complex value", 0, 1, &value); if (MXARRAY_P (value)) { /* FIXME-REF */ /* mxArray-CREATOR: destroy old array, create copy of new, don't pass a reference. Reason: Two objects would point to one array. If one of them (object and array) gets garbage-collected, the other points straight into nirvana. */ mxDestroyArray (*((mxArray**) real)); /* <===O */ /* FIXME: what happens when duplicate-array fails? */ *((mxArray**) real) = mxDuplicateArray (MXARRAY_PTR (value)); /* <===O */ } else scheme_wrong_type ("set_real_cell", "array", 0, 1, &value); } void set_real_char (void* real, void* imag, Scheme_Object* value) { if (SCHEME_COMPLEXP (value)) scheme_wrong_type ("set_real_char", "non-complex value", 0, 1, &value); if (SCHEME_CHARP (value)) *((mxChar*) real) = (mxChar) SCHEME_CHAR_VAL (value); /* <===O */ else scheme_wrong_type ("set_real_char", "char", 0, 1, &value); } void set_real_double (void* real, void* imag, Scheme_Object* value) { if (SCHEME_COMPLEXP (value)) scheme_wrong_type ("set_real_double", "non-complex value", 0, 1, &value); if (SCHEME_REALP (value)) *((double*) real) = scheme_real_to_double (value); /* <===O */ else scheme_wrong_type ("set_real_double", "real", 0, 1, &value); } void set_real_int32 (void* real, void* imag, Scheme_Object* value) { if (SCHEME_COMPLEXP (value)) scheme_wrong_type ("set_real_int32", "non-complex value", 0, 1, &value); if (! SCHEME_EXACT_INTEGERP (value)) scheme_wrong_type ("set_real_int32", "integer", 0, 1, &value); if (scheme_get_int_val (value, (long*) real) == 0) /* <===O */ scheme_signal_error ("Number does not fit into int."); } void set_complex_double (void* real, void* imag, Scheme_Object* value) { if (SCHEME_COMPLEXP (value)) { *((double*) real) = scheme_real_to_double (scheme_complex_real_part (value)); /* <===O */ *((double*) imag) = scheme_real_to_double (scheme_complex_imaginary_part (value)); /* <===O */ } else if (SCHEME_REALP (value)) { *((double*) real) = scheme_real_to_double (value); /* <===O */ *((double*) imag) = 0.0; } else scheme_wrong_type ("set_complex_double", "number", 0, 1, &value); } ss_to_mx_setter ss_to_mx_setter_switch (int storage_class, int complex_p) { if (! complex_p) { switch (storage_class) { case mxCELL_CLASS: return &set_real_cell; break; case mxCHAR_CLASS: return &set_real_char; break; case mxDOUBLE_CLASS: return &set_real_double; break; case mxINT32_CLASS: return &set_real_int32; break; default: scheme_signal_error ("ss_to_mx_setter_switch: " "Noncomplex array must be of type " "'cell', 'char', 'double' or 'int32'."); } return &set_dummy; /* satisfy compiler */ } else { switch (storage_class) { case mxDOUBLE_CLASS: return &set_complex_double; break; case mxINT32_CLASS: default: scheme_signal_error ("ss_to_mx_setter_switch: " "Complex arrays must be of type 'double'."); } return &set_dummy; /* satisfy compiler */ } } /* (arrayel_set_convert ) The pointers of point to the real and (optional: imaginary) value of one element of an mxArray. Convert from Scheme to C and set the element to the result. Conversion and setting is done by the 'setter'. Several setters exist. Depending on the type of the array 'ss_to_mx_setter_switch' returns the appropriate setter-function. */ static void arrayel_set_convert (Array_Element* element, Scheme_Object* value) { ss_to_mx_setter setter = ss_to_mx_setter_switch (element->storage_class, element->complex_p); (*setter) (element->real, element->imag, value); return; } /* (mxarray_set_with_vector ) -> unspecified The elements of mxArrays are stored in one long vector in memory - no matter how many dimensions mxArray may have. Set each element of to the corresponding element of . */ static void mxarray_set_with_vector (mxArray* mx_array, Scheme_Object* vector) { ss_to_mx_setter setter = ss_to_mx_setter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); int i = 0, size = mxGetElementSize (mx_array), length = mxGetNumberOfElements (mx_array); long real = 0, imag = 0; Scheme_Object **vecels = NULL; if ((! SCHEME_VECTORP (vector)) || (length != SCHEME_VEC_SIZE (vector))) scheme_signal_error ("mxarray_set_with_vector: " "Number of elements in array: %d is not equal to " "number of elements in vector: %d", length, SCHEME_VEC_SIZE (vector)); vecels = SCHEME_VEC_ELS (vector); if (mxIsComplex (mx_array)) for (i = 0, real = ((long) mxGetData (mx_array)), imag = ((long) mxGetImagData (mx_array)); i < length; i++, real += size, imag += size) (*setter) ((void*) real, (void*) imag, vecels [i]); else for (i = 0, real = ((long) mxGetData (mx_array)); i < length; i++, real += size) (*setter) ((void*) real, NULL, vecels [i]); } /* (mxarray_fill_with_object ) -> unspecified The elements of mxArrays are stored in one long vector in memory - no matter how many dimensions mxArray may have. Set each element of to . FIXME: unefficient, convert once, only. */ static void mxarray_fill_with_object (mxArray* mx_array, Scheme_Object* object) { ss_to_mx_setter setter = ss_to_mx_setter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); long real = ((long) mxGetData (mx_array)), imag = 0, size = mxGetElementSize (mx_array), brim = real + size * mxGetNumberOfElements (mx_array); if (mxIsComplex (mx_array)) for (imag = ((long) mxGetImagData (mx_array)); real < brim; real += size, imag += size) (*setter) ((void*) real, (void*) imag, object); else for ( ; real < brim; real += size) (*setter) ((void*) real, NULL, object); } /* (scalar_mxarray_set_with_object ) -> unspecified contains one element. This element is set to . */ static void scalar_mxarray_set_with_object (mxArray* mx_array, Scheme_Object* object) { ss_to_mx_setter setter = ss_to_mx_setter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); if (mxIsComplex (mx_array)) (*setter) (mxGetData (mx_array), mxGetImagData (mx_array), object); else (*setter) (mxGetData (mx_array), NULL, object); } /* (mxarray_set_with_matrix ) -> unspecified Set every value of to the corresponding value of . is a nesting of vectors that implements a multidimensional matrix in scheme. The innermost vector corresponds to the first dimension. */ static void mxarray_set_with_matrix (mxArray* mx_array, Scheme_Object* matrix, int ndim) { ss_to_mx_setter setter = ss_to_mx_setter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); mxarray_set_with_matrix_rec (mx_array, setter, matrix, ndim - 1, 0); } /* Recursive helper-function of mxarray_set_with_matrix. */ static int mxarray_set_with_matrix_rec (mxArray* mx_array, ss_to_mx_setter setter, Scheme_Object* matrix, int n, int elnum) { int i, size; long real, imag; if (! SCHEME_VECTORP (matrix)) scheme_signal_error ("mxarray_set_with_matrix_rec: " "The nesting-depth of the nested-vector-matrix " "must not vary."); if (SCHEME_VEC_SIZE (matrix) != mxGetDimensions (mx_array) [n]) scheme_signal_error ("mxarray_set_with_matrix_rec: " "In dimension %d: vector-length = %d, size-of-array = %d. " "There should be no difference between those two.", n, SCHEME_VEC_SIZE (matrix), mxGetDimensions (mx_array) [n]); if (n == 0) { size = mxGetElementSize (mx_array); if (mxIsComplex (mx_array)) { for (i = 0, real = ((long) mxGetData (mx_array)) + elnum * size, imag = ((long) mxGetImagData (mx_array)) + elnum * size; i < SCHEME_VEC_SIZE (matrix); i ++, elnum ++, real += size, imag += size) (*setter) ((void*) real, (void*) imag, SCHEME_VEC_ELS (matrix) [i]); return elnum; } else { for (i = 0, real = ((long) mxGetData (mx_array)) + elnum * size; i < SCHEME_VEC_SIZE (matrix); i ++, elnum ++, real += size) (*setter) ((void*) real, NULL, SCHEME_VEC_ELS (matrix) [i]); return elnum; } } else { for (i = 0; i < SCHEME_VEC_SIZE (matrix); i++) elnum = mxarray_set_with_matrix_rec (mx_array, setter, SCHEME_VEC_ELS (matrix) [i], n - 1, elnum); return elnum; } } /***** CONVERT MATLAB->SCHEME ========================================== */ static Scheme_Object* convert_dummy (void* dum, void* my) { return scheme_void; } static Scheme_Object* convert_real_cell (void* real, void* imag) { if (*((mxArray**) real) == NULL) return scheme_false; /* be forgiving */ /* mxArray-CREATOR */ /* FIXME-REF */ /* FIXME: what happens when duplicate-array fails? */ return create_ss_array (mxDuplicateArray (*((mxArray**) real))); /* <===O */ } static Scheme_Object* convert_real_char (void* real, void* imag) { return scheme_make_char (*((char*) real)); /* <===O */ /* return scheme_make_char ((char) *((mxChar*) real)); */ } static Scheme_Object* convert_real_double (void* real, void* imag) { return scheme_make_double (*((double*) real)); /* <===O */ } static Scheme_Object* convert_real_int32 (void* real, void* imag) { return scheme_make_integer_value (*((int*) real)); /* <===O */ } static Scheme_Object* convert_complex_double (void* real, void* imag) { return scheme_make_complex (scheme_make_double (*((double*) real)), scheme_make_double (*((double*) imag))); /* <===O */} mx_to_ss_converter mx_to_ss_converter_switch (int storage_class, int complex_p) { if (!(complex_p)) { switch (storage_class) { case mxCELL_CLASS: return &convert_real_cell; break; case mxCHAR_CLASS: return &convert_real_char; break; case mxDOUBLE_CLASS: return &convert_real_double; break; case mxINT32_CLASS: return &convert_real_int32; break; default: scheme_signal_error ("mx_to_ss_converter_switch: Array is not of type " "'cell', 'char', 'double' or 'int32'."); return &convert_dummy; /* satisfy compiler */ } } else { switch (storage_class) { case mxDOUBLE_CLASS: return &convert_complex_double; break; case mxINT32_CLASS: default: scheme_signal_error ("mx_to_ss_converter_switch: Complex arrays " "are restricted to type 'double'"); return &convert_dummy; /* satisfy compiler */ } } } /* (convert_arrayel_ref The pointers of point to the real and (optional: imaginary) value of one element of an mxArray. Return the Scheme-, that is equal to this element. Conversion is done by the 'converter'. Several converters exist. Depending on the type of the array 'mx_to_ss_converter_switch' returns the appropriate converter. */ static Scheme_Object* convert_arrayel_ref (Array_Element* element) { mx_to_ss_converter converter; converter = mx_to_ss_converter_switch (element->storage_class, element->complex_p); return (*converter) (element->real, element->imag); } /* (mxarray_to_vector ) -> Each element of is equal to the corresponding element of . */ static Scheme_Object* mxarray_to_vector (mxArray *mx_array) { mx_to_ss_converter converter = mx_to_ss_converter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); int i = 0, size = mxGetElementSize (mx_array), length = mxGetNumberOfElements (mx_array); long real = 0, imag = 0; Scheme_Object *vector = scheme_make_vector (length, scheme_null), **vecels = SCHEME_VEC_ELS (vector); if (mxIsComplex (mx_array)) for (i = 0, real = ((long) mxGetData (mx_array)), imag = ((long) mxGetImagData (mx_array)); i < length; i++, real += size, imag += size) vecels [i] = (*converter) ((void*) real, (void*) imag); else for (i = 0, real = ((long) mxGetData (mx_array)); i < length; i++, real += size) vecels [i] = (*converter) ((void*) real, NULL); return vector; } /* (mxarray_to_object ) -> object Convert and return the single element of . */ static Scheme_Object* mxarray_to_object (mxArray *mx_array) { mx_to_ss_converter converter = mx_to_ss_converter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); if (mxIsComplex (mx_array)) return (*converter) (mxGetData (mx_array), mxGetImagData (mx_array)); else return (*converter) (mxGetData (mx_array), NULL); } /* (mxarray_to_matrix ) -> matrix */ static Scheme_Object* mxarray_to_matrix (mxArray *mx_array) { Scheme_Object *vector = NULL; mx_to_ss_converter converter = mx_to_ss_converter_switch (mxGetClassID (mx_array), mxIsComplex (mx_array)); mxarray_to_matrix_rec (mx_array, converter, &vector, mxGetNumberOfDimensions (mx_array) - 1, 0); return vector; } /* Recursive helper-function of mxarray_to_matrix. */ static int mxarray_to_matrix_rec (mxArray *mx_array, mx_to_ss_converter converter, Scheme_Object **vector_ptr, int n, int elnum) { int i, size; long real, imag; *vector_ptr = scheme_make_vector (mxGetDimensions (mx_array) [n], NULL); if (n == 0) { size = mxGetElementSize (mx_array); if (mxIsComplex (mx_array)) { for (i = 0, real = ((long) mxGetData (mx_array)) + elnum * size, imag = ((long) mxGetImagData (mx_array)) + elnum * size; i < SCHEME_VEC_SIZE (*vector_ptr); i ++, elnum ++, real += size, imag += size) SCHEME_VEC_ELS (*vector_ptr) [i] = (*converter) ((void*) real, (void*) imag); return elnum; } else { for (i = 0, real = ((long) mxGetData (mx_array)) + elnum * size; i < SCHEME_VEC_SIZE (*vector_ptr); i ++, elnum ++, real += size) SCHEME_VEC_ELS (*vector_ptr) [i] = (*converter) ((void*) real, NULL); return elnum; } } else { for (i = 0; i < SCHEME_VEC_SIZE (*vector_ptr); i++) elnum = mxarray_to_matrix_rec (mx_array, converter, SCHEME_VEC_ELS (*vector_ptr) + i, n - 1, elnum); return elnum; } } /***** SCHEME-PRIMITIVES: MAKE-ARRAY =================================== */ /* (make-cell-array #(dim-x dim-y ...) [object]) -> array (make-cell-array #(2 10)) => 2x10-Array-of-Arrays.*/ static Scheme_Object* mx_create_cell_array (int argc, Scheme_Object *argv []) { int ndim = dimarray_set (argv [0]); /* mxArray-CREATOR */ mxArray* mx_array = mxCreateCellArray (ndim, dimarray); if (mx_array == NULL) scheme_signal_error ("mx_create_cell_array: " "Not enough memory to allocate mxArray."); if (argc == 2) mxarray_fill_with_object (mx_array, argv [1]); return create_ss_array (mx_array); } /* (make-char-array #(dim-x dim-y ...) [object]) -> array */ static Scheme_Object* mx_create_char_array (int argc, Scheme_Object *argv []) { int ndim = dimarray_set (argv [0]); /* mxArray-CREATOR */ mxArray* mx_array = mxCreateCharArray (ndim, dimarray); if (mx_array == NULL) scheme_signal_error ("mx_create_char_array: " "Not enough memory to allocate mxArray."); if (argc == 2) mxarray_fill_with_object (mx_array, argv [1]); return create_ss_array (mx_array); } /* (make-array #(dim-x dim-y ...) [type-symbol] [complexity-symbol] [object]) -> array (make-array #(3 20) 'double 'real) => 3x20-noncomplex-double-array */ static Scheme_Object* mx_create_numeric_array (int argc, Scheme_Object *argv []) { int ndim; mxArray* mx_array; mxClassID type_id; bool complexity_id; ndim = dimarray_set (argv [0]); if (argc >= 2) if (! SCHEME_SYMBOLP (argv [1])) scheme_wrong_type ("mx_create_numeric_array", "symbol", 1, argc, argv); else type_id = type_symbol_to_id (argv [1]); else type_id = mxDOUBLE_CLASS; if (argc >= 3) if (! SCHEME_SYMBOLP (argv [2])) scheme_wrong_type ("mx_create_numeric_array", "symbol", 2, argc, argv); else complexity_id = complexity_symbol_to_id (argv [2]); else complexity_id = mxREAL; /* mxArray-CREATOR */ mx_array = create_numeric_array (ndim, dimarray, type_id, complexity_id); if (mx_array == NULL) scheme_signal_error ("mx_create_numeric_array: " "Not enough memory to allocate mxArray."); if (argc == 4) mxarray_fill_with_object (mx_array, argv [3]); return create_ss_array (mx_array); } /* (array-clone ) -> array */ static Scheme_Object* mx_duplicate_array (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_duplicate_array", "array", 0, argc, argv); return create_ss_array (mxDuplicateArray (MXARRAY_PTR (argv [0]))); } /***** SCHEME-PRIMITIVES: ARRAY-NAME -SHAPE -SET! AND -REF ============= */ /* (array-name [string]) -> string If [string] is specified, set name of to [string]. If [string] is not specified return name of . */ static Scheme_Object* mx_get_or_set_name (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_or_set_name", "array", 0, argc, argv); if (argc == 1) return scheme_make_string (mxGetName (MXARRAY_PTR (argv[0]))); if (! SCHEME_STRINGP (argv[1])) scheme_wrong_type ("mx_set_or_set_name", "string", 1, argc, argv); mxSetName (MXARRAY_PTR (argv[0]), SCHEME_STR_VAL (argv[1])); return (argv[1]); } /* (array-rank array): Array -> Integer Number of dimensions of array. */ static Scheme_Object* mx_get_number_of_dimensions (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_number_of_dimensions", "array", 0, argc, argv); return scheme_make_integer_value ((long) mxGetNumberOfDimensions (MXARRAY_PTR (argv [0]))); } /* (array-size array): Array -> Integer Number of elements of array. */ static Scheme_Object* mx_get_number_of_elements (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_number_of_elements", "array", 0, argc, argv); return scheme_make_integer_value ((long) mxGetNumberOfElements (MXARRAY_PTR (argv [0]))); } /* (array-shape ): ssArray -> Vector Integer-Vector that - for each dimension of array - contains the number of elements. */ static Scheme_Object* mx_get_dimensions (int argc, Scheme_Object *argv []) { mxArray* mx_array; Scheme_Object* vector; const int* dims; int i, ndim; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_dimensions", "array", 0, argc, argv); mx_array = MXARRAY_PTR (argv [0]); ndim = mxGetNumberOfDimensions (mx_array); dims = mxGetDimensions (mx_array); vector = scheme_make_vector (ndim, scheme_void); for (i = 0; i < ndim; i++) SCHEME_VEC_ELS (vector) [i] = scheme_make_integer_value ((long) dims [i]); return vector; } /* (array-set! ) -> unspecified */ static Scheme_Object* array_set (int argc, Scheme_Object *argv []) { Array_Element element = {0, 0, NULL, NULL}; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("array_set", "array", 0, argc, argv); arrayel_get_with_index (MXARRAY_PTR (argv [0]), &element, argv [1]); arrayel_set_convert (&element, argv [2]); return scheme_void; } /* (array-ref ) */ static Scheme_Object* array_ref (int argc, Scheme_Object* argv []) { Array_Element element = {0,0,NULL,NULL}; if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_ref", "array", 0, argc, argv); arrayel_get_with_index (MXARRAY_PTR (argv [0]), &element, argv [1]); return convert_arrayel_ref (&element); } /* (array-fill! ) -> unspecified */ static Scheme_Object* array_fill (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("array_fill", "array", 0, argc, argv); mxarray_fill_with_object (MXARRAY_PTR (argv [0]), argv [1]); return scheme_void; } /***** SCHEME-PRIMITIVES: ARRAY-TYPE AND COMPLEXITY ==================== */ /* (array-type-symbol ) -> symbol Common symbolic name of the storage-type of each element of . */ static Scheme_Object* mx_get_class_symbol (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_class_name", "array", 0, argc, argv); return scheme_intern_symbol (mxGetClassName (MXARRAY_PTR (argv[0]))); } /* (array-complexity ) -> symbol */ static Scheme_Object* mx_complexity_symbol (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_complexity_symbol", "array", 0, argc, argv); if (mxIsComplex (MXARRAY_PTR (argv[0]))) return scheme_intern_symbol ("complex"); else return scheme_intern_symbol ("real"); } /***** SCHEME-PRIMITIVES: PREDICATES =================================== */ /* (array? ) -> boolean */ static Scheme_Object* mx_is_array (int argc, Scheme_Object *argv []) { if (MXARRAY_P (argv[0])) return scheme_true; else return scheme_false; } /* (complex-array? ) -> */ static Scheme_Object* mx_is_complex (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_complex", "array", 0, argc, argv); if (mxIsComplex (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (real-array? ) -> */ static Scheme_Object* mx_is_real (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_real", "array", 0, argc, argv); if (mxIsComplex (MXARRAY_PTR (argv[0]))) return scheme_false; else return scheme_true; } /* (numeric-array? ) -> True if the array's storage type is one of: '(double sparse single int8 uint8 int16 uint16 int32 uint32) False if the array's storage type is one of: '(cell char object struct unknown) */ static Scheme_Object* mx_is_numeric (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_numeric", "array", 0, argc, argv); if (mxIsNumeric (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (cell-array? ) -> */ static Scheme_Object* mx_is_cell (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_cell", "array", 0, argc, argv); if (mxIsCell (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (char-array? ) -> */ static Scheme_Object* mx_is_char (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_char", "array", 0, argc, argv); if (mxIsChar (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (int32-array? ) -> */ static Scheme_Object* mx_is_int32 (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_int32", "array", 0, argc, argv); if (mxIsInt32 (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (double-array? ) -> */ static Scheme_Object* mx_is_double (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_double", "array", 0, argc, argv); if (mxIsDouble (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /* (empty-array? ) -> */ static Scheme_Object* mx_is_empty (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_is_empty", "array", 0, argc, argv); if (mxIsEmpty (MXARRAY_PTR (argv[0]))) return scheme_true; else return scheme_false; } /***** SCHEME-PRIMITIVES: COMMUNICATE WITH MATLAB-'ENGINE'-PROCESS ===== */ /* (engine-open) * Opens connection to matlab-engine. * Return true if connection can be established, false otherwise. * Other engine - commands can then be used. * A second call starts a new engine, and stops the old one. */ static Scheme_Object* eng_open (int argc, Scheme_Object *argv []) { Engine* new = engOpen (NULL); /* ((windows: NULL) . (unix: "\0") */ if (!ep && new) ep = new; else if (ep && new) { scheme_warning ("eng_open: Engine already open. Restart forced."); engClose (ep); ep = new; } else if (ep && !new) scheme_warning ("eng_open: Engine already open. Restart failed."); else if (!ep && !new) scheme_signal_error ("eng_open: Engine start failed."); return scheme_void; } /* (engine-close) * Close the matlab-engine. */ static Scheme_Object* eng_close (int argc, Scheme_Object *argv []) { if (ep) { engClose (ep); ep = NULL; } else scheme_warning ("eng_close: No need to kill engine. It's already dead."); return scheme_void; } /* (engine-eval string): string -> string * Evaluate string in matlab-environment and print the result * of the evaluation in the scheme-environment. * (engine-eval "2 + 2") => 4 * If there is no output in the matlab-environment, return scheme_void. */ static Scheme_Object* eng_eval_string (int argc, Scheme_Object *argv []) { if (ep == NULL) scheme_signal_error ("eng_eval_string: Engine offline."); if (! (SCHEME_STRINGP (argv[0]))) scheme_wrong_type ("eng_eval_string", "string", 0, argc, argv); engOutputBuffer (ep, stringbuf, STRING_BUFFER_SIZE - 1); engEvalString (ep, SCHEME_STR_VAL (argv[0])); if (0 < strlen (stringbuf)) return scheme_make_string (stringbuf); else return scheme_void; } /* (engine-put [string]) If [string] is specified assign name [string] to . Put to the matlab-engine. */ static Scheme_Object* eng_put_array (int argc, Scheme_Object *argv []) { if (ep == NULL) scheme_signal_error ("eng_put_array: Engine offline."); if (! MXARRAY_P (argv [0])) scheme_wrong_type ("eng_put_array", "array", 0, argc, argv); if (argc == 2) if (! SCHEME_STRINGP (argv[1])) scheme_wrong_type ("eng_put_array", "string", 1, argc, argv); else mxSetName (MXARRAY_PTR (argv[0]), SCHEME_STR_VAL (argv[1])); engPutArray (ep, MXARRAY_PTR (argv[0])); return scheme_void; } /* (engine-get ): string -> array Get mxArray named from the matlab-engine and return a newly allocated ssArray or signal an error if the attempt fails. The attempt fails if the named variable does not exist. All numeric arrays are imported as double. */ static Scheme_Object* eng_get_array (int argc, Scheme_Object *argv []) { mxArray* mx_array; if (ep == NULL) scheme_signal_error ("eng_get_array: Engine offline."); if (! SCHEME_STRINGP (argv[0])) scheme_wrong_type ("eng_get_array", "string", 0, argc, argv); /* mxArray-CREATOR */ mx_array = engGetArray (ep, SCHEME_STR_VAL (argv [0])); if (mx_array == NULL) scheme_signal_error ("eng_get_array: No mxArray of name '%s' " "exists in matlab-environment.", SCHEME_STR_VAL (argv [0])); if (mxGetNumberOfDimensions (mx_array) > MAX_NUMBER_OF_DIMENSIONS) scheme_signal_error ("eng_get_array: Import-Array is %d-dimensional. " "Not more than %d dimensions possible. " "You may want to adjust 'MAX_NUMBER_OF_DIMENSIONS' " "and recompile.", mxGetNumberOfDimensions (mx_array), MAX_NUMBER_OF_DIMENSIONS); /* 2002-05-08: Empty arrays are now supported. */ /* if (mxIsEmpty (mx_array)) */ /* scheme_signal_error ("eng_get_array: Import-Array is empty."); */ return create_ss_array (mx_array); } /***** SCHEME-PRIMITIVES: CONVERT STRING<->ARRAY VECTOR<->ARRAY OBJECT<->ARRAY */ /* (array->string ) -> string */ static Scheme_Object* mx_array_to_string (int argc, Scheme_Object *argv []) { mxArray *mx_array; char *string; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_array_to_string", "array", 0, argc, argv); mx_array = MXARRAY_PTR (argv [0]); if ((! mxIsChar (mx_array)) || (mxGetNumberOfDimensions (mx_array) != 2)) scheme_wrong_type ("mx_array_to_string", "2-dimensional-char-array", 0, argc, argv); string = mxArrayToString (mx_array); if (string == NULL) scheme_signal_error ("mx_array_to_string: Not enough memory."); /* ATTENTION: 'string' must be freed, so do NOT return a copy! The garbage-collector must free 'string'. */ return scheme_make_sized_offset_string (string, 0, mxGetNumberOfElements (mx_array), 0); } /* (string->array ) -> array */ static Scheme_Object* mx_create_string (int argc, Scheme_Object *argv []) { mxArray *mx_array; if (! SCHEME_STRINGP (argv [0])) scheme_wrong_type ("mx_create_string", "string", 0, argc, argv); /* mxArray-CREATOR */ mx_array = mxCreateString ((const char*) (SCHEME_STR_VAL (argv [0]))); if (mx_array == NULL) scheme_signal_error ("mx_create_string: " "Not enough memory to allocate mxArray."); return create_ss_array (mx_array); } /* (array->vector ) -> vector */ static Scheme_Object* array_to_vector (int argc, Scheme_Object *argv []) { mxArray *mx_array; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("array_to_vector", "array", 0, argc, argv); mx_array = MXARRAY_PTR (argv [0]); if ((mxGetNumberOfDimensions (mx_array) != 2) || (mxGetDimensions (mx_array) [1] != 1)) scheme_wrong_type ("array_to_vector", "#(n 1)-column-array", 0, argc, argv); return mxarray_to_vector (mx_array); } /* (vector->array [type-symbol] [complexity-symbol]) -> array Assign each element of to the corresponding element of a newly created #(-length 1)-array of type [type-symbol] and complexity [complexity-symbol]. Return the array. */ static Scheme_Object* vector_to_array (int argc, Scheme_Object *argv []) { int dims [2]; mxArray *mx_array; mxClassID type_id; bool complexity_id; if (! SCHEME_VECTORP (argv[0])) scheme_wrong_type ("vector_to_array", "vector", 0, argc, argv); dims [0] = SCHEME_VEC_SIZE (argv [0]); dims [1] = 1; /* n-by-1-column-array */ if (argc >= 2) if (! SCHEME_SYMBOLP (argv[1])) scheme_wrong_type ("vector_to_array", "symbol", 1, argc, argv); else type_id = type_symbol_to_id (argv [1]); else type_id = mxDOUBLE_CLASS; if (argc == 3) if (! SCHEME_SYMBOLP (argv[2])) scheme_wrong_type ("vector_to_array", "symbol", 2, argc, argv); else complexity_id = complexity_symbol_to_id (argv [2]); else complexity_id = mxREAL; /* mxArray-CREATOR */ mx_array = create_numeric_array (2, dims, type_id, complexity_id); if (mx_array == NULL) scheme_signal_error ("vector_to_array: " "Not enough memory to allocate mxArray."); mxarray_set_with_vector (mx_array, argv [0]); return create_ss_array (mx_array); } /* (array->object ) -> object */ static Scheme_Object* array_to_object (int argc, Scheme_Object *argv []) { mxArray *mx_array; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("array_to_object", "array", 0, argc, argv); mx_array = MXARRAY_PTR (argv [0]); if ((mxGetNumberOfDimensions (mx_array) != 2) || (mxGetDimensions (mx_array) [0] != 1) || (mxGetDimensions (mx_array) [1] != 1)) scheme_wrong_type ("array_to_object", "#(1 1)-scalar-array", 0, argc, argv); return mxarray_to_object (mx_array); } /* (object->array [type-symbol] [complexity-symbol]) -> array */ static Scheme_Object* object_to_array (int argc, Scheme_Object *argv []) { int dims [2] = {1, 1}; mxArray *mx_array; mxClassID type_id; bool complexity_id; if (argc == 1) { if (! SCHEME_REALP (argv [0])) scheme_wrong_type ("object_to_array", "real", 0, argc, argv); /* mxArray-CREATOR */ mx_array = mxCreateScalarDouble (scheme_real_to_double (argv [0])); if (mx_array == NULL) scheme_signal_error ("object_to_array: " "Not enough memory to allocate mxArray."); return create_ss_array (mx_array); } if (argc >= 2) if (! SCHEME_SYMBOLP (argv [1])) scheme_wrong_type ("number_to_array", "symbol", 1, argc, argv); else type_id = type_symbol_to_id (argv [1]); else type_id = mxDOUBLE_CLASS; if (argc == 3) if (! SCHEME_SYMBOLP (argv [2])) scheme_wrong_type ("number_to_array", "symbol", 2, argc, argv); else complexity_id = complexity_symbol_to_id (argv [2]); else complexity_id = mxREAL; /* mxArray-CREATOR */ mx_array = create_numeric_array (2, dims, type_id, complexity_id); if (mx_array == NULL) scheme_signal_error ("object_to_array: " "Not enough memory to allocate mxArray."); scalar_mxarray_set_with_object (mx_array, argv [0]); return create_ss_array (mx_array); } /* (matrix->array [type-symbol] [complexity-symbol]) -> array Assign each element of to the corresponding element of a newly created array of the same dimensions, type [type-symbol] and complexity [complexity-symbol]. is a nesting of vectors that implements a multidimensional matrix in scheme. The innermost vector corresponds to the first dimension. */ static Scheme_Object* matrix_to_array (int argc, Scheme_Object *argv []) { int ndim; mxArray *mx_array; mxClassID type_id; bool complexity_id; ndim = dimarray_take_matrix_shape (argv [0]); if (ndim == 1) scheme_wrong_type ("matrix_to_array", "nested-vectors", 0, argc, argv); if (argc >= 2) if (! SCHEME_SYMBOLP (argv [1])) scheme_wrong_type ("matrix_to_array", "symbol", 1, argc, argv); else type_id = type_symbol_to_id (argv [1]); else type_id = mxDOUBLE_CLASS; if (argc == 3) if (! SCHEME_SYMBOLP (argv [2])) scheme_wrong_type ("matrix_to_array", "symbol", 2, argc, argv); else complexity_id = complexity_symbol_to_id (argv [2]); else complexity_id = mxREAL; /* mxArray-CREATOR */ mx_array = create_numeric_array (ndim, dimarray, type_id, complexity_id); if (mx_array == NULL) scheme_signal_error ("matrix_to_array: " "Not enough memory to allocate mxArray."); mxarray_set_with_matrix (mx_array, argv [0], ndim); return create_ss_array (mx_array); } /* (array->matrix ) */ static Scheme_Object* array_to_matrix (int argc, Scheme_Object *argv []) { mxArray *mx_array; if (! MXARRAY_P (argv[0])) scheme_wrong_type ("array_to_matrix", "array", 0, argc, argv); mx_array = MXARRAY_PTR (argv [0]); return mxarray_to_matrix (mx_array); } /***** SCHEME-PRIMITIVES: INFO ========================================= */ /* Demonstrate use of _scheme_apply. */ static Scheme_Object* my_apply (char proc [], int argc, Scheme_Object *argv []) { /* _scheme_apply (, , ) */ return _scheme_apply (scheme_lookup_global (scheme_intern_symbol (proc), scheme_get_env (scheme_config)), argc, argv); } /* In Scheme, say: (playground-print-matlab-array-info) Demonstrate use of _scheme_apply. Demonstrate printing in Scheme. Print some info about matlab-arrays. */ static Scheme_Object* print_matlab_array_info (int argc, Scheme_Object *argv []) { Scheme_Object **args; args = (Scheme_Object**) scheme_malloc (32 * sizeof (Scheme_Object*)); args [0] = scheme_make_string ("\ mxArrays are homogenous: all elements of the array share one single type.\n\ Internally, types are identified by their ID which is a number.\n\ Following a table of corresponding ID and number:\n\ \n\ mxUNKNOWN_CLASS = ~s\n\ mxCELL_CLASS = ~s\n\ mxSTRUCT_CLASS = ~s\n\ mxOBJECT_CLASS = ~s\n\ mxCHAR_CLASS = ~s\n\ mxSPARSE_CLASS = ~s\n\ mxDOUBLE_CLASS = ~s\n\ mxSINGLE_CLASS = ~s\n\ mxINT8_CLASS = ~s\n\ mxUINT8_CLASS = ~s\n\ mxINT16_CLASS = ~s\n\ mxUINT16_CLASS = ~s\n\ mxINT32_CLASS = ~s\n\ mxUINT32_CLASS = ~s\n\ mxINT64_CLASS = ~s\n\ mxUINT64_CLASS = ~s\n\ mxREAL = ~s\n\ mxCOMPLEX = ~s\n\ "); args [1] = scheme_make_integer ((int) mxUNKNOWN_CLASS); args [2] = scheme_make_integer ((int) mxCELL_CLASS); args [3] = scheme_make_integer ((int) mxSTRUCT_CLASS); args [4] = scheme_make_integer ((int) mxOBJECT_CLASS); args [5] = scheme_make_integer ((int) mxCHAR_CLASS); args [6] = scheme_make_integer ((int) mxSPARSE_CLASS); args [7] = scheme_make_integer ((int) mxDOUBLE_CLASS); args [8] = scheme_make_integer ((int) mxSINGLE_CLASS); args [9] = scheme_make_integer ((int) mxINT8_CLASS); args [10] = scheme_make_integer ((int) mxUINT8_CLASS); args [11] = scheme_make_integer ((int) mxINT16_CLASS); args [12] = scheme_make_integer ((int) mxUINT16_CLASS); args [13] = scheme_make_integer ((int) mxINT32_CLASS); args [14] = scheme_make_integer ((int) mxUINT32_CLASS); args [15] = scheme_make_integer ((int) mxINT64_CLASS); args [16] = scheme_make_integer ((int) mxUINT64_CLASS); args [17] = scheme_make_integer ((int) mxREAL); args [18] = scheme_make_integer ((int) mxCOMPLEX); return my_apply ("printf", 19, args); } #ifdef DEBUG /***** DEBUG =========================================================== */ /* (array-index->offset ): ssArray Vector -> Integer Element-Number: Number of storage-places in memory that the ed element of is away from the first element of . */ static Scheme_Object* array_index_to_offset (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_index_to_offset", "Array", 0, argc, argv); return scheme_make_integer_value ((long) (mxarray_index_to_offset (MXARRAY_PTR (argv [0]), argv [1]))); } /* (array-index->elnum ): ssArray Vector -> Integer Element-Number: Number of storage-places in memory that the ed element of is away from the first element of . */ static Scheme_Object* array_index_to_elnum (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_index_to_elnum", "Array", 0, argc, argv); return scheme_make_integer_value ((long) (mxarray_index_to_elnum (MXARRAY_PTR (argv [0]), argv [1]))); } /* (array-bytes-per-element ): ssArray -> Integer Bytes used to store each single element of . */ static Scheme_Object* array_bytes_per_element (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_bytes_per_element", "Array", 0, argc, argv); return scheme_make_integer ((long) mxGetElementSize (MXARRAY_PTR (argv [0]))); } /* (array-element-ref ) -> value */ static Scheme_Object* array_element_ref (int argc, Scheme_Object *argv []) { mxArray *mx_array; Array_Element element = {0, 0, NULL, NULL}; if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_element_ref", "array", 0, argc, argv); if (! SCHEME_INTP (argv [1])) scheme_wrong_type ("array_element_ref", "fixnum", 1, argc, argv); mx_array = MXARRAY_PTR (argv [0]); arrayel_get_with_offset (mx_array, &element, mxGetElementSize (mx_array) * SCHEME_INT_VAL (argv [1])); return convert_arrayel_ref (&element); } /* (array-element-set! ) */ static Scheme_Object* array_element_set (int argc, Scheme_Object *argv []) { mxArray *mx_array; Array_Element element = {0, 0, NULL, NULL}; if (! MXARRAY_P (argv [0])) scheme_wrong_type ("array_element_set", "Array", 0, argc, argv); if (! SCHEME_INTP (argv [1])) scheme_wrong_type ("array_element_set", "Fixnum-Integer", 1, argc, argv); mx_array = MXARRAY_PTR (argv [0]); arrayel_get_with_offset (mx_array, &element, mxGetElementSize (mx_array) * SCHEME_INT_VAL (argv [1])); arrayel_set_convert (&element, argv [2]); return scheme_void; } /* (matlab-eps) -> flonum Eps is equal to the distance from 1.0 to the next largest floating-point number. Thus, it is a measure of floating-point accuracy. */ static Scheme_Object* matlab_eps (int argc, Scheme_Object *argv []) { return scheme_make_double (mxGetEps ()); } /* (compatible-array? ) -> boolean */ static Scheme_Object* is_compatible_array (int argc, Scheme_Object *argv []) { if (! SCHEME_SYMBOLP (argv [0])) scheme_wrong_type ("is_compatible_array", "symbol", 0, argc, argv); if (! SCHEME_SYMBOLP (argv [1])) scheme_wrong_type ("is_compatible_array", "symbol", 1, argc, argv); if (IS_COMPATIBLE_MXARRAY (type_symbol_to_id (argv [0]), complexity_symbol_to_id (argv [1]))) return scheme_true; else return scheme_false; } /* (type-symbol->id ) -> fixnum */ static Scheme_Object* debug_type_symbol_to_id (int argc, Scheme_Object *argv []) { if (! SCHEME_SYMBOLP (argv [0])) scheme_wrong_type ("debug_type_symbol_to_id", "symbol", 0, argc, argv); return scheme_make_integer ((long) type_symbol_to_id (argv [0])); } /* (complexity-symbol->id ) -> fixnum */ static Scheme_Object* debug_complexity_symbol_to_id (int argc, Scheme_Object *argv []) { if (! SCHEME_SYMBOLP (argv [0])) scheme_wrong_type ("debug_complexity_symbol_to_id", "symbol", 0, argc, argv); return scheme_make_integer ((long) complexity_symbol_to_id (argv [0])); } /* (array-type-id ) -> Common id-integer of the storage-type of each element of . */ static Scheme_Object* mx_get_class_id (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_class_id", "array", 0, argc, argv); return scheme_make_integer ((long) mxGetClassID (MXARRAY_PTR (argv[0]))); } /* (array-type-string ) -> Common name of the storage-type of each element of . */ static Scheme_Object* mx_get_class_name (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_get_class_name", "array", 0, argc, argv); return scheme_make_string (mxGetClassName (MXARRAY_PTR (argv[0]))); } /* (array-complexity-id ) -> integer */ static Scheme_Object* mx_complexity_id (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_complexity_id", "array", 0, argc, argv); return scheme_make_integer ((long) mxIsComplex (MXARRAY_PTR (argv[0]))); } /* (array-complexity-string ) -> string */ static Scheme_Object* mx_complexity_name (int argc, Scheme_Object *argv []) { if (! MXARRAY_P (argv[0])) scheme_wrong_type ("mx_complexity_name", "array", 0, argc, argv); if (mxIsComplex (MXARRAY_PTR (argv[0]))) return scheme_make_string ("complex"); else return scheme_make_string ("real"); } /* (matrix-shape ) -> vector Shape of . is a nesting of vectors that implements a multidimensional matrix in scheme. The innermost vector corresponds to the first dimension. */ static Scheme_Object* matrix_shape (int argc, Scheme_Object *argv []) { int i, ndim; Scheme_Object* shape; ndim = dimarray_take_matrix_shape (argv [0]); shape = scheme_make_vector (ndim, NULL); for (i = 0; i < ndim; i ++) SCHEME_VEC_ELS (shape) [i] = scheme_make_integer (dimarray [i]); return shape; } /***** PLAYGROUND ====================================================== */ /* (visible_finaliser ) Make finalization visible. Interacts with playground_eat_memory. */ static void playground_visible_finaliser (void* obj, void* nbytes) { scheme_warning ("visible-finaliser: %d bytes freed.", *((int*) nbytes)); } /* (playground-eat-memory ) Check if finalization ever happens. */ static Scheme_Object* playground_eat_memory (int argc, Scheme_Object *argv []) { void* memory; int nbytes; if (! SCHEME_INTP (argv [0])) scheme_wrong_type ("playground_eat_memory", "fixnum", 0, argc, argv); nbytes = SCHEME_INT_VAL (argv [0]); if (nbytes < 0) scheme_wrong_type ("playground_eat_memory", "non-negative fixnum", 0, argc, argv); memory = scheme_malloc_fail_ok (scheme_malloc_atomic, nbytes); scheme_warning ("Mmmmmhhhhh, %d bytes of memory. That was delicioussss.", nbytes); scheme_register_finalizer (memory, playground_visible_finaliser, (void*) &nbytes, NULL, NULL); return scheme_void; } #endif