├── .gitignore ├── test ├── Makefile ├── fileio.lua ├── bench.py ├── capi.lua ├── bench.lua ├── slice.lua ├── capi.c ├── array.lua └── ascii.dat ├── src ├── Makefile ├── debug.c ├── lunum.h ├── numarray.h ├── bin2c.lua ├── slicing.c ├── array_class.c ├── lunum_capi.c ├── array_class.lua ├── numarray.c └── lunum.c ├── setup ├── INSTALL ├── changelog.md ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Compiled source # 3 | ################### 4 | src/*.o 5 | src/*.a 6 | src/*.so 7 | src/*.lc 8 | 9 | 10 | # Lib install # 11 | ################### 12 | bin 13 | lib 14 | include 15 | 16 | 17 | 18 | lua-5.2.0 19 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | LUA = -I$(LUA_HOME)/include -L$(LUA_HOME)/lib -llua 4 | LUNUM = -I../src -L../src -llunum 5 | 6 | EXE = capi 7 | 8 | default : $(EXE) 9 | 10 | capi : capi.c ../src/liblunum.a 11 | $(CC) $(CFLAGS) -std=c99 -o $@ $< $(LUNUM) $(LUA) $(CLIBS) 12 | 13 | clean : 14 | rm -f $(EXE) 15 | -------------------------------------------------------------------------------- /test/fileio.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'lunum' 4 | 5 | local A = lunum.loadtxt("test/ascii.dat") 6 | print("[ 33, 9 ] ?= ", A:shape('array')) 7 | 8 | local B = lunum.range(4):astype(lunum.double) 9 | B:tofile("A.bin") 10 | local C = lunum.fromfile("A.bin", lunum.double) 11 | print("[ true, true, true, true ] ?= ", B:eq(C)) 12 | 13 | -------------------------------------------------------------------------------- /test/bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import numpy as np 5 | 6 | 7 | def Test1(N): 8 | A = np.zeros([N,N]) 9 | for i in range(50): 10 | B = np.sin(A) + np.cos(A) 11 | 12 | 13 | if __name__ == "__main__": 14 | from timeit import Timer 15 | t = Timer("Test1(512)", "from __main__ import Test1") 16 | print "numpy: test took %f seconds" % t.timeit(number=1) 17 | -------------------------------------------------------------------------------- /test/capi.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | local array = lunum.array 5 | 6 | local A = test_upcast({1,2,3}, lunum.char) 7 | local B = test_upcast(array({1,2,3}), lunum.double) 8 | local C = test_upcast(array({1,2,3}, lunum.int), lunum.long) 9 | local D = test_upcast({1,2,3}, lunum.complex) 10 | 11 | 12 | print(A:dtype(), A) 13 | print(B:dtype(), B) 14 | print(C:dtype(), C) 15 | print(D:dtype(), D) 16 | 17 | print(A + {1,2,3}) 18 | 19 | print(test_astable()) 20 | print(A.__cstruct) 21 | 22 | 23 | test_checkarray(A, B, lunum.array({1,2,3,4,5}), {1,2,3,4}) 24 | -------------------------------------------------------------------------------- /test/bench.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'lunum' 4 | 5 | 6 | local function Test1(N) 7 | 8 | local start = os.clock() 9 | 10 | local A = lunum.zeros({N,N}) 11 | for i=0,50 do 12 | local B = lunum.sin(A) + lunum.cos(A) 13 | end 14 | 15 | print(string.format("lunum: test took %f seconds", os.clock() - start)) 16 | end 17 | 18 | collectgarbage("setpause", 50) 19 | collectgarbage("setstepmul", 200) 20 | Test1(512) 21 | 22 | 23 | -- v0.5.1, no GC: 229 24 | -- v0.5.1, w/ GC: 232 25 | 26 | -- v0.5.2, no GC: 201 27 | -- v0.5.2, w/ GC: 197 28 | 29 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | LUA = $(LUA_HOME)/bin/lua 4 | 5 | default : $(LUNUM_A) $(LUNUM_SO) 6 | 7 | %.o : %.c 8 | $(CC) $(CFLAGS) -std=c99 -c $< 9 | 10 | lunum.o : lunum.c array_class.lc 11 | $(CC) $(CFLAGS) -std=c99 -c $< -I$(LUA_HOME)/include 12 | 13 | lunum_capi.o : lunum_capi.c 14 | $(CC) $(CFLAGS) -std=c99 -c $< -I$(LUA_HOME)/include 15 | 16 | array_class.o : array_class.c 17 | $(CC) $(CFLAGS) -std=c99 -c $< -I$(LUA_HOME)/include 18 | 19 | $(LUNUM_A) : numarray.o lunum.o lunum_capi.o array_class.o 20 | $(AR) $@ $? 21 | 22 | $(LUNUM_SO) : numarray.o lunum.o lunum_capi.o array_class.o 23 | $(SO) -o $(LUNUM_SO) $^ 24 | 25 | array_class.lc : array_class.lua 26 | $(LUA) bin2c.lua +$< > $@ 27 | 28 | clean : 29 | rm -f *.o $(LUNUM_A) $(LUNUM_SO) *.lc 30 | -------------------------------------------------------------------------------- /setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lua="lua-5.2.0" 4 | luabin=$(pwd)/${lua}/bin 5 | 6 | 7 | if [ $(uname) == "Darwin" ]; then 8 | arch="macosx" 9 | fi 10 | 11 | if [ $(uname) == "Linux" ]; then 12 | arch="linux" 13 | fi 14 | 15 | if [ ! -d ${lua} ]; then 16 | echo "Downloading and installing Lua..." 17 | wget http://www.lua.org/ftp/${lua}.tar.gz 18 | tar xvf ${lua}.tar.gz 19 | rm ${lua}.tar.gz 20 | cd ${lua}; make $arch; make install INSTALL_TOP=$(pwd); cd ..; 21 | fi 22 | 23 | PATH=${PATH/${luabin}:/""} # Remove lua/bin from path if it exists 24 | 25 | export PATH=${luabin}:$PATH 26 | export LUA_CPATH=$(pwd)/src/?.so 27 | export LUA_HOME=$(pwd)/${lua} 28 | 29 | echo "*** run with 'source' to export these environment variables for build: ***" 30 | echo "PATH=${PATH}" 31 | echo "LUA_CPATH=${LUA_CPATH}" 32 | echo "LUA_HOME=${LUA_HOME}" 33 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | static void _stackdump(lua_State* l) 5 | { 6 | int i; 7 | int top = lua_gettop(l); 8 | 9 | printf("total in stack %d\n",top); 10 | 11 | for (i = 1; i <= top; i++) 12 | { /* repeat for each level */ 13 | int t = lua_type(l, i); 14 | switch (t) { 15 | case LUA_TSTRING: /* strings */ 16 | printf("string: '%s'\n", lua_tostring(l, i)); 17 | break; 18 | case LUA_TBOOLEAN: /* booleans */ 19 | printf("boolean %s\n",lua_toboolean(l, i) ? "true" : "false"); 20 | break; 21 | case LUA_TNUMBER: /* numbers */ 22 | printf("number: %g\n", lua_tonumber(l, i)); 23 | break; 24 | default: /* other values */ 25 | printf("%s\n", lua_typename(l, t)); 26 | break; 27 | } 28 | printf(" "); /* put a separator */ 29 | } 30 | printf("\n"); /* end the listing */ 31 | } 32 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | _ _ _ _ _ _ ____ 2 | |"| U |"|u| | | \ |"| U /"\ uU | _"\ u 3 | U | | u \| |\| |<| \| |> \/ _ \/ \| |_) |/ 4 | \| |/__ | |_| |U| |\ |u / ___ \ | _ < 5 | |_____|<<\___/ |_| \_| /_/ \_\ |_| \_\ 6 | // \\(__) )( || \\,-.\\ >> // \\_ 7 | (_")("_) (__) (_") (_/(__) (__)(__) (__) 8 | 9 | Numeric arrays for Lua 10 | 11 | 12 | 13 | 14 | 15 | INSTALLTION INSTRUCTIONS 16 | 17 | (1a) ./setup [will download and install Lua to the currect directory] 18 | (1b) source ./setup [will also export build evironment for local Lua install] 19 | (2) make 20 | 21 | 22 | 23 | RUNNING TESTS 24 | 25 | (1) make test 26 | (2) lua test/array.lua 27 | 28 | 29 | 30 | TO specify the location of Lua header and library files, put 31 | LUA_HOME=/path/to/lua at the end of the make command. 'make install' 32 | creates lib and include directories locally. To specify a different 33 | location, use INSTALL_TOP=/path/to/lunum. To load lunum as a Lua 34 | extension, set the environment variable as follows: 35 | 36 | export LUA_CPATH=/path/to/lunum/lib/?.so 37 | 38 | -------------------------------------------------------------------------------- /src/lunum.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __NumluaCapi_HEADER__ 4 | #define __NumluaCapi_HEADER__ 5 | 6 | #include "numarray.h" 7 | #include "lualib.h" 8 | 9 | typedef unsigned char Bool; 10 | int luaopen_lunum(lua_State *L); 11 | 12 | struct Array *lunum_checkarray1(lua_State *L, int pos); 13 | void *lunum_checkarray2(lua_State *L, int pos, enum ArrayType T, int *N); 14 | void lunum_pusharray1(lua_State *L, struct Array *B); 15 | void lunum_pusharray2(lua_State *L, void *data, enum ArrayType T, int N); 16 | void lunum_astable(lua_State *L, int pos); 17 | int lunum_upcast(lua_State *L, int pos, enum ArrayType T, int N); 18 | int lunum_hasmetatable(lua_State *L, int pos, const char *name); 19 | void *lunum_tovalue(lua_State *L, enum ArrayType T); 20 | 21 | #ifndef LUNUM_API_NOCOMPLEX 22 | #include 23 | typedef double complex Complex; 24 | Complex lunum_checkcomplex(lua_State *L, int n); 25 | void lunum_pushcomplex(lua_State *L, Complex z); 26 | #endif // LUNUM_API_COMPLEX 27 | 28 | #ifdef LUNUM_PRIVATE_API 29 | void _lunum_register_array(lua_State *L, struct Array *B); 30 | #endif // LUNUM_PRIVATE_API 31 | 32 | #endif // __NumluaCapi_HEADER__ 33 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # Version 0.5.3 6 | -------------------------------------------------------------------------------- 7 | 8 | ## Features 9 | 10 | + Slicing operations as r-values 11 | + Read ASCII tables from disk with `lunum.loadtxt` 12 | + Read binary tables from disk with `lunum.fromfile` 13 | + Write binary files with `array:tofile` 14 | 15 | 16 | ## Performance 17 | 18 | + There is now a small series of tests that tests performace against the same 19 | operations in Numpy. Results are that Numpy is faster by factors of between 1 20 | and 3. This will evenually be improved by reducing the number of `memcpy` 21 | operations. 22 | 23 | 24 | 25 | # Version 0.5.2 26 | -------------------------------------------------------------------------------- 27 | 28 | ## Bug fixes 29 | 30 | + The C API fixes the lunum_upcast function, which was handling scalars assuming 31 | they were doubles 32 | 33 | 34 | ## Performance 35 | 36 | + The Lua library now owns the data buffers used by Lunum arrays. This makes 37 | garbage collection able to operate correctly, since Lua is aware of how much 38 | memory it is using 39 | 40 | 41 | 42 | # Version 0.5.1 43 | -------------------------------------------------------------------------------- 44 | First stable public release of Lunum 45 | -------------------------------------------------------------------------------- /test/slice.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'lunum' 4 | 5 | local A = lunum.range(200) 6 | local B = A:reshape{10,10,2} 7 | 8 | if true then 9 | print("testing manual slicing function") 10 | print(lunum.slice(A,0,10,1,0)) 11 | print(lunum.slice(A,0,10,2,0)) 12 | print(lunum.slice(A,0,10,3,0)) 13 | print(lunum.slice(A,0,10,4,0)) 14 | 15 | print("testing squeeze...") 16 | print("[ 5, 5, 1 ] ?= ", lunum.slice(B, {0,0,0}, {10,10,2}, {2,2,2}, {0,0,0}):shape('array')) 17 | print("[ 5, 5 ] ?= ", lunum.slice(B, {0,0,0}, {10,10,2}, {2,2,2}, {0,0,1}):shape('array')) 18 | 19 | print("testing slicing with tables...") 20 | print("[ 0, 4 ] ?= ", A[{{0,8,4}}]) 21 | print("[ 2, 2, 2 ] ?= ", B[{{0,10,5}, {0,10,5}, {0,2,1}}]:shape('array')) 22 | print("[ 2, 2 ] ?= ", B[{{0,10,5}, {0,10,5}, 0}]:shape('array')) 23 | 24 | print("testing slicing with strings...") 25 | print("[ 0, 4 ] ?= ", A['0:8:4']) 26 | print("[ 2, 2, 2 ] ?= ", B['0:10:5,0:10:5,0:2:1']:shape('array')) 27 | print("[ 2, 2 ] ?= ", B['0:10:5,0:10:5, 0 ' ]:shape('array')) 28 | end 29 | 30 | print(" 45 ?= ", B['2,2,1']) 31 | print(" 45 ?= ", B[{2,2,1}]) 32 | print(" 45 ?= ", B( 2,2,1 )) 33 | 34 | 35 | print(A:shape('array')) 36 | print(A[(A+1):astype(lunum.bool)]:shape('array')) 37 | print(A[(A+1):astype(lunum.bool)]) 38 | 39 | -------------------------------------------------------------------------------- /test/capi.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include "lunum.h" 5 | #include "lauxlib.h" 6 | 7 | 8 | static int test_upcast(lua_State *L); 9 | static int test_astable(lua_State *L); 10 | static int test_checkarray(lua_State *L); 11 | 12 | 13 | int main() 14 | { 15 | lua_State *L = luaL_newstate(); 16 | luaL_openlibs(L); 17 | luaopen_lunum(L); 18 | 19 | lua_register(L, "test_upcast", test_upcast); 20 | lua_register(L, "test_astable", test_astable); 21 | lua_register(L, "test_checkarray", test_checkarray); 22 | 23 | if (luaL_dofile(L, "test/capi.lua")) { 24 | printf("%s\n", lua_tostring(L, -1)); 25 | } 26 | 27 | lua_close(L); 28 | return 0; 29 | } 30 | 31 | 32 | int test_upcast(lua_State *L) 33 | { 34 | enum ArrayType T = luaL_checkinteger(L, 2); 35 | 36 | if (lunum_upcast(L, 1, T, 0)) { 37 | lua_replace(L, 1); 38 | } 39 | 40 | lua_settop(L, 1); 41 | return 1; 42 | } 43 | 44 | int test_astable(lua_State *L) 45 | { 46 | int N = 100; 47 | struct Array A = array_new_zeros(N, ARRAY_TYPE_DOUBLE); 48 | lunum_pusharray1(L, &A); 49 | 50 | lunum_astable(L, -1); 51 | lua_replace(L, -2); 52 | 53 | return 1; 54 | } 55 | 56 | int test_checkarray(lua_State *L) 57 | { 58 | int narg = lua_gettop(L); 59 | for (int i=1; i<=narg; ++i) { 60 | int N; 61 | double *data = (double*)lunum_checkarray2(L, i, ARRAY_TYPE_DOUBLE, &N); 62 | printf("argument %d had length %d\n", i, N); 63 | } 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /src/numarray.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __Array_HEADER__ 4 | #define __Array_HEADER__ 5 | 6 | enum ArrayType { 7 | ARRAY_TYPE_BOOL, 8 | ARRAY_TYPE_CHAR, 9 | ARRAY_TYPE_SHORT, 10 | ARRAY_TYPE_INT, 11 | ARRAY_TYPE_LONG, 12 | ARRAY_TYPE_FLOAT, 13 | ARRAY_TYPE_DOUBLE, 14 | ARRAY_TYPE_COMPLEX, 15 | }; 16 | 17 | enum ArrayOperation { 18 | ARRAY_OP_ADD, 19 | ARRAY_OP_SUB, 20 | ARRAY_OP_MUL, 21 | ARRAY_OP_DIV, 22 | ARRAY_OP_POW, 23 | }; 24 | 25 | struct Array 26 | { 27 | void *data; 28 | enum ArrayType dtype; 29 | int size, ndims, owns; 30 | int *shape; 31 | } ; 32 | 33 | char *array_typename(enum ArrayType T); 34 | enum ArrayType array_typeflag(char c); 35 | struct Array array_new_zeros(int N, enum ArrayType T); 36 | struct Array array_new_copy(const struct Array *B, enum ArrayType T); 37 | struct Array array_new_from_slice(const struct Array *B1, 38 | int *start, int *stop, int *skip, int Nd); 39 | struct Array array_new_from_mask(const struct Array *B1, struct Array *M); 40 | void array_del(struct Array *A); 41 | void array_assign_from_scalar(struct Array *A, const void *val); 42 | void array_assign_from_array(struct Array *A, const struct Array *B); 43 | void array_binary_op(const struct Array *A, 44 | const struct Array *B, 45 | struct Array *C, enum ArrayOperation op); 46 | int array_sizeof(enum ArrayType T); 47 | int array_resize(struct Array *A, const int *N, int Nd); 48 | void array_extract_slice(struct Array *B0, const struct Array *B1, 49 | int *start, int *size, int *stride, int Nd); 50 | 51 | #endif // __Array_HEADER__ 52 | -------------------------------------------------------------------------------- /src/bin2c.lua: -------------------------------------------------------------------------------- 1 | local description = [=[ 2 | Usage: lua bin2c.lua [+]filename [status] 3 | 4 | Write a C source file to standard output. When this C source file is 5 | included in another C source file, it has the effect of loading and 6 | running the specified file at that point in the program. 7 | 8 | The file named by 'filename' contains either Lua byte code or Lua source. 9 | Its contents are used to generate the C output. If + is used, then the 10 | contents of 'filename' are first compiled before being used to generate 11 | the C output. If given, 'status' names a C variable used to store the 12 | return value of either luaL_loadbuffer() or lua_pcall(). Otherwise, 13 | the return values of these functions will be unavailable. 14 | 15 | This program is (overly) careful to generate output identical to the 16 | output generated by bin2c5.1 from LuaBinaries. 17 | 18 | http://lua-users.org/wiki/BinTwoCee 19 | ]=] 20 | 21 | if not arg or not arg[1] then 22 | io.stderr:write(description) 23 | return 24 | end 25 | 26 | local compile, filename = arg[1]:match"^(+?)(.*)" 27 | local status = arg[2] 28 | 29 | local content = compile=="+" 30 | and string.dump(assert(loadfile(filename))) 31 | or assert(io.open(filename,"rb")):read"*a" 32 | 33 | local function boilerplate(fmt) 34 | return string.format(fmt, 35 | status and "("..status.."=" or "", 36 | filename, 37 | status and ")" or "", 38 | status and status.."=" or "", 39 | filename) 40 | end 41 | 42 | local dump do 43 | local numtab={}; for i=0,255 do numtab[string.char(i)]=("%3d,"):format(i) end 44 | function dump(str) 45 | return (str:gsub(".", numtab):gsub(("."):rep(80), "%0\n")) 46 | end 47 | end 48 | 49 | io.write(boilerplate[=[ 50 | /* code automatically generated by bin2c -- DO NOT EDIT */ 51 | { 52 | /* #include'ing this file in a C program is equivalent to calling 53 | if (%sluaL_loadfile(L,%q)%s==0) %slua_pcall(L, 0, 0, 0); 54 | */ 55 | /* %s */ 56 | static const unsigned char B1[]={ 57 | ]=], dump(content), boilerplate[=[ 58 | 59 | }; 60 | 61 | if (%sluaL_loadbuffer(L,(const char*)B1,sizeof(B1),%q)%s==0) %slua_pcall(L, 0, 0, 0); 62 | } 63 | ]=]) 64 | -------------------------------------------------------------------------------- /src/slicing.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef unsigned char Bool; 9 | typedef double complex Complex; 10 | 11 | 12 | enum ArrayType { 13 | ARRAY_TYPE_BOOL, 14 | ARRAY_TYPE_CHAR, 15 | ARRAY_TYPE_SHORT, 16 | ARRAY_TYPE_INT, 17 | ARRAY_TYPE_LONG, 18 | ARRAY_TYPE_FLOAT, 19 | ARRAY_TYPE_DOUBLE, 20 | ARRAY_TYPE_COMPLEX, 21 | }; 22 | 23 | struct Buffer { 24 | int size, owner; 25 | void *data; 26 | enum ArrayType dtype; 27 | } ; 28 | 29 | int buffer_sizeof(enum ArrayType T) 30 | { 31 | switch (T) { 32 | case ARRAY_TYPE_BOOL : return sizeof(Bool); 33 | case ARRAY_TYPE_CHAR : return sizeof(char); 34 | case ARRAY_TYPE_SHORT : return sizeof(short); 35 | case ARRAY_TYPE_INT : return sizeof(int); 36 | case ARRAY_TYPE_LONG : return sizeof(long); 37 | case ARRAY_TYPE_FLOAT : return sizeof(float); 38 | case ARRAY_TYPE_DOUBLE : return sizeof(double); 39 | case ARRAY_TYPE_COMPLEX : return sizeof(Complex); 40 | } 41 | } 42 | 43 | struct Buffer buffer_new(int size, enum ArrayType T, void *data) 44 | { 45 | struct Buffer buf; 46 | 47 | if (data == NULL) { 48 | buf.data = malloc(size*buffer_sizeof(T)); 49 | buf.owner = 1; 50 | } 51 | else { 52 | buf.data = data; 53 | buf.owner = 0; 54 | } 55 | 56 | buf.size = size; 57 | buf.dtype = T; 58 | 59 | return buf; 60 | } 61 | 62 | void buffer_free(struct Buffer *buf) 63 | { 64 | if (buf->owner) free(buf->data); 65 | buf->data = NULL; 66 | buf->dtype = -1; 67 | buf->size = 0; 68 | buf->owner = 0; 69 | } 70 | 71 | void buffer_extract_slice(struct Buffer *B0, const struct Buffer *B1, 72 | int *start, int *size, int *stride, int Nd) 73 | // ----------------------------------------------------------------------------- 74 | // Extract the slice from B1, and insert it contiguously into B0 75 | // ----------------------------------------------------------------------------- 76 | // @start : starting indices into B1 77 | // @size : number of entries to extract along each axis 78 | // @stride : distance between entries of B1 along each axis 79 | // @Nd : the number of axes in each buffer 80 | // ----------------------------------------------------------------------------- 81 | { 82 | 83 | char *b0 = (char*) B0->data; 84 | char *b1 = (char*) B1->data; 85 | 86 | int *N = size; 87 | int *S = stride; 88 | int *J = (int*) malloc(Nd*sizeof(int)); 89 | for (int d=0; ddtype); 92 | int m = 0; // indexes into B0, advanced uniformly 93 | 94 | while (J[0] < N[0]) { 95 | 96 | int M = 0; 97 | for (int d=0; d A ? ", A:gt(B)) 131 | print("A >= A ? ", A:ge(B)) 132 | end 133 | 134 | local function test12() 135 | print "testing type flags" 136 | local C = lunum.array({1,2,3}, 'd') 137 | print("double ?= ", C:dtype()) 138 | print("complex ?= ", C:astype('z'):dtype()) 139 | print("[ 1+0j, 2+0j, 3+0j ] ?= ", C:astype('z')) 140 | end 141 | 142 | test1() 143 | test2() 144 | 145 | test3() 146 | test4() 147 | test5() 148 | test6() 149 | test7() 150 | test8() 151 | test9() 152 | test10() 153 | 154 | test11() 155 | test12() 156 | -------------------------------------------------------------------------------- /test/ascii.dat: -------------------------------------------------------------------------------- 1 | 2.530997889450e-03 2.484492163202e-01 5.521848896575e-02 1.031667e+00 -1.408505e-02 8.266572e-03 4.717873e-02 2.659149e+00 5.674526e-02 2 | 2.409662664859e-03 2.461813106648e-01 2.582681268618e-02 1.033739e+00 -9.920481e-03 -1.117081e-03 2.135003e-02 2.657949e+00 8.687002e-02 3 | 1.575304759969e-03 2.449647211622e-01 1.183633804572e-01 1.032065e+00 -4.672312e-03 1.320985e-02 1.406115e-01 2.641295e+00 1.838422e-02 4 | 5.687616891451e-03 2.413833018256e-01 1.122210013244e-02 1.038139e+00 -3.435971e-02 -8.249153e-03 8.509153e-03 2.657475e+00 1.263769e-03 5 | 1.120485742758e-03 2.373289737659e-01 4.155420335613e-02 1.041615e+00 -2.794008e-02 -1.720275e-02 3.867671e-02 2.669676e+00 2.553312e-02 6 | 9.668415543252e-03 2.496277810183e-01 1.376929204899e-01 1.032748e+00 -5.658958e-03 2.304603e-02 1.619448e-01 2.635683e+00 6.644522e-03 7 | 9.004179609717e-03 2.496319351355e-01 2.059813235009e-01 1.022267e+00 -3.352227e-03 1.911648e-02 2.973198e-01 2.601662e+00 3.026479e-02 8 | 6.287262036912e-03 2.428371207430e-01 7.468465163654e-02 1.038922e+00 -3.496406e-02 5.951986e-03 7.051805e-02 2.654077e+00 1.448433e-02 9 | 1.387013342463e-03 2.377874309724e-01 1.022988922524e-01 1.036563e+00 -3.276430e-02 9.191270e-03 1.149196e-01 2.647708e+00 1.696741e-02 10 | 5.789058757727e-03 2.393405679872e-01 1.608230217131e-01 1.029490e+00 -3.690016e-02 1.437261e-02 2.008271e-01 2.621643e+00 3.013206e-03 11 | 4.144879756121e-03 2.281061643845e-01 1.762752614350e-02 1.040361e+00 -5.585691e-02 -1.721190e-02 1.182643e-02 2.656217e+00 4.552432e-03 12 | 6.314648282475e-03 2.415554662302e-01 1.917706806802e-01 1.025410e+00 -3.024961e-02 2.081974e-02 2.756056e-01 2.607412e+00 1.325533e-02 13 | 1.634544426099e-02 2.458177139336e-01 1.396662796223e-02 1.043115e+00 -4.257909e-02 2.521753e-02 9.951802e-03 2.663859e+00 7.579899e-04 14 | 1.284350713537e-02 2.436459183225e-01 3.985910422751e-02 1.039877e+00 -4.029061e-02 2.838449e-02 3.889186e-02 2.654277e+00 8.443577e-03 15 | 3.342730811247e-03 2.317078666331e-01 6.681745904435e-02 1.042313e+00 -5.398634e-02 -1.702976e-02 7.212433e-02 2.661181e+00 2.123895e-02 16 | 3.525947043887e-03 2.314399813144e-01 9.351846134938e-02 1.040549e+00 -4.366250e-02 -3.577635e-03 1.084592e-01 2.655170e+00 1.624437e-03 17 | 1.402266595036e-02 2.459471222037e-01 9.544901730043e-02 1.040859e+00 -4.700251e-02 3.101349e-02 1.051778e-01 2.655515e+00 5.755492e-05 18 | 3.830945986763e-03 2.321871862290e-01 1.194488868324e-01 1.035084e+00 -6.120668e-02 -2.640806e-03 1.447821e-01 2.637053e+00 5.241311e-05 19 | 1.330191860393e-02 2.396672356806e-01 1.482556628753e-01 1.031931e+00 -4.890074e-02 2.091305e-02 1.890358e-01 2.626448e+00 6.378030e-04 20 | 3.980238754044e-05 2.159385044855e-01 8.704299813631e-03 1.041268e+00 -7.746376e-02 -2.618230e-02 -4.738992e-03 2.655477e+00 7.497029e-05 21 | 1.247397087785e-02 2.289107102314e-01 8.828353027322e-03 1.041673e+00 -6.946140e-02 -3.198570e-03 2.359166e-03 2.656870e+00 4.614228e-05 22 | 1.190776253950e-02 2.309862110069e-01 3.649011844207e-02 1.040328e+00 -5.642773e-02 1.017732e-02 3.436343e-02 2.652320e+00 9.482259e-05 23 | 4.237538860651e-03 2.212139859209e-01 5.837754507136e-02 1.039499e+00 -7.747207e-02 -1.173500e-02 6.389829e-02 2.649269e+00 2.756491e-04 24 | 1.651182349920e-02 2.471927808607e-01 1.780079792262e-01 1.029197e+00 -3.528252e-02 4.635316e-02 2.390659e-01 2.620218e+00 3.650395e-03 25 | 9.267596229331e-03 2.378639193931e-01 2.115792881623e-01 1.019841e+00 -5.117370e-02 1.941194e-02 3.005399e-01 2.587196e+00 4.019458e-03 26 | 2.815030807052e-02 2.467007820773e-01 1.668978584239e-02 1.038727e+00 -5.387088e-02 2.924774e-02 1.408713e-02 2.647391e+00 6.620690e-07 27 | 2.247636964429e-02 2.376260907090e-01 4.477112398496e-02 1.038996e+00 -6.757677e-02 1.700923e-02 3.933776e-02 2.646503e+00 1.216740e-04 28 | 1.967884291606e-02 2.386477657848e-01 6.804117397087e-02 1.039320e+00 -6.938718e-02 2.381816e-02 5.405917e-02 2.648489e+00 3.401036e-05 29 | 7.212336867263e-03 2.245204571763e-01 8.601136473154e-02 1.040299e+00 -8.673474e-02 3.489319e-03 8.718100e-02 2.649164e+00 8.838427e-05 30 | 8.360187090411e-04 2.190429177938e-01 1.138045962446e-01 1.036997e+00 -9.208766e-02 -1.810543e-02 1.388101e-01 2.640745e+00 2.367287e-03 31 | 1.620571910678e-02 2.474951497505e-01 2.478294225535e-01 1.010815e+00 -9.028042e-03 2.856858e-02 3.587080e-01 2.556573e+00 7.957200e-03 32 | 2.261001053460e-02 2.367413438468e-01 1.048673543849e-02 1.039610e+00 -7.782518e-02 2.082949e-02 5.786572e-04 2.646987e+00 2.374072e-06 33 | 1.572935668957e-02 2.248950624384e-01 3.079627736249e-02 1.040126e+00 -8.183745e-02 9.750955e-03 2.765226e-02 2.649183e+00 3.781181e-06 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # ------------------------------------------------------------------------------ 3 | # 4 | # Command line macros: 5 | # 6 | # INSTALL_TOP ... place to put includes and library files 7 | # LUA_HOME ... where to find lua header and library files 8 | # 9 | # If this is a local build, then you should set the environment variable 10 | # LUA_CPATH to the local directory. On bash, this will work: 11 | # 12 | # export LUA_CPATH="$LUA_CPATH;`pwd`/lib/?.so" 13 | # 14 | # Absolute paths may be used instead, which will be useful if you are doing a 15 | # remote install. 16 | # 17 | # ------------------------------------------------------------------------------ 18 | 19 | 20 | 21 | # System-specific things. 22 | # ------------------------------------------------------------------------------ 23 | 24 | # C compiler icc, gcc, etc if other than system default 25 | # CC = cc 26 | 27 | # C++ compiler icpc, g++, etc if other than system default 28 | # CXX = c++ 29 | 30 | # sometimes only -fpic works 31 | FPIC = -fPIC 32 | 33 | # Warning level flags 34 | WARN = -Wall 35 | 36 | # robust optimazation level 37 | OPTIM = -O2 38 | 39 | # debug flags, use -g for debug symbols 40 | DEBUG = 41 | 42 | # location of Lua install on this system 43 | LUA_HOME ?= $(PWD)/lua 44 | 45 | # where to install lunum library and include 46 | INSTALL_TOP = $(PWD) 47 | 48 | # C Flags 49 | CFLAGS = $(WARN) $(OPTIM) $(DEBUG) $(FPIC) 50 | 51 | 52 | # Configuration for common platforms. If you need to use a different linker, 53 | # archiver, or C libraries then uncomment the UNAME = Custom line below, and 54 | # edit the custom first block following. 55 | # ------------------------------------------------------------------------------ 56 | UNAME = $(shell uname) 57 | #UNAME = Custom 58 | # ------------------------------------------------------------------------------ 59 | # 60 | # 61 | ifeq ($(UNAME), Custom) 62 | # common for library links on Linux 63 | CLIBS = -lm -ldl 64 | # command for building shared libraries (this works for most Linux systems) 65 | SO = $(CC) -O -shared 66 | # command for generating static archives 67 | AR = ar rcu 68 | endif 69 | 70 | ifeq ($(UNAME), Linux) 71 | SO = $(CC) -O -shared 72 | AR = ar rcu 73 | CLIBS = -lm -ldl 74 | endif 75 | 76 | ifeq ($(UNAME), Darwin) 77 | SO = $(CC) -O -bundle -undefined dynamic_lookup 78 | AR = ar rcu 79 | CLIBS = 80 | endif 81 | 82 | 83 | 84 | # ------------------------------------------------- 85 | # Ensure these values are passed to child Makefiles 86 | # ------------------------------------------------- 87 | export CC 88 | export CXX 89 | export CFLAGS 90 | export LUA_HOME 91 | export SO 92 | export AR 93 | export CLIBS 94 | # ------------------------------------------------- 95 | 96 | 97 | BUILD_TOP = $(shell pwd) 98 | LIB_SO = lunum.so 99 | LIB_A = liblunum.a 100 | 101 | export LUNUM_SO = $(BUILD_TOP)/src/$(LIB_SO) 102 | export LUNUM_A = $(BUILD_TOP)/src/$(LIB_A) 103 | 104 | INSTALL_SO = $(INSTALL_TOP)/lib/$(LIB_SO) 105 | INSTALL_A = $(INSTALL_TOP)/lib/$(LIB_A) 106 | 107 | H1 = lunum.h 108 | H2 = numarray.h 109 | 110 | HEADERS = \ 111 | $(INSTALL_TOP)/include/$(H1) \ 112 | $(INSTALL_TOP)/include/$(H2) 113 | 114 | 115 | default : $(LUNUM_SO) $(LUNUM_A) 116 | 117 | config : 118 | @echo "CC = $(CC)" 119 | @echo "CXX = $(CXX)" 120 | @echo "FPIC = $(FPIC)" 121 | @echo "WARN = $(WARN)" 122 | @echo "OPTIM = $(OPTIM)" 123 | @echo "DEBUG = $(DEBUG)" 124 | @echo "AR = $(AR)" 125 | @echo "SO = $(SO)" 126 | @echo "LUA_HOME = $(LUA_HOME)" 127 | @echo "INSTALL_TOP = $(INSTALL_TOP)" 128 | 129 | test : $(LUNUM_SO) $(LUNUM_A) 130 | 131 | all : default test 132 | 133 | install : $(INSTALL_SO) $(INSTALL_A) $(HEADERS) 134 | 135 | $(INSTALL_TOP)/include/$(H1) : 136 | mkdir -p $(INSTALL_TOP)/include 137 | cp src/$(H1) $(INSTALL_TOP)/include 138 | 139 | $(INSTALL_TOP)/include/$(H2) : 140 | mkdir -p $(INSTALL_TOP)/include 141 | cp src/$(H2) $(INSTALL_TOP)/include 142 | 143 | $(LUNUM_SO) : FORCE 144 | @make -C src $(LUNUM_SO) 145 | 146 | $(LUNUM_A) : FORCE 147 | @make -C src $(LUNUM_A) 148 | 149 | test : FORCE 150 | @make -C test 151 | 152 | $(INSTALL_SO) : $(LUNUM_SO) 153 | mkdir -p $(INSTALL_TOP)/lib 154 | cp $(LUNUM_SO) $(INSTALL_TOP)/lib 155 | 156 | $(INSTALL_A) : $(LUNUM_A) 157 | mkdir -p $(INSTALL_TOP)/lib 158 | cp $(LUNUM_A) $(INSTALL_TOP)/lib 159 | 160 | clean : 161 | make -C test clean 162 | make -C src clean 163 | rm -rf lib include 164 | 165 | FORCE : 166 | -------------------------------------------------------------------------------- /src/array_class.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define LUNUM_PRIVATE_API 4 | #include 5 | #include 6 | #include "lunum.h" 7 | #include "lauxlib.h" 8 | 9 | 10 | static int luaC_array_dtype(lua_State *L); 11 | static int luaC_array_shape(lua_State *L); 12 | static int luaC_array_size(lua_State *L); 13 | static int luaC_array_astable(lua_State *L); 14 | static int luaC_array_astype(lua_State *L); 15 | static int luaC_array_tofile(lua_State *L); 16 | 17 | 18 | void _lunum_register_array(lua_State *L, struct Array *B) 19 | { 20 | lua_newtable(L); 21 | 22 | lua_pushcfunction(L, luaC_array_dtype); 23 | lua_setfield(L, -2, "dtype"); 24 | 25 | lua_pushcfunction(L, luaC_array_shape); 26 | lua_setfield(L, -2, "shape"); 27 | 28 | lua_pushcfunction(L, luaC_array_size); 29 | lua_setfield(L, -2, "size"); 30 | 31 | lua_pushcfunction(L, luaC_array_astable); 32 | lua_setfield(L, -2, "astable"); 33 | 34 | lua_pushcfunction(L, luaC_array_astype); 35 | lua_setfield(L, -2, "astype"); 36 | 37 | lua_pushcfunction(L, luaC_array_tofile); 38 | lua_setfield(L, -2, "tofile"); 39 | 40 | 41 | // Calls code written in Lua: lunum.__register_array(new_array) to add 42 | // additional methods to the new array. 43 | // --------------------------------------------------------------------------- 44 | lua_getglobal(L, "lunum"); 45 | lua_getfield(L, -1, "__register_array"); 46 | lua_pushvalue(L, -3); 47 | lua_call(L, 1, 0); 48 | lua_pop(L, 1); 49 | 50 | 51 | void *data = lua_newuserdata(L, B->size * array_sizeof(B->dtype)); 52 | lua_setfield(L, -2, "__buffer"); 53 | 54 | memcpy(data, B->data, B->size * array_sizeof(B->dtype)); 55 | free(B->data); 56 | B->owns = 0; 57 | B->data = data; 58 | 59 | struct Array *A = (struct Array*) lua_newuserdata(L, sizeof(struct Array)); 60 | lua_setfield(L, -2, "__cstruct"); 61 | *A = *B; 62 | 63 | luaL_getmetatable(L, "array"); 64 | lua_setmetatable(L, -2); 65 | } 66 | 67 | 68 | 69 | 70 | int luaC_array_dtype(lua_State *L) 71 | // ----------------------------------------------------------------------------- 72 | // If there is no argument, return a string description of the data type. If 73 | // the string 'enum' is given as the first argument, then return the enumated 74 | // value of the Array's type. 75 | // ----------------------------------------------------------------------------- 76 | { 77 | struct Array *A = lunum_checkarray1(L, 1); 78 | 79 | if (lua_isstring(L, 2)) { 80 | if (strcmp(lua_tostring(L, 2), "enum") == 0) { 81 | lua_pushnumber(L, A->dtype); 82 | return 1; 83 | } 84 | } 85 | 86 | lua_pushstring(L, array_typename(A->dtype)); 87 | return 1; 88 | } 89 | 90 | int luaC_array_shape(lua_State *L) 91 | // ----------------------------------------------------------------------------- 92 | // If there is no argument, return the shape as a table. If the string 'array' 93 | // is given, return it as an array. 94 | // ----------------------------------------------------------------------------- 95 | { 96 | struct Array *A = lunum_checkarray1(L, 1); 97 | lunum_pusharray2(L, A->shape, ARRAY_TYPE_INT, A->ndims); 98 | 99 | if (lua_isstring(L, 2)) { 100 | if (strcmp(lua_tostring(L, 2), "array") == 0) { 101 | return 1; 102 | } 103 | } 104 | 105 | lunum_astable(L, 2); 106 | lua_replace(L, -2); 107 | return 1; 108 | } 109 | 110 | int luaC_array_size(lua_State *L) 111 | { 112 | struct Array *A = lunum_checkarray1(L, 1); 113 | lua_pushnumber(L, A->size); 114 | return 1; 115 | } 116 | 117 | int luaC_array_astable(lua_State *L) 118 | { 119 | lunum_astable(L, 1); 120 | return 1; 121 | } 122 | 123 | int luaC_array_astype(lua_State *L) 124 | { 125 | struct Array *A = lunum_checkarray1(L, 1); 126 | enum ArrayType T; 127 | 128 | if (lua_type(L, 2) == LUA_TSTRING) { 129 | T = array_typeflag(lua_tostring(L, 2)[0]); 130 | } 131 | else { 132 | T = (enum ArrayType) luaL_checkinteger(L, 2); 133 | } 134 | 135 | struct Array B = array_new_copy(A, T); 136 | lunum_pusharray1(L, &B); 137 | return 1; 138 | } 139 | 140 | 141 | 142 | int luaC_array_tofile(lua_State *L) 143 | // ----------------------------------------------------------------------------- 144 | // Writes the array 'A' as binary data to the file named 'fname'. 145 | // ----------------------------------------------------------------------------- 146 | { 147 | struct Array *A = lunum_checkarray1(L, 1); 148 | const char *fname = luaL_checkstring(L, 2); 149 | FILE *output = fopen(fname, "wb"); 150 | 151 | if (output == NULL) { 152 | luaL_error(L, "could not create file %s", fname); 153 | } 154 | fwrite(A->data, A->size, array_sizeof(A->dtype), output); 155 | fclose(output); 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /src/lunum_capi.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define LUNUM_PRIVATE_API 8 | #include "lunum.h" 9 | #include "lauxlib.h" 10 | 11 | 12 | void lunum_pusharray1(lua_State *L, struct Array *B) 13 | { 14 | _lunum_register_array(L, B); 15 | } 16 | 17 | void lunum_pusharray2(lua_State *L, void *data, enum ArrayType T, int N) 18 | { 19 | struct Array A = array_new_zeros(N, T); 20 | memcpy(A.data, data, N*array_sizeof(T)); 21 | lunum_pusharray1(L, &A); 22 | } 23 | 24 | struct Array *lunum_checkarray1(lua_State *L, int pos) 25 | { 26 | lua_pushvalue(L, pos); 27 | 28 | if (!lunum_hasmetatable(L, -1, "array")) { 29 | luaL_error(L, "bad argument #%d (array expected, got %s)", 30 | pos, lua_typename(L, lua_type(L, -1))); 31 | } 32 | lua_pushstring(L, "__cstruct"); 33 | lua_rawget(L, -2); 34 | 35 | struct Array *A = (struct Array*) lua_touserdata(L, -1); 36 | lua_pop(L, 2); 37 | 38 | return A; 39 | } 40 | 41 | void *lunum_checkarray2(lua_State *L, int pos, enum ArrayType T, int *N) 42 | { 43 | if (lunum_upcast(L, pos, T, 1)) { 44 | lua_replace(L, pos); 45 | } 46 | struct Array *A = lunum_checkarray1(L, pos); 47 | if (N != NULL) *N = A->size; 48 | return A->data; 49 | } 50 | 51 | 52 | void lunum_astable(lua_State *L, int pos) 53 | { 54 | struct Array *A = lunum_checkarray1(L, pos); 55 | const void *a = A->data; 56 | 57 | lua_newtable(L); 58 | for (int i=0; isize; ++i) { 59 | 60 | lua_pushnumber(L, i+1); 61 | 62 | switch (A->dtype) { 63 | case ARRAY_TYPE_BOOL : lua_pushboolean (L, ((Bool *)a)[i]); break; 64 | case ARRAY_TYPE_CHAR : lua_pushnumber (L, ((char *)a)[i]); break; 65 | case ARRAY_TYPE_SHORT : lua_pushnumber (L, ((short *)a)[i]); break; 66 | case ARRAY_TYPE_INT : lua_pushnumber (L, ((int *)a)[i]); break; 67 | case ARRAY_TYPE_LONG : lua_pushnumber (L, ((long *)a)[i]); break; 68 | case ARRAY_TYPE_FLOAT : lua_pushnumber (L, ((float *)a)[i]); break; 69 | case ARRAY_TYPE_DOUBLE : lua_pushnumber (L, ((double *)a)[i]); break; 70 | case ARRAY_TYPE_COMPLEX : lunum_pushcomplex(L, ((Complex *)a)[i]); break; 71 | } 72 | 73 | lua_settable(L, -3); 74 | } 75 | } 76 | 77 | void lunum_pushcomplex(lua_State *L, Complex z) 78 | { 79 | Complex *w = (Complex*) lua_newuserdata(L, sizeof(Complex)); 80 | luaL_getmetatable(L, "complex"); 81 | lua_setmetatable(L, -2); 82 | *w = z; 83 | } 84 | 85 | Complex lunum_checkcomplex(lua_State *L, int n) 86 | { 87 | Complex *w = (Complex*) luaL_checkudata(L, n, "complex"); 88 | return *w; 89 | } 90 | 91 | 92 | 93 | int lunum_upcast(lua_State *L, int pos, enum ArrayType T, int N) 94 | // ----------------------------------------------------------------------------- 95 | // If the object at position 'pos' is already an array of dtype 'T', then push 96 | // nothing and return 0. If the dtype is not 'T', then return 1 and push a copy 97 | // of that array with dtype 'T' onto the stack. If it is a table, then push an 98 | // array of dtype 'T' having the length of the table. If it is a number or 99 | // complex, then push an array of dtype float or complex respectively having 100 | // length 'N'. 101 | // ----------------------------------------------------------------------------- 102 | { 103 | if (array_typename(T) == NULL) { 104 | luaL_error(L, "invalid array type"); 105 | } 106 | 107 | // Deal with lunum.array 108 | // --------------------------------------------------------------------------- 109 | if (lunum_hasmetatable(L, pos, "array")) { 110 | 111 | struct Array *A = lunum_checkarray1(L, pos); 112 | 113 | if (A->dtype == T) { 114 | return 0; 115 | } 116 | 117 | else { 118 | 119 | struct Array A_ = array_new_copy(A, T); 120 | lunum_pusharray1(L, &A_); 121 | return 1; 122 | } 123 | } 124 | 125 | // Deal with Lua table 126 | // --------------------------------------------------------------------------- 127 | else if (lua_istable(L, pos)) { 128 | 129 | struct Array A = array_new_zeros(lua_rawlen(L, pos), T); 130 | 131 | for (int i=0; i '3.141592653589' 83 | print(A(9,9,2)) -- > '3.141592653589', short-hand for r-value access 84 | 85 | ### Slicing 86 | 87 | As of Lunum version 0.5.3, slicing operations as r-values (on the 88 | right hand side of the equals sign) are supported. Slicing uses the 89 | same convention as Numpy, but requires a slightly different syntax 90 | since Lua does not allow `:`'s or `,`'s inside square brackets. It is 91 | easiest to demonstrate with a few examples. 92 | 93 | local A = lunum.range(800):reshape{20,10,4} -- (20 x 10 x 4) array of int's 94 | 95 | -- All equivalent ways of extracting a 1d slice along the second axis: 96 | local B = A[{10,nil,1}] 97 | local C = A[{10,{},1}] 98 | local D = A['10,:,1'] # Using a string to describe the slice 99 | 100 | -- Ranges and strides are also supported. These all generate the same 3d array: 101 | local E = A[{{ 0,20 ,4}, nil, {0,4,2}}] 102 | local F = A[{{nil,nil,4}, {}, {0,4,2}}] 103 | local G = A['::4, :, 0:4:2'] -- slice descriptions ignore white-space 104 | 105 | Range selections are all done using the `start:stop:skip` convention, 106 | where the upper bound `stop` is *not* included. Describing the slice 107 | using a string is often the clearest syntactically, but if more 108 | automation is needed (for example extracting slices in a loop) the 109 | alternative *table of tables* works just fine too. Elements of the 110 | outer table labeled `nil` indicate all values along that axis. `nil` 111 | or absent entries on the inner table means `0`, `N`, and `1` for 112 | `start`, `stop`, and `stride`. 113 | 114 | 115 | ## Array member functions 116 | 117 | ### array:dtype([kind]) 118 | *** 119 | 120 | Returns a string containing the data type of the array. If the 121 | optional argument `kind` is the string `enum`, instead returns the 122 | type code. Useful for example, 123 | 124 | local B = lunum.zeros(A:shape(), A:dtype('enum')) 125 | 126 | ### array:shape([kind]) 127 | *** 128 | 129 | Returns an array with the array's dimensions as a table. If the 130 | optional argument `kind` is the string `array`, instead the shape as 131 | a Lunum array. Two ways of printing the shape of an array are 132 | 133 | print(unpack(A:shape()) 134 | print(A:shape('array')) 135 | 136 | ### array:astable() 137 | *** 138 | 139 | Returns the array, converted to a Lua table. All data types except 140 | complex are converted to Lua Numbers. The returned table is a 141 | flattened copy of the array. 142 | 143 | ### array:astype(type) 144 | *** 145 | 146 | Returns the array, converted to `type`. 147 | 148 | ### array:min() 149 | *** 150 | 151 | Returns the minimum value of the array. 152 | 153 | ### array:max() 154 | *** 155 | 156 | Returns the maximum value of the array. 157 | 158 | ### array:real() 159 | *** 160 | 161 | Returns the real part of an array. If it is not complex, then a simple 162 | copy of the array is returned. 163 | 164 | ### array:imag() 165 | *** 166 | 167 | Returns the imaginary part of an array. If it is not complex, then an 168 | array of zeros with the same shape and data type is returned. 169 | 170 | ### array:conj() 171 | *** 172 | 173 | Returns the complex conjugate of an array. If it is not complex, then 174 | a simple copy of the array is returned. 175 | 176 | ### array:copy() 177 | *** 178 | Returns a deep-copy of the array. 179 | 180 | ### array:resize(newshape) 181 | *** 182 | 183 | Same as lunum.resize(A, newshape). Changes the array `A` in-place. 184 | 185 | ### array:reshape(newshape) 186 | *** 187 | 188 | Returns a copy of the array `A` with the shape `newshape`. Does not 189 | change `A` at all. 190 | 191 | ### array:setasflat() 192 | *** 193 | Resizes `A` in-place to a flattened version. 194 | 195 | ### array:indices([kind]) 196 | *** 197 | 198 | Returns an iterator over all permutations of valid indices. If the 199 | optional argument `kind` is the string `table`, then returns a table 200 | containing the indices instead of unpacking them. For example: 201 | 202 | 203 | local B = lunum.range(24) 204 | B:resize{2,2,3,2} 205 | 206 | for i,j,k,m in B:indices() do 207 | print(i,j,k,m, B(i,j,k,m)) 208 | end 209 | 210 | for I in B:indices('table') do 211 | -- I := { i,j,k,m } 212 | B[I] = some_function(I[1], I[4]) 213 | end 214 | 215 | 216 | ### array:eq(), ne(), lt(), le(), gt(), ge() 217 | *** 218 | 219 | Array comparison functions. Compares the calling array 220 | element-by-element with another array and returns an array of boolean 221 | values accordingly. Unfortunately, the native Lua metamethods `__eq`, 222 | etc. must return booleans which is why comparisons are implemented as 223 | array methods. Example: 224 | 225 | local A = lunum.array({0,1,true,false}, lunum.bool) 226 | print(A) -- > [ false, true, false, true ] 227 | 228 | local B = lunum.array{1,2,3} 229 | local C = lunum.array{3,2,1} 230 | print(B:ne(C)) -- > [ true, false, true ] 231 | 232 | ### array:tofile(fname) 233 | *** 234 | 235 | Writes a flattened version of the array to the file `fname` in binary 236 | format. 237 | 238 | 239 | ## Lunum functions 240 | 241 | ### lunum.array(tab, [dtype]) 242 | *** 243 | 244 | Returns a new array from the table `tab` of type `dtype`. Default is 245 | double precision. 246 | 247 | ### lunum.zeros(N, [dtype]) 248 | *** 249 | 250 | Returns a new array with `N` elements, initialized to zero, of type 251 | `dtype`. Default is double precision. 252 | 253 | ### lunum.range(N) 254 | *** 255 | Returns the integer array `[0,1,...N-1]` 256 | 257 | ### lunum.resize(A, newshape) 258 | *** 259 | 260 | Resizes the array `A` (in-place) to have the dimensions given in the 261 | table `newshape`. The total size must be unchanged. Arrays of 262 | arbitrary dimension are supported. 263 | 264 | ### lunum.apply(f, A, B, ...) 265 | *** 266 | 267 | Returns the lunum array `C`, where `C[i] = f(A[i], B[i], ...)` for any 268 | number of array input arguments. Arguments must all have the same 269 | shape. The returned array has the highest data type of any of the 270 | inputs. 271 | 272 | ### lunum.sin(), cos(), etc. 273 | *** 274 | 275 | Lunum math library function call. Accepts as arguments Lunum arrays, 276 | or single numbers of all data types. Overloaded for complex values by 277 | calling the appropriate functions in the C math library. All 278 | functions in the C math library are provided. 279 | 280 | ### lunum.loadtxt(fname) 281 | *** 282 | 283 | Loads an ASCII table from the file `fname`, and returns it as either a 284 | 1d or 2d (if more than one column) array. Presently only double 285 | precision is supported. 286 | 287 | 288 | ### lunum.fromfile(fname, [dtype]) 289 | *** 290 | 291 | Opens the binary file `fname` for reading, and returns a 1d array from 292 | the data. The file size must be a multiple of the data type `type`, 293 | which defaults to `double`. 294 | -------------------------------------------------------------------------------- /src/array_class.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- ***************************************************************************** 4 | -- Local utility function, never exported 5 | -- ***************************************************************************** 6 | local function check_shapes_agree(A, B) 7 | local go = true 8 | local As = A:shape() 9 | local Bs = B:shape() 10 | 11 | if #As ~= #Bs then go = false end 12 | for d=1,#As do 13 | if As[d] ~= Bs[d] then go = false end 14 | end 15 | if not go then 16 | error('arguments of incompatible shapes.') 17 | end 18 | end 19 | 20 | function string_split(self, sSeparator, nMax, bRegexp) 21 | -- -------------------------------------------------------------------------- 22 | -- http://lua-users.org/wiki/SplitJoin 23 | -- -------------------------------------------------------------------------- 24 | assert(sSeparator ~= '') 25 | assert(nMax == nil or nMax >= 1) 26 | 27 | local aRecord = {} 28 | 29 | if self:len() > 0 then 30 | local bPlain = not bRegexp 31 | nMax = nMax or -1 32 | 33 | local nField=1 nStart=1 34 | local nFirst,nLast = self:find(sSeparator, nStart, bPlain) 35 | while nFirst and nMax ~= 0 do 36 | aRecord[nField] = self:sub(nStart, nFirst-1) 37 | nField = nField+1 38 | nStart = nLast+1 39 | nFirst,nLast = self:find(sSeparator, nStart, bPlain) 40 | nMax = nMax-1 41 | end 42 | aRecord[nField] = self:sub(nStart) 43 | end 44 | 45 | return aRecord 46 | end 47 | 48 | function string_trim(s) 49 | -- -------------------------------------------------------------------------- 50 | -- http://lua-users.org/wiki/CommonFunctions 51 | -- from PiL2 20.4 52 | -- -------------------------------------------------------------------------- 53 | return (s:gsub("^%s*(.-)%s*$", "%1")) 54 | end 55 | 56 | 57 | local function copy(A) 58 | -- -------------------------------------------------------------------------- 59 | -- Returns a deepcopy of the array 'A'. 60 | -- -------------------------------------------------------------------------- 61 | return A:astype(A:dtype('enum')) 62 | end 63 | 64 | local function min(A) 65 | -- -------------------------------------------------------------------------- 66 | -- Returns the minimum value of the array 'A'. 67 | -- -------------------------------------------------------------------------- 68 | local x = A[0] 69 | for I in A:indices('table') do 70 | if A[I] < x then x = A[I] end 71 | end 72 | return x 73 | end 74 | 75 | local function max(A) 76 | -- -------------------------------------------------------------------------- 77 | -- Returns the maximum value of the array 'A'. 78 | -- -------------------------------------------------------------------------- 79 | local x = A[0] 80 | for I in A:indices('table') do 81 | if A[I] > x then x = A[I] end 82 | end 83 | return x 84 | end 85 | 86 | local function conj(A) 87 | -- -------------------------------------------------------------------------- 88 | -- Returns the complex conjugate of the array 'A'. 89 | -- -------------------------------------------------------------------------- 90 | return lunum.conjugate(A) 91 | end 92 | 93 | local function real(A) 94 | -- -------------------------------------------------------------------------- 95 | -- Returns the real part of the array 'A'. 96 | -- -------------------------------------------------------------------------- 97 | if A:dtype('enum') ~= lunum.complex then return A:copy() 98 | else 99 | return A:astype(lunum.double) 100 | end 101 | end 102 | 103 | local function imag(A) 104 | -- -------------------------------------------------------------------------- 105 | -- Returns the imaginary part of the array 'A'. 106 | -- -------------------------------------------------------------------------- 107 | if A:dtype('enum') ~= lunum.complex then 108 | return lunum.zeros(A:shape(), A:dtype('enum')) 109 | else 110 | return (-lunum.I*A):astype(lunum.double) 111 | end 112 | end 113 | 114 | local function resize(A, newshape) 115 | -- -------------------------------------------------------------------------- 116 | -- Resizes the array 'A' in-place to have the shape 'newshape'. Returns 'A'. 117 | -- -------------------------------------------------------------------------- 118 | lunum.resize(A, newshape) 119 | return A 120 | end 121 | 122 | local function reshape(A, newshape) 123 | -- -------------------------------------------------------------------------- 124 | -- Returns a copy of 'A' with the shape 'newshape'. Similar to array:resize, 125 | -- but does not change 'A'. 126 | -- -------------------------------------------------------------------------- 127 | local B = A:copy() 128 | B:resize(newshape) 129 | return B 130 | end 131 | 132 | local function setasflat(A) 133 | -- -------------------------------------------------------------------------- 134 | -- Resizes 'A' in-place to a flattened version. 135 | -- -------------------------------------------------------------------------- 136 | A:resize({A:size()}) 137 | end 138 | 139 | local function indices(A, kind) 140 | -- -------------------------------------------------------------------------- 141 | -- Returns an iterator over all permutations of valid indices. If the 142 | -- optional argument 'kind' is the string 'table', then returns a table 143 | -- containing the indices instead of unpacking them. 144 | -- -------------------------------------------------------------------------- 145 | local N = A:shape() 146 | local I = { } 147 | 148 | for d=1,#N-1 do I[d] = 0 end 149 | I[#N] = -1 150 | 151 | local function f() 152 | I[#N] = I[#N] + 1 153 | for d=#N,2,-1 do 154 | if I[d] == N[d] then 155 | I[d] = 0 156 | I[d-1] = I[d-1] + 1 157 | end 158 | end 159 | if I[1] == N[1] then return nil end 160 | return unpack(I) 161 | end 162 | 163 | local function g() 164 | I[#N] = I[#N] + 1 165 | for d=#N,2,-1 do 166 | if I[d] == N[d] then 167 | I[d] = 0 168 | I[d-1] = I[d-1] + 1 169 | end 170 | end 171 | if I[1] == N[1] then return nil end 172 | return I 173 | end 174 | if kind == 'table' then return g 175 | else return f 176 | end 177 | end 178 | 179 | 180 | -- ----------------------------------------------------------------------------- 181 | -- Array comparison operators. 182 | -- ----------------------------------------------------------------------------- 183 | local function eq(A, B) 184 | check_shapes_agree(A, B) 185 | local C = lunum.zeros(A:shape(), lunum.bool) 186 | for I in A:indices('table') do C[I] = A[I]==B[I] end 187 | return C 188 | end 189 | 190 | local function ne(A, B) 191 | check_shapes_agree(A, B) 192 | local C = lunum.zeros(A:shape(), lunum.bool) 193 | for I in A:indices('table') do C[I] = A[I]~=B[I] end 194 | return C 195 | end 196 | 197 | local function lt(A, B) 198 | check_shapes_agree(A, B) 199 | local C = lunum.zeros(A:shape(), lunum.bool) 200 | for I in A:indices('table') do C[I] = A[I]< B[I] end 201 | return C 202 | end 203 | 204 | local function le(A, B) 205 | check_shapes_agree(A, B) 206 | local C = lunum.zeros(A:shape(), lunum.bool) 207 | for I in A:indices('table') do C[I] = A[I]<=B[I] end 208 | return C 209 | end 210 | 211 | local function gt(A, B) 212 | check_shapes_agree(A, B) 213 | local C = lunum.zeros(A:shape(), lunum.bool) 214 | for I in A:indices('table') do C[I] = A[I]> B[I] end 215 | return C 216 | end 217 | 218 | local function ge(A, B) 219 | check_shapes_agree(A, B) 220 | local C = lunum.zeros(A:shape(), lunum.bool) 221 | for I in A:indices('table') do C[I] = A[I]>=B[I] end 222 | return C 223 | end 224 | 225 | 226 | local function apply(f,...) 227 | -- -------------------------------------------------------------------------- 228 | -- Returns the lunum array 'C', where C[i] = f(A[i], B[i], ...) for any 229 | -- number of array input arguments. Arguments must all have the same 230 | -- shape. The returned array has the highest data type of any of the inputs. 231 | -- -------------------------------------------------------------------------- 232 | local A = {...} 233 | local T = A[1]:dtype('enum') 234 | local s1 = A[1]:shape() 235 | local go = true 236 | 237 | for d=2,#A do 238 | local Td = A[d]:dtype('enum') 239 | if T < Td then T = Td end 240 | local sd = A[d]:shape() 241 | if #s1 ~= #sd then go = false end 242 | for m=1,#sd do if s1[m] ~= sd[m] then go = false end end 243 | end 244 | 245 | if not go then 246 | error('arguments of incompatible shapes.') 247 | end 248 | 249 | local B = lunum.zeros(s1, T) 250 | for i=0,B:size()-1 do 251 | local x = { } 252 | for d=1,#A do x[d] = A[d][i] end 253 | B[i] = f(unpack(x)) 254 | end 255 | return B 256 | end 257 | 258 | local function __build_slice(A,t) 259 | -- -------------------------------------------------------------------------- 260 | -- Returns a slice of the lunum array 'A', i.e. A[i0:i1:si,j0:j1:sj]. 261 | -- -------------------------------------------------------------------------- 262 | local s = { } 263 | 264 | if type(t) == 'string' then 265 | for k,v in pairs(string_split(t, ',')) do 266 | local addr = string_split(v, ':') 267 | if #addr == 1 then 268 | addr = { addr[1], addr[1]+1, 1, 1 } 269 | else 270 | for i=1,#addr do 271 | if string_trim(addr[i]) == '' then addr[i] = nil end 272 | end 273 | end 274 | s[k] = addr 275 | end 276 | 277 | elseif type(t) == 'table' then 278 | for k,v in pairs(t) do 279 | if type(v) == 'number' then 280 | s[k] = { v, v+1, 1, 1 } 281 | elseif type(v) == 'table' then 282 | s[k] = { v[1], v[2], v[3], 0 } 283 | end 284 | end 285 | end 286 | 287 | -- Return the entirety of dims not specified. 288 | for i=#s+1,#A:shape() do 289 | s[i] = { nil, nil, nil, 0 } 290 | end 291 | 292 | local sT = { {},{},{},{} } 293 | 294 | for i=1,#s do 295 | sT[1][i] = s[i][1] or 0 -- start 296 | sT[2][i] = s[i][2] or A:shape()[i] -- stop 297 | sT[3][i] = s[i][3] or 1 -- skip 298 | sT[4][i] = s[i][4] or 0 -- squeeze 299 | end 300 | 301 | return lunum.slice(A, unpack(sT)) 302 | end 303 | 304 | -- ----------------------------------------------------------------------------- 305 | -- This function gets called from C code to register an array's class methods 306 | -- have been were implemented in Lua. 307 | -- ----------------------------------------------------------------------------- 308 | local function __register_array(t) 309 | t.copy = copy 310 | t.min = min 311 | t.max = max 312 | t.real = real 313 | t.imag = imag 314 | t.conj = conj 315 | t.resize = resize 316 | t.setasflat = setasflat 317 | t.reshape = reshape 318 | t.indices = indices 319 | t.eq = eq 320 | t.ne = ne 321 | t.lt = lt 322 | t.le = le 323 | t.gt = gt 324 | t.ge = ge 325 | end 326 | 327 | 328 | -- ----------------------------------------------------------------------------- 329 | -- Registering the functions with the lunum table. 330 | -- ----------------------------------------------------------------------------- 331 | lunum.__build_slice = __build_slice 332 | lunum.__register_array = __register_array 333 | lunum.apply = apply 334 | 335 | -------------------------------------------------------------------------------- /src/numarray.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "numarray.h" 8 | 9 | typedef unsigned char Bool; 10 | typedef double complex Complex; 11 | 12 | #define EXPR_ADD(T) {for(int i=0;isize, T); 87 | array_resize(&A, B->shape, B->ndims); 88 | array_assign_from_array(&A, B); 89 | return A; 90 | } 91 | 92 | void array_del(struct Array *A) 93 | { 94 | if (A->data && A->owns) free(A->data); 95 | if (A->shape) free(A->shape); 96 | 97 | A->size = 0; 98 | A->data = NULL; 99 | A->shape = NULL; 100 | } 101 | 102 | int array_resize(struct Array *A, const int *N, int Nd) 103 | { 104 | int ntot = 1; 105 | for (int d=0; dsize != ntot) { 108 | return 1; 109 | } 110 | if (A->shape) free(A->shape); 111 | 112 | A->ndims = Nd; 113 | A->shape = (int*) malloc(Nd*sizeof(int)); 114 | memcpy(A->shape, N, Nd*sizeof(int)); 115 | 116 | return 0; 117 | } 118 | 119 | void array_binary_op(const struct Array *A, const struct Array *B, 120 | struct Array *C, enum ArrayOperation op) 121 | { 122 | const int N = A->size; 123 | const void *a = A->data; 124 | const void *b = B->data; 125 | void *c = C->data; 126 | 127 | switch (op) { 128 | case ARRAY_OP_ADD: 129 | switch (A->dtype) { 130 | case ARRAY_TYPE_BOOL : EXPR_ADD(Bool ) ; break; 131 | case ARRAY_TYPE_CHAR : EXPR_ADD(char ) ; break; 132 | case ARRAY_TYPE_SHORT : EXPR_ADD(short ) ; break; 133 | case ARRAY_TYPE_INT : EXPR_ADD(int ) ; break; 134 | case ARRAY_TYPE_LONG : EXPR_ADD(long ) ; break; 135 | case ARRAY_TYPE_FLOAT : EXPR_ADD(float ) ; break; 136 | case ARRAY_TYPE_DOUBLE : EXPR_ADD(double ) ; break; 137 | case ARRAY_TYPE_COMPLEX : EXPR_ADD(Complex) ; break; 138 | } 139 | break; 140 | case ARRAY_OP_SUB: 141 | switch (A->dtype) { 142 | case ARRAY_TYPE_BOOL : EXPR_SUB(Bool ) ; break; 143 | case ARRAY_TYPE_CHAR : EXPR_SUB(char ) ; break; 144 | case ARRAY_TYPE_SHORT : EXPR_SUB(short ) ; break; 145 | case ARRAY_TYPE_INT : EXPR_SUB(int ) ; break; 146 | case ARRAY_TYPE_LONG : EXPR_SUB(long ) ; break; 147 | case ARRAY_TYPE_FLOAT : EXPR_SUB(float ) ; break; 148 | case ARRAY_TYPE_DOUBLE : EXPR_SUB(double ) ; break; 149 | case ARRAY_TYPE_COMPLEX : EXPR_SUB(Complex) ; break; 150 | } 151 | break; 152 | case ARRAY_OP_MUL: 153 | switch (A->dtype) { 154 | case ARRAY_TYPE_BOOL : EXPR_MUL(Bool ) ; break; 155 | case ARRAY_TYPE_CHAR : EXPR_MUL(char ) ; break; 156 | case ARRAY_TYPE_SHORT : EXPR_MUL(short ) ; break; 157 | case ARRAY_TYPE_INT : EXPR_MUL(int ) ; break; 158 | case ARRAY_TYPE_LONG : EXPR_MUL(long ) ; break; 159 | case ARRAY_TYPE_FLOAT : EXPR_MUL(float ) ; break; 160 | case ARRAY_TYPE_DOUBLE : EXPR_MUL(double ) ; break; 161 | case ARRAY_TYPE_COMPLEX : EXPR_MUL(Complex) ; break; 162 | } 163 | break; 164 | case ARRAY_OP_DIV: 165 | switch (A->dtype) { 166 | case ARRAY_TYPE_BOOL : EXPR_DIV(Bool ) ; break; 167 | case ARRAY_TYPE_CHAR : EXPR_DIV(char ) ; break; 168 | case ARRAY_TYPE_SHORT : EXPR_DIV(short ) ; break; 169 | case ARRAY_TYPE_INT : EXPR_DIV(int ) ; break; 170 | case ARRAY_TYPE_LONG : EXPR_DIV(long ) ; break; 171 | case ARRAY_TYPE_FLOAT : EXPR_DIV(float ) ; break; 172 | case ARRAY_TYPE_DOUBLE : EXPR_DIV(double ) ; break; 173 | case ARRAY_TYPE_COMPLEX : EXPR_DIV(Complex) ; break; 174 | } 175 | break; 176 | case ARRAY_OP_POW: 177 | switch (A->dtype) { 178 | case ARRAY_TYPE_BOOL : EXPR_POW(Bool ) ; break; 179 | case ARRAY_TYPE_CHAR : EXPR_POW(char ) ; break; 180 | case ARRAY_TYPE_SHORT : EXPR_POW(short ) ; break; 181 | case ARRAY_TYPE_INT : EXPR_POW(int ) ; break; 182 | case ARRAY_TYPE_LONG : EXPR_POW(long ) ; break; 183 | case ARRAY_TYPE_FLOAT : EXPR_POW(float ) ; break; 184 | case ARRAY_TYPE_DOUBLE : EXPR_POW(double ) ; break; 185 | case ARRAY_TYPE_COMPLEX : EXPR_COW(Complex) ; break; 186 | } 187 | break; 188 | } 189 | } 190 | 191 | int array_sizeof(enum ArrayType T) 192 | { 193 | switch (T) { 194 | case ARRAY_TYPE_BOOL : return sizeof(Bool); 195 | case ARRAY_TYPE_CHAR : return sizeof(char); 196 | case ARRAY_TYPE_SHORT : return sizeof(short); 197 | case ARRAY_TYPE_INT : return sizeof(int); 198 | case ARRAY_TYPE_LONG : return sizeof(long); 199 | case ARRAY_TYPE_FLOAT : return sizeof(float); 200 | case ARRAY_TYPE_DOUBLE : return sizeof(double); 201 | case ARRAY_TYPE_COMPLEX : return sizeof(Complex); 202 | } 203 | return sizeof(int); 204 | } 205 | 206 | void array_assign_from_scalar(struct Array *A, const void *val) 207 | { 208 | const int N = A->size; 209 | void *a = A->data; 210 | 211 | switch (A->dtype) { 212 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN1(Bool , val) ; break; 213 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN1(char , val) ; break; 214 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN1(short , val) ; break; 215 | case ARRAY_TYPE_INT : EXPR_ASSIGN1(int , val) ; break; 216 | case ARRAY_TYPE_LONG : EXPR_ASSIGN1(long , val) ; break; 217 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN1(float , val) ; break; 218 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN1(double , val) ; break; 219 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN1(Complex, val) ; break; 220 | } 221 | } 222 | 223 | void array_assign_from_array(struct Array *A, const struct Array *B) 224 | { 225 | void *a = A->data; 226 | const void *b = B->data; 227 | const int N = B->size; 228 | 229 | switch (A->dtype) { 230 | case ARRAY_TYPE_BOOL: 231 | switch (B->dtype) { 232 | // (A->type, B->type) 233 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(Bool, Bool) ; break; 234 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(Bool, char) ; break; 235 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(Bool, short) ; break; 236 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(Bool, int) ; break; 237 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(Bool, long) ; break; 238 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(Bool, float) ; break; 239 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(Bool, double) ; break; 240 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(Bool, Complex) ; break; 241 | } 242 | break; 243 | 244 | case ARRAY_TYPE_CHAR: 245 | switch (B->dtype) { 246 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(char, Bool) ; break; 247 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(char, char) ; break; 248 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(char, short) ; break; 249 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(char, int) ; break; 250 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(char, long) ; break; 251 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(char, float) ; break; 252 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(char, double) ; break; 253 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(char, Complex) ; break; 254 | } 255 | break; 256 | 257 | case ARRAY_TYPE_SHORT: 258 | switch (B->dtype) { 259 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(short, Bool) ; break; 260 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(short, char) ; break; 261 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(short, short) ; break; 262 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(short, int) ; break; 263 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(short, long) ; break; 264 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(short, float) ; break; 265 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(short, double) ; break; 266 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(short, Complex) ; break; 267 | } 268 | break; 269 | 270 | case ARRAY_TYPE_INT: 271 | switch (B->dtype) { 272 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(int, Bool) ; break; 273 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(int, char) ; break; 274 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(int, short) ; break; 275 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(int, int) ; break; 276 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(int, long) ; break; 277 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(int, float) ; break; 278 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(int, double) ; break; 279 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(int, Complex) ; break; 280 | } 281 | break; 282 | 283 | case ARRAY_TYPE_LONG: 284 | switch (B->dtype) { 285 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(long, Bool) ; break; 286 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(long, char) ; break; 287 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(long, short) ; break; 288 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(long, int) ; break; 289 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(long, long) ; break; 290 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(long, float) ; break; 291 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(long, double) ; break; 292 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(long, Complex) ; break; 293 | } 294 | break; 295 | 296 | case ARRAY_TYPE_FLOAT: 297 | switch (B->dtype) { 298 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(float, Bool) ; break; 299 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(float, char) ; break; 300 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(float, short) ; break; 301 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(float, int) ; break; 302 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(float, long) ; break; 303 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(float, float) ; break; 304 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(float, double) ; break; 305 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(float, Complex) ; break; 306 | } 307 | break; 308 | 309 | case ARRAY_TYPE_DOUBLE: 310 | switch (B->dtype) { 311 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(double, Bool) ; break; 312 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(double, char) ; break; 313 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(double, short) ; break; 314 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(double, int) ; break; 315 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(double, long) ; break; 316 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(double, float) ; break; 317 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(double, double) ; break; 318 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(double, Complex) ; break; 319 | } 320 | break; 321 | 322 | case ARRAY_TYPE_COMPLEX: 323 | switch (B->dtype) { 324 | case ARRAY_TYPE_BOOL : EXPR_ASSIGN2(Complex, Bool) ; break; 325 | case ARRAY_TYPE_CHAR : EXPR_ASSIGN2(Complex, char) ; break; 326 | case ARRAY_TYPE_SHORT : EXPR_ASSIGN2(Complex, short) ; break; 327 | case ARRAY_TYPE_INT : EXPR_ASSIGN2(Complex, int) ; break; 328 | case ARRAY_TYPE_LONG : EXPR_ASSIGN2(Complex, long) ; break; 329 | case ARRAY_TYPE_FLOAT : EXPR_ASSIGN2(Complex, float) ; break; 330 | case ARRAY_TYPE_DOUBLE : EXPR_ASSIGN2(Complex, double) ; break; 331 | case ARRAY_TYPE_COMPLEX : EXPR_ASSIGN2(Complex, Complex) ; break; 332 | } 333 | break; 334 | } 335 | } 336 | 337 | struct Array array_new_from_slice(const struct Array *B1, 338 | int *start, int *stop, int *skip, int Nd) 339 | // ----------------------------------------------------------------------------- 340 | // Extracts a slice from B1, and returns it as the contiguous array 'B0' 341 | // ----------------------------------------------------------------------------- 342 | // @start : starting indices into B1 343 | // @stop : upper bound on selection (non-inclusive) 344 | // @skip : distance between entries of B1 along each axis 345 | // @Nd : the number of axes in each array 346 | // ----------------------------------------------------------------------------- 347 | { 348 | 349 | int *J = (int*) malloc(Nd*sizeof(int)); // current indices into B1 350 | int *N = (int*) malloc(Nd*sizeof(int)); // number of elements to select 351 | int *S = (int*) malloc(Nd*sizeof(int)); // strides (in memory) along each axis 352 | 353 | int ntot = 1; 354 | 355 | for (int d=0; d=0; --d) S[d] = S[d+1] * B1->shape[d+1]; 363 | 364 | 365 | struct Array B0 = array_new_zeros(ntot, B1->dtype); 366 | array_resize(&B0, N, Nd); 367 | int sizeof_T = array_sizeof(B0.dtype); 368 | int m = 0; // indexes into B0, advanced uniformly 369 | 370 | 371 | char *b0 = (char*) B0 .data; 372 | char *b1 = (char*) B1->data; 373 | 374 | 375 | while (J[0] < N[0]) { 376 | 377 | int M = 0; 378 | for (int d=0; ddtype); 408 | 409 | char *b0 = (char*) malloc(sizeof_T); 410 | char *b1 = (char*) B1->data; 411 | 412 | int m = 0; 413 | 414 | for (int n=0; nsize; ++n) { 415 | if (((Bool*)M->data)[n]) { 416 | b0 = (char*) realloc(b0, (++m)*sizeof(double)); 417 | memcpy(b0 + (m-1)*sizeof_T, b1 + n*sizeof_T, sizeof_T); 418 | } 419 | } 420 | 421 | struct Array B0 = array_new_zeros(m, B1->dtype); 422 | memcpy(B0.data, b0, m*sizeof_T); 423 | free(b0); 424 | 425 | return B0; 426 | } 427 | -------------------------------------------------------------------------------- /src/lunum.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lualib.h" 8 | #include "lauxlib.h" 9 | #include "lunum.h" 10 | 11 | 12 | 13 | #define LUA_NEW_METAMETHOD(luastate, obj, funcname) { \ 14 | lua_pushfstring((luastate), "__%s", (#funcname)); \ 15 | lua_pushcfunction((luastate), (luaC_##obj##__##funcname)); \ 16 | lua_settable((luastate), -3); \ 17 | } 18 | 19 | #define LUA_NEW_MODULEMETHOD(luastate, obj, funcname) { \ 20 | lua_pushfstring((luastate), "%s", (#funcname)); \ 21 | lua_pushcfunction((luastate), (luaC_##obj##_##funcname)); \ 22 | lua_settable((luastate), -3); \ 23 | } 24 | 25 | #define LUA_NEW_MODULEDATA(luastate, obj, dataname) { \ 26 | lua_pushstring((luastate), (#dataname)); \ 27 | lua_pushnumber((luastate), (obj)); \ 28 | lua_settable((luastate), -3); \ 29 | } 30 | 31 | 32 | 33 | static int luaC_lunum_array(lua_State *L); 34 | static int luaC_lunum_zeros(lua_State *L); 35 | static int luaC_lunum_range(lua_State *L); 36 | static int luaC_lunum_resize(lua_State *L); 37 | 38 | 39 | static int luaC_lunum_sin(lua_State *L); 40 | static int luaC_lunum_cos(lua_State *L); 41 | static int luaC_lunum_tan(lua_State *L); 42 | 43 | static int luaC_lunum_sin(lua_State *L); 44 | static int luaC_lunum_cos(lua_State *L); 45 | static int luaC_lunum_tan(lua_State *L); 46 | 47 | static int luaC_lunum_asin(lua_State *L); 48 | static int luaC_lunum_acos(lua_State *L); 49 | static int luaC_lunum_atan(lua_State *L); 50 | 51 | static int luaC_lunum_sinh(lua_State *L); 52 | static int luaC_lunum_cosh(lua_State *L); 53 | static int luaC_lunum_tanh(lua_State *L); 54 | 55 | static int luaC_lunum_asinh(lua_State *L); 56 | static int luaC_lunum_acosh(lua_State *L); 57 | static int luaC_lunum_atanh(lua_State *L); 58 | 59 | static int luaC_lunum_exp(lua_State *L); 60 | static int luaC_lunum_log(lua_State *L); 61 | static int luaC_lunum_log10(lua_State *L); 62 | 63 | static int luaC_lunum_conjugate(lua_State *L); 64 | static int luaC_lunum_slice(lua_State *L); 65 | static int luaC_lunum_loadtxt(lua_State *L); 66 | static int luaC_lunum_fromfile(lua_State *L); 67 | 68 | 69 | 70 | static int luaC_array__tostring(lua_State *L); 71 | static int luaC_array__call(lua_State *L); 72 | static int luaC_array__index(lua_State *L); 73 | static int luaC_array__newindex(lua_State *L); 74 | static int luaC_array__add(lua_State *L); 75 | static int luaC_array__sub(lua_State *L); 76 | static int luaC_array__mul(lua_State *L); 77 | static int luaC_array__div(lua_State *L); 78 | static int luaC_array__pow(lua_State *L); 79 | static int luaC_array__unm(lua_State *L); 80 | static int luaC_array__gc(lua_State *L); 81 | 82 | 83 | static int luaC_complex__tostring(lua_State *L); 84 | static int luaC_complex__add(lua_State *L); 85 | static int luaC_complex__sub(lua_State *L); 86 | static int luaC_complex__mul(lua_State *L); 87 | static int luaC_complex__div(lua_State *L); 88 | static int luaC_complex__pow(lua_State *L); 89 | static int luaC_complex__unm(lua_State *L); 90 | static int luaC_complex__eq(lua_State *L); 91 | static int luaC_complex__lt(lua_State *L); 92 | static int luaC_complex__le(lua_State *L); 93 | 94 | 95 | static int _array_binary_op1(lua_State *L, enum ArrayOperation op); 96 | static int _array_binary_op2(lua_State *L, enum ArrayOperation op); 97 | 98 | static int _complex_binary_op1(lua_State *L, enum ArrayOperation op); 99 | static int _complex_binary_op2(lua_State *L, enum ArrayOperation op); 100 | 101 | static void _unary_func(lua_State *L, double(*f)(double), Complex(*g)(Complex), 102 | int cast); 103 | static void _push_value(lua_State *L, enum ArrayType T, void *v); 104 | static int _get_index(lua_State *L, struct Array *A); 105 | 106 | 107 | 108 | // Functions used by unary predicates 109 | // ----------------------------------------------------------------------------- 110 | static double rconj(double x) { return x; } // conj, but for real argument 111 | static double runm(double x) { return -x; } // unary minus, real 112 | static Complex cunm(Complex z) { return -z; } // unary minus, complex 113 | // ----------------------------------------------------------------------------- 114 | 115 | 116 | 117 | 118 | 119 | int luaopen_lunum(lua_State *L) 120 | { 121 | lua_settop(L, 0); // start with an empty stack 122 | 123 | // Create the 'array' metatable 124 | // --------------------------------------------------------------------------- 125 | luaL_newmetatable(L, "array"); 126 | LUA_NEW_METAMETHOD(L, array, tostring); 127 | LUA_NEW_METAMETHOD(L, array, call); 128 | LUA_NEW_METAMETHOD(L, array, index); 129 | LUA_NEW_METAMETHOD(L, array, newindex); 130 | LUA_NEW_METAMETHOD(L, array, add); 131 | LUA_NEW_METAMETHOD(L, array, sub); 132 | LUA_NEW_METAMETHOD(L, array, mul); 133 | LUA_NEW_METAMETHOD(L, array, div); 134 | LUA_NEW_METAMETHOD(L, array, pow); 135 | LUA_NEW_METAMETHOD(L, array, unm); 136 | LUA_NEW_METAMETHOD(L, array, gc); 137 | lua_pop(L, 1); 138 | 139 | 140 | // Create the 'complex' metatable 141 | // --------------------------------------------------------------------------- 142 | luaL_newmetatable(L, "complex"); 143 | LUA_NEW_METAMETHOD(L, complex, tostring); 144 | LUA_NEW_METAMETHOD(L, complex, add); 145 | LUA_NEW_METAMETHOD(L, complex, sub); 146 | LUA_NEW_METAMETHOD(L, complex, mul); 147 | LUA_NEW_METAMETHOD(L, complex, div); 148 | LUA_NEW_METAMETHOD(L, complex, pow); 149 | LUA_NEW_METAMETHOD(L, complex, unm); 150 | LUA_NEW_METAMETHOD(L, complex, eq); 151 | LUA_NEW_METAMETHOD(L, complex, lt); 152 | LUA_NEW_METAMETHOD(L, complex, le); 153 | lua_pop(L, 1); 154 | 155 | 156 | // Create the 'lunum' table 157 | // --------------------------------------------------------------------------- 158 | lua_newtable(L); 159 | LUA_NEW_MODULEMETHOD(L, lunum, array); 160 | LUA_NEW_MODULEMETHOD(L, lunum, zeros); 161 | LUA_NEW_MODULEMETHOD(L, lunum, range); 162 | LUA_NEW_MODULEMETHOD(L, lunum, resize); 163 | 164 | LUA_NEW_MODULEMETHOD(L, lunum, sin); 165 | LUA_NEW_MODULEMETHOD(L, lunum, cos); 166 | LUA_NEW_MODULEMETHOD(L, lunum, tan); 167 | 168 | LUA_NEW_MODULEMETHOD(L, lunum, asin); 169 | LUA_NEW_MODULEMETHOD(L, lunum, acos); 170 | LUA_NEW_MODULEMETHOD(L, lunum, atan); 171 | 172 | LUA_NEW_MODULEMETHOD(L, lunum, sinh); 173 | LUA_NEW_MODULEMETHOD(L, lunum, cosh); 174 | LUA_NEW_MODULEMETHOD(L, lunum, tanh); 175 | 176 | LUA_NEW_MODULEMETHOD(L, lunum, asinh); 177 | LUA_NEW_MODULEMETHOD(L, lunum, acosh); 178 | LUA_NEW_MODULEMETHOD(L, lunum, atanh); 179 | 180 | LUA_NEW_MODULEMETHOD(L, lunum, exp); 181 | LUA_NEW_MODULEMETHOD(L, lunum, log); 182 | LUA_NEW_MODULEMETHOD(L, lunum, log10); 183 | 184 | LUA_NEW_MODULEMETHOD(L, lunum, conjugate); 185 | LUA_NEW_MODULEMETHOD(L, lunum, loadtxt); 186 | LUA_NEW_MODULEMETHOD(L, lunum, fromfile); 187 | 188 | LUA_NEW_MODULEMETHOD(L, lunum, slice); 189 | 190 | 191 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_BOOL , bool); 192 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_CHAR , char); 193 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_SHORT , short); 194 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_INT , int); 195 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_LONG , long); 196 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_FLOAT , float); 197 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_DOUBLE , double); 198 | LUA_NEW_MODULEDATA(L, ARRAY_TYPE_COMPLEX, complex); 199 | 200 | // Register the purely imaginary number 'I' 201 | lunum_pushcomplex(L, I); 202 | lua_setfield(L, 1, "I"); 203 | 204 | lua_setglobal(L, "lunum"); 205 | #include "array_class.lc" // sets the lunum.__array_methods table 206 | 207 | lua_getglobal(L, "lunum"); 208 | return 1; 209 | } 210 | 211 | 212 | 213 | 214 | // ***************************************************************************** 215 | // Implementation of lunum.array metatable 216 | // 217 | // ***************************************************************************** 218 | int luaC_array__gc(lua_State *L) 219 | { 220 | struct Array *A = lunum_checkarray1(L, 1); 221 | array_del(A); 222 | return 0; 223 | } 224 | 225 | int luaC_array__tostring(lua_State *L) 226 | { 227 | struct Array *A = lunum_checkarray1(L, 1); 228 | 229 | lua_pushstring(L, " [ "); 230 | int nstr = 1; 231 | for (int n=0; nsize; ++n) { 232 | 233 | char s[64]; 234 | 235 | switch (A->dtype) { 236 | case ARRAY_TYPE_BOOL : sprintf(s, "%s" , ((Bool*)A->data)[n]?"true":"false"); break; 237 | case ARRAY_TYPE_CHAR : sprintf(s, "%d" , ((char *)A->data)[n]); break; 238 | case ARRAY_TYPE_SHORT : sprintf(s, "%d" , ((short *)A->data)[n]); break; 239 | case ARRAY_TYPE_INT : sprintf(s, "%d" , ((int *)A->data)[n]); break; 240 | case ARRAY_TYPE_LONG : sprintf(s, "%ld", ((long *)A->data)[n]); break; 241 | case ARRAY_TYPE_FLOAT : sprintf(s, "%g" , ((float *)A->data)[n]); break; 242 | case ARRAY_TYPE_DOUBLE : sprintf(s, "%g" , ((double *)A->data)[n]); break; 243 | case ARRAY_TYPE_COMPLEX : sprintf(s, "%g%s%gj", 244 | creal(((Complex*)A->data)[n]), 245 | cimag(((Complex*)A->data)[n]) >= 0.0 ? "+" : "-", 246 | fabs(cimag(((Complex*)A->data)[n]))); break; 247 | } 248 | 249 | if (n == A->size-1) { 250 | lua_pushfstring(L, "%s", s); 251 | } 252 | else { 253 | lua_pushfstring(L, "%s, ", s); 254 | } 255 | if ((n+1) % 10 == 0 && n != 0 && n != A->size-1) { 256 | lua_pushstring(L, "\n "); ++nstr; 257 | } 258 | } 259 | lua_pushstring(L, " ]"); ++nstr; 260 | lua_concat(L, A->size + nstr); 261 | 262 | return 1; 263 | } 264 | 265 | int luaC_array__call(lua_State *L) 266 | { 267 | struct Array *A = lunum_checkarray1(L, 1); 268 | int nind = lua_gettop(L) - 1; 269 | 270 | if (nind != A->ndims) { 271 | luaL_error(L, "wrong number of indices (%d) for array of dimension %d", 272 | nind, A->ndims); 273 | return 0; 274 | } 275 | const int Nd = A->ndims; 276 | int *stride = (int*) malloc(A->ndims * sizeof(int)); 277 | stride[Nd-1] = 1; 278 | 279 | for (int d=Nd-2; d>=0; --d) { 280 | stride[d] = stride[d+1] * A->shape[d+1]; 281 | } 282 | 283 | int m = 0; 284 | for (int d=0; dndims; ++d) { 285 | int i = lua_tointeger(L, d+2); 286 | m += i*stride[d]; 287 | } 288 | 289 | _push_value(L, A->dtype, (char*)A->data + m*array_sizeof(A->dtype)); 290 | free(stride); 291 | 292 | return 1; 293 | } 294 | 295 | int luaC_array__index(lua_State *L) 296 | { 297 | struct Array *A = lunum_checkarray1(L, 1); 298 | 299 | // Figure out what is the format of the input index. If it's a number or a 300 | // table of numbers, then pass it along to _get_index. If it's a table of 301 | // tables or numbers, then assume it's a slice. If it's an array of bools, 302 | // then use it as a mask. 303 | // --------------------------------------------------------------------------- 304 | 305 | if (lunum_hasmetatable(L, 2, "array")) { 306 | struct Array *M = lunum_checkarray1(L, 2); 307 | if (M->dtype != ARRAY_TYPE_BOOL) { 308 | luaL_error(L, "index array must be of type bool"); 309 | } 310 | struct Array B = array_new_from_mask(A, M); 311 | lunum_pusharray1(L, &B); 312 | return 1; 313 | } 314 | else if (lua_type(L, 2) == LUA_TTABLE || lua_type(L, 2) == LUA_TSTRING) { 315 | 316 | lua_getglobal(L, "lunum"); 317 | lua_getfield(L, -1, "__build_slice"); 318 | lua_remove(L, -2); 319 | lua_pushvalue(L, 1); 320 | lua_pushvalue(L, 2); 321 | lua_call(L, 2, 1); 322 | 323 | return 1; 324 | } 325 | 326 | const int m = _get_index(L, A); 327 | _push_value(L, A->dtype, (char*)A->data + array_sizeof(A->dtype)*m); 328 | 329 | return 1; 330 | } 331 | 332 | 333 | int luaC_array__newindex(lua_State *L) 334 | { 335 | struct Array *A = lunum_checkarray1(L, 1); 336 | const int m = _get_index(L, A); 337 | 338 | const enum ArrayType T = A->dtype; 339 | 340 | void *val = lunum_tovalue(L, T); 341 | memcpy((char*)A->data + array_sizeof(T)*m, val, array_sizeof(T)); 342 | free(val); 343 | 344 | return 0; 345 | } 346 | 347 | int luaC_array__add(lua_State *L) { return _array_binary_op1(L, ARRAY_OP_ADD); } 348 | int luaC_array__sub(lua_State *L) { return _array_binary_op1(L, ARRAY_OP_SUB); } 349 | int luaC_array__mul(lua_State *L) { return _array_binary_op1(L, ARRAY_OP_MUL); } 350 | int luaC_array__div(lua_State *L) { return _array_binary_op1(L, ARRAY_OP_DIV); } 351 | int luaC_array__pow(lua_State *L) { return _array_binary_op1(L, ARRAY_OP_POW); } 352 | int luaC_array__unm(lua_State *L) { _unary_func(L, runm, cunm, 0); return 1; } 353 | 354 | 355 | int _array_binary_op1(lua_State *L, enum ArrayOperation op) 356 | { 357 | if (!lunum_hasmetatable(L, 1, "array")) { 358 | struct Array *B = lunum_checkarray1(L, 2); 359 | lunum_upcast(L, 1, B->dtype, B->size); 360 | lua_replace(L, 1); 361 | struct Array *A = lunum_checkarray1(L, 1); 362 | array_resize(A, B->shape, B->ndims); 363 | } 364 | if (!lunum_hasmetatable(L, 2, "array")) { 365 | struct Array *A = lunum_checkarray1(L, 1); 366 | lunum_upcast(L, 2, A->dtype, A->size); 367 | lua_replace(L, 2); 368 | struct Array *B = lunum_checkarray1(L, 2); 369 | array_resize(B, A->shape, A->ndims); 370 | } 371 | return _array_binary_op2(L, op); 372 | } 373 | 374 | int _array_binary_op2(lua_State *L, enum ArrayOperation op) 375 | { 376 | struct Array *A = lunum_checkarray1(L, 1); 377 | struct Array *B = lunum_checkarray1(L, 2); 378 | 379 | 380 | if (A->ndims != B->ndims) { 381 | luaL_error(L, "arrays have different dimensions"); 382 | } 383 | for (int d=0; dndims; ++d) { 384 | if (A->shape[d] != B->shape[d]) { 385 | luaL_error(L, "arrays shapes do not agree"); 386 | } 387 | } 388 | 389 | 390 | const int N = A->size; 391 | enum ArrayType T = (A->dtype >= B->dtype) ? A->dtype : B->dtype; 392 | 393 | struct Array A_ = (A->dtype == T) ? *A : array_new_copy(A, T); 394 | struct Array B_ = (B->dtype == T) ? *B : array_new_copy(B, T); 395 | 396 | struct Array C = array_new_zeros(N, T); 397 | array_resize(&C, A->shape, A->ndims); 398 | lunum_pusharray1(L, &C); 399 | 400 | array_binary_op(&A_, &B_, &C, op); 401 | 402 | luaL_getmetatable(L, "array"); 403 | lua_setmetatable(L, -2); 404 | 405 | if (A->dtype != T) array_del(&A_); 406 | if (B->dtype != T) array_del(&B_); 407 | 408 | return 1; 409 | } 410 | 411 | 412 | 413 | // ***************************************************************************** 414 | // Implementation of lunum.complex metatable 415 | // 416 | // ***************************************************************************** 417 | int luaC_complex__tostring(lua_State *L) 418 | { 419 | Complex z = *((Complex*) luaL_checkudata(L, 1, "complex")); 420 | 421 | lua_pushfstring(L, "%f%s%fj", creal(z), cimag(z)>=0.0?"+":"-", fabs(cimag(z))); 422 | return 1; 423 | } 424 | int luaC_complex__add(lua_State *L) { return _complex_binary_op1(L, ARRAY_OP_ADD); } 425 | int luaC_complex__sub(lua_State *L) { return _complex_binary_op1(L, ARRAY_OP_SUB); } 426 | int luaC_complex__mul(lua_State *L) { return _complex_binary_op1(L, ARRAY_OP_MUL); } 427 | int luaC_complex__div(lua_State *L) { return _complex_binary_op1(L, ARRAY_OP_DIV); } 428 | int luaC_complex__pow(lua_State *L) { return _complex_binary_op1(L, ARRAY_OP_POW); } 429 | int luaC_complex__unm(lua_State *L) { _unary_func(L, runm, cunm, 0); return 1; } 430 | 431 | 432 | // ----------------------------------------------------------------------------- 433 | // Use a dictionary ordering on the complex numbers. Might not be useful too 434 | // often, but it's better than having this behavior undefined. 435 | // ----------------------------------------------------------------------------- 436 | #define LUA_COMPARISON(comp) \ 437 | { \ 438 | Complex z1 = lunum_checkcomplex(L, 1); \ 439 | Complex z2 = lunum_checkcomplex(L, 2); \ 440 | \ 441 | if (creal(z1) != creal(z2)) { \ 442 | lua_pushboolean(L, creal(z1) comp creal(z2)); \ 443 | } \ 444 | else { \ 445 | lua_pushboolean(L, cimag(z1) comp cimag(z2)); \ 446 | } \ 447 | return 1; \ 448 | } \ 449 | 450 | 451 | int luaC_complex__lt(lua_State *L) LUA_COMPARISON(<); 452 | int luaC_complex__le(lua_State *L) LUA_COMPARISON(<=); 453 | int luaC_complex__eq(lua_State *L) 454 | { 455 | Complex z1 = lunum_checkcomplex(L, 1); 456 | Complex z2 = lunum_checkcomplex(L, 2); 457 | lua_pushboolean(L, z1==z2); 458 | return 1; 459 | } 460 | 461 | 462 | int _complex_binary_op1(lua_State *L, enum ArrayOperation op) 463 | { 464 | if (lunum_hasmetatable(L, 1, "array") || 465 | lunum_hasmetatable(L, 2, "array")) { 466 | return _array_binary_op1(L, op); 467 | } 468 | if (!lunum_hasmetatable(L, 1, "complex")) { 469 | lunum_pushcomplex(L, lua_tonumber(L, 1)); 470 | lua_replace(L, 1); 471 | } 472 | if (!lunum_hasmetatable(L, 2, "complex")) { 473 | lunum_pushcomplex(L, lua_tonumber(L, 2)); 474 | lua_replace(L, 2); 475 | } 476 | return _complex_binary_op2(L, op); 477 | } 478 | 479 | int _complex_binary_op2(lua_State *L, enum ArrayOperation op) 480 | { 481 | Complex v = *((Complex*) luaL_checkudata(L, 1, "complex")); 482 | Complex w = *((Complex*) luaL_checkudata(L, 2, "complex")); 483 | 484 | Complex *z = (Complex*) lua_newuserdata(L, sizeof(Complex)); 485 | luaL_getmetatable(L, "complex"); 486 | lua_setmetatable(L, -2); 487 | 488 | switch (op) { 489 | case ARRAY_OP_ADD: *z = v + w; break; 490 | case ARRAY_OP_SUB: *z = v - w; break; 491 | case ARRAY_OP_MUL: *z = v * w; break; 492 | case ARRAY_OP_DIV: *z = v / w; break; 493 | case ARRAY_OP_POW: *z = cpow(v,w); break; 494 | } 495 | 496 | return 1; 497 | } 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | int luaC_lunum_array(lua_State *L) 507 | { 508 | if (lua_type(L, 2) == LUA_TSTRING) { 509 | const enum ArrayType T = array_typeflag(lua_tostring(L, 2)[0]); 510 | lunum_upcast(L, 1, T, 1); 511 | } 512 | else { 513 | const enum ArrayType T = (enum ArrayType) luaL_optinteger(L, 2, ARRAY_TYPE_DOUBLE); 514 | lunum_upcast(L, 1, T, 1); 515 | } 516 | return 1; 517 | } 518 | 519 | int luaC_lunum_zeros(lua_State *L) 520 | { 521 | if (lua_isnumber(L, 1)) { 522 | const int N = luaL_checkinteger(L, 1); 523 | const enum ArrayType T = (enum ArrayType) luaL_optinteger(L, 2, ARRAY_TYPE_DOUBLE); 524 | struct Array A = array_new_zeros(N, T); 525 | lunum_pusharray1(L, &A); 526 | return 1; 527 | } 528 | else if (lua_istable(L, 1) || lunum_hasmetatable(L, 1, "array")) { 529 | 530 | int Nd; 531 | int *N = (int*) lunum_checkarray2(L, 1, ARRAY_TYPE_INT, &Nd); 532 | const enum ArrayType T = (enum ArrayType) luaL_optinteger(L, 2, ARRAY_TYPE_DOUBLE); 533 | 534 | int ntot = 1; 535 | for (int d=0; dsize != ntot) { 570 | luaL_error(L, "new and old total sizes do not agree"); 571 | return 0; 572 | } 573 | array_resize(A, N, Nd); 574 | 575 | return 0; 576 | } 577 | 578 | int luaC_lunum_slice(lua_State *L) 579 | { 580 | 581 | // The first part of this function extracts a slice of the array 'A' according 582 | // to the convention start:stop:skip. The result is a contiguous array 'B' 583 | // having the same number of dimensions as 'A'. 584 | // --------------------------------------------------------------------------- 585 | int Nd0, Nd1, Nd2, Nd3; 586 | 587 | const struct Array *A = lunum_checkarray1(L, 1); // the array to resize 588 | int *start = (int*) lunum_checkarray2(L, 2, ARRAY_TYPE_INT, &Nd0); 589 | int *stop = (int*) lunum_checkarray2(L, 3, ARRAY_TYPE_INT, &Nd1); 590 | int *skip = (int*) lunum_checkarray2(L, 4, ARRAY_TYPE_INT, &Nd2); 591 | int *squeeze = (int*) lunum_checkarray2(L, 5, ARRAY_TYPE_INT, &Nd3); 592 | 593 | 594 | if (Nd0 != A->ndims || Nd1 != A->ndims || Nd2 != A->ndims || Nd3 != A->ndims) { 595 | luaL_error(L, "slice has wrong number of dimensions for array"); 596 | } 597 | 598 | for (int d=0; dndims; ++d) { 599 | if (start[d] < 0 || stop[d] > A->shape[d]) { 600 | luaL_error(L, "slice not within array extent"); 601 | } 602 | } 603 | struct Array B = array_new_from_slice(A, start, stop, skip, Nd0); 604 | 605 | 606 | // The rest of this function deals with squeezing out the size-1 dimensions of 607 | // 'B' which are marked by the 'squeeze' array. 608 | // --------------------------------------------------------------------------- 609 | int Nd_new = 0; 610 | for (int d=0; d 1 || !squeeze[d]) { 623 | shape_new[e] = B.shape[d]; 624 | ++e; 625 | } 626 | } 627 | array_resize(&B, shape_new, Nd_new); 628 | free(shape_new); 629 | } 630 | 631 | lunum_pusharray1(L, &B); 632 | return 1; 633 | } 634 | 635 | 636 | 637 | int luaC_lunum_sin(lua_State *L) { _unary_func(L, sin, csin, 1); return 1; } 638 | int luaC_lunum_cos(lua_State *L) { _unary_func(L, cos, ccos, 1); return 1; } 639 | int luaC_lunum_tan(lua_State *L) { _unary_func(L, tan, ctan, 1); return 1; } 640 | 641 | int luaC_lunum_asin(lua_State *L) { _unary_func(L, asin, casin, 1); return 1; } 642 | int luaC_lunum_acos(lua_State *L) { _unary_func(L, acos, cacos, 1); return 1; } 643 | int luaC_lunum_atan(lua_State *L) { _unary_func(L, atan, catan, 1); return 1; } 644 | 645 | int luaC_lunum_sinh(lua_State *L) { _unary_func(L, sinh, csinh, 1); return 1; } 646 | int luaC_lunum_cosh(lua_State *L) { _unary_func(L, cosh, ccosh, 1); return 1; } 647 | int luaC_lunum_tanh(lua_State *L) { _unary_func(L, tanh, ctanh, 1); return 1; } 648 | 649 | int luaC_lunum_asinh(lua_State *L) { _unary_func(L, asinh, casinh, 1); return 1; } 650 | int luaC_lunum_acosh(lua_State *L) { _unary_func(L, acosh, cacosh, 1); return 1; } 651 | int luaC_lunum_atanh(lua_State *L) { _unary_func(L, atanh, catanh, 1); return 1; } 652 | 653 | int luaC_lunum_exp(lua_State *L) { _unary_func(L, exp, cexp, 1); return 1; } 654 | int luaC_lunum_log(lua_State *L) { _unary_func(L, log, clog, 1); return 1; } 655 | int luaC_lunum_log10(lua_State *L) { _unary_func(L, log10, NULL, 1); return 1; } 656 | int luaC_lunum_conjugate(lua_State *L) { _unary_func(L, rconj, conj, 0); return 1; } 657 | 658 | 659 | int luaC_lunum_loadtxt(lua_State *L) 660 | // ----------------------------------------------------------------------------- 661 | // Opens the text file 'fname' for reading, and parses the data 662 | // line-by-line. It is assumed that the data is all floating point, and that 663 | // only a space is used as a separator. If there are multiple columns then a 2d 664 | // array is created. All rows must have the same number of entries, otherwise an 665 | // error is generated. 666 | // ----------------------------------------------------------------------------- 667 | { 668 | const char *fname = luaL_checkstring(L, 1); 669 | FILE *input = fopen(fname, "r"); 670 | 671 | if (input == NULL) { 672 | luaL_error(L, "no such file %s", fname); 673 | } 674 | 675 | int nline = 0; 676 | int ncols = 0; 677 | int ntot = 0; 678 | double *data = NULL; 679 | 680 | char line[2048]; 681 | 682 | while (fgets(line, sizeof(line), input)) { 683 | 684 | if (strlen(line) == 1) { 685 | continue; 686 | } 687 | 688 | int nvals = 0; 689 | double *vals = NULL; 690 | char *word = strtok(line, " \n"); 691 | 692 | while (word) { 693 | vals = (double*) realloc(vals, ++nvals*sizeof(double)); 694 | vals[nvals-1] = atof(word); 695 | word = strtok(NULL, " \n"); 696 | } 697 | 698 | if (ncols == 0) ncols = nvals; 699 | if (ncols != nvals) { 700 | luaL_error(L, "wrong number of data on line %d of %s", nline, fname); 701 | } 702 | 703 | data = (double*) realloc(data, (ntot+=nvals)*sizeof(double)); 704 | memcpy(data+ntot-nvals, vals, nvals*sizeof(double)); 705 | free(vals); 706 | 707 | ++nline; 708 | } 709 | fclose(input); 710 | 711 | lunum_pusharray2(L, data, ARRAY_TYPE_DOUBLE, ntot); 712 | struct Array *A = lunum_checkarray1(L, -1); 713 | 714 | int shape[2] = { nline, ncols }; 715 | array_resize(A, shape, ncols == 1 ? 1 : 2); 716 | 717 | free(data); 718 | return 1; 719 | } 720 | 721 | 722 | int luaC_lunum_fromfile(lua_State *L) 723 | // ----------------------------------------------------------------------------- 724 | // Opens the binary file 'fname' for reading, and returns a 1d array from the 725 | // data. The file size must be a multiple of the data type 'T'. 726 | // ----------------------------------------------------------------------------- 727 | { 728 | const char *fname = luaL_checkstring(L, 1); 729 | const enum ArrayType T = luaL_optinteger(L, 2, ARRAY_TYPE_DOUBLE); 730 | const int sizeof_T = array_sizeof(T); 731 | 732 | FILE *input = fopen(fname, "rb"); 733 | 734 | if (input == NULL) { 735 | luaL_error(L, "no such file %s", fname); 736 | } 737 | fseek(input, 0L, SEEK_END); const int sz = ftell(input); 738 | fseek(input, 0L, SEEK_SET); 739 | 740 | if (sz % sizeof_T != 0) { 741 | luaL_error(L, "file size must be a multiple of the data type size"); 742 | } 743 | const int N = sz / sizeof_T; 744 | struct Array A = array_new_zeros(N, T); 745 | 746 | fread(A.data, N, sizeof_T, input); 747 | fclose(input); 748 | lunum_pusharray1(L, &A); 749 | 750 | return 1; 751 | } 752 | 753 | 754 | #define EXPR_EVALF(T,N,x) {for(int i=0;idtype); 777 | 778 | switch (B.dtype) { 779 | case ARRAY_TYPE_BOOL : EXPR_EVALF(Bool , B.size, B.data); break; 780 | case ARRAY_TYPE_CHAR : EXPR_EVALF(char , B.size, B.data); break; 781 | case ARRAY_TYPE_SHORT : EXPR_EVALF(short , B.size, B.data); break; 782 | case ARRAY_TYPE_INT : EXPR_EVALF(long , B.size, B.data); break; 783 | case ARRAY_TYPE_LONG : EXPR_EVALF(int , B.size, B.data); break; 784 | case ARRAY_TYPE_FLOAT : EXPR_EVALF(float , B.size, B.data); break; 785 | case ARRAY_TYPE_DOUBLE : EXPR_EVALF(double , B.size, B.data); break; 786 | case ARRAY_TYPE_COMPLEX : EXPR_EVALG(Complex, B.size, B.data); break; 787 | } 788 | 789 | lunum_pusharray1(L, &B); 790 | } 791 | else if (A->dtype <= ARRAY_TYPE_DOUBLE) { 792 | struct Array B = array_new_copy(A, ARRAY_TYPE_DOUBLE); 793 | double *b = (double*) B.data; 794 | for (int i=0; idtype == ARRAY_TYPE_COMPLEX) { 798 | 799 | if (g == NULL) { 800 | luaL_error(L, "complex operation not supported"); 801 | } 802 | 803 | struct Array B = array_new_copy(A, ARRAY_TYPE_COMPLEX); 804 | Complex *b = (Complex*) B.data; 805 | for (int i=0; i= A->size || m < 0) { 819 | luaL_error(L, "index %d out of bounds on array of length %d", m, A->size); 820 | } 821 | } 822 | else if (lua_istable(L, 2)) { 823 | int Nd; 824 | int *ind = (int*) lunum_checkarray2(L, 2, ARRAY_TYPE_INT, &Nd); 825 | 826 | if (A->ndims != Nd) { 827 | luaL_error(L, "wrong number of indices (%d) on array of dimension %d", 828 | Nd, A->ndims); 829 | } 830 | int *stride = (int*) malloc(A->ndims * sizeof(int)); 831 | stride[Nd-1] = 1; 832 | 833 | for (int d=Nd-2; d>=0; --d) { 834 | stride[d] = stride[d+1] * A->shape[d+1]; 835 | } 836 | 837 | for (int d=0; dndims; ++d) { 838 | if (ind[d] >= A->shape[d] || ind[d] < 0) { 839 | luaL_error(L, "array indexed out of bounds (%d) on dimension %d of size %d", 840 | ind[d], d, A->shape[d]); 841 | } 842 | m += ind[d]*stride[d]; 843 | } 844 | free(stride); 845 | } 846 | return m; 847 | } 848 | 849 | 850 | void _push_value(lua_State *L, enum ArrayType T, void *v) 851 | { 852 | switch (T) { 853 | case ARRAY_TYPE_BOOL : lua_pushboolean(L, *((Bool *)v)); break; 854 | case ARRAY_TYPE_CHAR : lua_pushnumber (L, *((char *)v)); break; 855 | case ARRAY_TYPE_SHORT : lua_pushnumber (L, *((short *)v)); break; 856 | case ARRAY_TYPE_INT : lua_pushnumber (L, *((int *)v)); break; 857 | case ARRAY_TYPE_LONG : lua_pushnumber (L, *((long *)v)); break; 858 | case ARRAY_TYPE_FLOAT : lua_pushnumber (L, *((float *)v)); break; 859 | case ARRAY_TYPE_DOUBLE : lua_pushnumber (L, *((double *)v)); break; 860 | case ARRAY_TYPE_COMPLEX : lunum_pushcomplex (L, *((Complex*)v)); break; 861 | } 862 | } 863 | 864 | --------------------------------------------------------------------------------