@hsusulist/luc 0.2.5 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -14
- package/bin/luc.js +295 -5
- package/install.ps1 +222 -9
- package/package.json +4 -5
- package/scripts/build-linux.js +34 -0
- package/scripts/postinstall.js +8 -2
- package/scripts/postpack.js +14 -0
- package/scripts/prepack.js +27 -0
- package/src/lcode-linux/lcode.c +582 -0
- package/src/luc.h +303 -0
- package/src/luc_core.c +5527 -0
- package/src/luc_libs.c +5760 -0
- package/src/luc_nums.c +1747 -0
- package/src/luc_trans.c +1735 -0
package/src/luc_nums.c
ADDED
|
@@ -0,0 +1,1747 @@
|
|
|
1
|
+
/* Extra list, search, string and time APIs exposed by the nums system module. */
|
|
2
|
+
|
|
3
|
+
static Table *nums_list_api;
|
|
4
|
+
static Table *nums_search_api;
|
|
5
|
+
static Table *nums_time_api;
|
|
6
|
+
static Table *nums_text_api;
|
|
7
|
+
static Table *nums_matrix_api;
|
|
8
|
+
static Table *nums_sets_api;
|
|
9
|
+
static Table *nums_finance_api;
|
|
10
|
+
|
|
11
|
+
static Table *nums_list_arg(LucState *L,int base,int nargs,int i,const char *fn){
|
|
12
|
+
Value v=AR(i);
|
|
13
|
+
if(v.t!=LT_LIST) luc_error("%s: expected a list (got %s)",fn,type_name(v));
|
|
14
|
+
return AS_TAB(v);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static int nums_int_arg(LucState *L,int base,int nargs,int i,const char *fn){
|
|
18
|
+
return checkint(L,base,nargs,i,fn);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static int nums_nonnegative(int n,const char *fn){
|
|
22
|
+
if(n<0){ luc_error("%s: size must be >= 0",fn); return 0; }
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static Table *nums_list_copy(Table *src){
|
|
27
|
+
Table *dst=tab_new(1);
|
|
28
|
+
for(int i=0;i<src->alen;i++) list_push(dst,src->arr[i]);
|
|
29
|
+
return dst;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static Table *nums_list_from_value(Value v,const char *fn){
|
|
33
|
+
Table *out=tab_new(1);
|
|
34
|
+
if(v.t==LT_LIST){
|
|
35
|
+
Table *src=AS_TAB(v);
|
|
36
|
+
for(int i=0;i<src->alen;i++) list_push(out,src->arr[i]);
|
|
37
|
+
}else if(v.t==LT_STR){
|
|
38
|
+
Str *s=AS_STR(v);
|
|
39
|
+
for(int i=0;i<s->len;i++) list_push(out,strv(s->s+i,1));
|
|
40
|
+
}else if(v.t==LT_TABLE){
|
|
41
|
+
Value key=NIL,kval,val;
|
|
42
|
+
while(tab_next(AS_TAB(v),key,&kval,&val)){key=kval;list_push(out,val);}
|
|
43
|
+
}else if(v.t!=LT_NIL) list_push(out,v);
|
|
44
|
+
else if(!strcmp(fn,"fromIterator")) luc_error("list.fromIterator: expected an iterable value");
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void nums_flatten_into(Table *src,Table *dst,int depth){
|
|
49
|
+
if(depth>128) luc_error("list.flatten: nesting exceeds 128 levels");
|
|
50
|
+
for(int i=0;i<src->alen;i++){
|
|
51
|
+
Value v=src->arr[i];
|
|
52
|
+
if(v.t==LT_LIST) nums_flatten_into(AS_TAB(v),dst,depth+1);
|
|
53
|
+
else list_push(dst,v);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static int nums_shape_value(Value value,int *shape,int depth){
|
|
58
|
+
if(value.t!=LT_LIST) return depth;
|
|
59
|
+
if(depth>=16) luc_error("list: nesting exceeds 16 dimensions");
|
|
60
|
+
Table *t=AS_TAB(value); shape[depth]=t->alen;
|
|
61
|
+
if(t->alen==0) return depth+1;
|
|
62
|
+
int rank=nums_shape_value(t->arr[0],shape,depth+1);
|
|
63
|
+
for(int i=1;i<t->alen;i++){
|
|
64
|
+
int other[16]={0};
|
|
65
|
+
int other_rank=nums_shape_value(t->arr[i],other,depth+1);
|
|
66
|
+
if(other_rank!=rank) luc_error("list: nested list must be rectangular");
|
|
67
|
+
for(int d=depth+1;d<rank;d++)
|
|
68
|
+
if(other[d]!=shape[d]) luc_error("list: nested list must be rectangular");
|
|
69
|
+
}
|
|
70
|
+
return rank;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static int nums_list_product(const int *dims,int ndims){
|
|
74
|
+
int total=1;
|
|
75
|
+
for(int i=0;i<ndims;i++){
|
|
76
|
+
if(dims[i]<0) luc_error("list.reshape: dimensions must be >= 0");
|
|
77
|
+
if(dims[i] && total>10000000/dims[i])
|
|
78
|
+
luc_error("list.reshape: result is too large");
|
|
79
|
+
total*=dims[i];
|
|
80
|
+
}
|
|
81
|
+
return total;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static Table *nums_reshape_level(Table *flat,const int *dims,int ndims,int level,int *at){
|
|
85
|
+
Table *out=tab_new(1);
|
|
86
|
+
int n=dims[level];
|
|
87
|
+
for(int i=0;i<n;i++){
|
|
88
|
+
if(level+1==ndims) list_push(out,flat->arr[(*at)++]);
|
|
89
|
+
else list_push(out,mkobj(LT_LIST,nums_reshape_level(flat,dims,ndims,level+1,at)));
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static int nums_value_less(Value a,Value b){
|
|
95
|
+
if(a.t==LT_NUM&&b.t==LT_NUM) return a.u.n<b.u.n;
|
|
96
|
+
if(a.t==LT_STR&&b.t==LT_STR) return vm_lessthan(a,b,0);
|
|
97
|
+
if(a.t==LT_BOOL&&b.t==LT_BOOL) return !a.u.b&&b.u.b;
|
|
98
|
+
return vm_lessthan(a,b,0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static void nums_sort_table(Table *t,int descending){
|
|
102
|
+
for(int i=1;i<t->alen;i++){
|
|
103
|
+
Value key=t->arr[i]; int j=i-1;
|
|
104
|
+
while(j>=0&&(descending?nums_value_less(key,t->arr[j]):nums_value_less(t->arr[j],key))){
|
|
105
|
+
t->arr[j+1]=t->arr[j]; j--;
|
|
106
|
+
}
|
|
107
|
+
t->arr[j+1]=key;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static void nums_list_register(Table *t,const char *name,CFn fn){
|
|
112
|
+
char lower[64],alllower[64],upper[64];
|
|
113
|
+
size_t n=strlen(name);
|
|
114
|
+
if(n>=sizeof lower){ luc_error("nums.list: function name too long"); return; }
|
|
115
|
+
memcpy(lower,name,n+1);
|
|
116
|
+
memcpy(alllower,name,n+1);
|
|
117
|
+
memcpy(upper,name,n+1);
|
|
118
|
+
lower[0]=(char)tolower((unsigned char)lower[0]);
|
|
119
|
+
upper[0]=(char)toupper((unsigned char)upper[0]);
|
|
120
|
+
for(size_t i=0;i<n;i++) alllower[i]=(char)tolower((unsigned char)alllower[i]);
|
|
121
|
+
CFunc *cf=cfunc_new(fn,name,0);
|
|
122
|
+
tab_set(t,cstrv(name),mkobj(LT_CFUNC,cf));
|
|
123
|
+
if(strcmp(name,lower)){ cf=cfunc_new(fn,name,0); tab_set(t,cstrv(lower),mkobj(LT_CFUNC,cf)); }
|
|
124
|
+
if(strcmp(name,upper)&&strcmp(lower,upper)){ cf=cfunc_new(fn,name,0); tab_set(t,cstrv(upper),mkobj(LT_CFUNC,cf)); }
|
|
125
|
+
if(strcmp(name,alllower)&&strcmp(lower,alllower)){
|
|
126
|
+
cf=cfunc_new(fn,name,0); tab_set(t,cstrv(alllower),mkobj(LT_CFUNC,cf));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static int nums_write_value(FILE *f,Value v){
|
|
131
|
+
if(v.t==LT_STR){
|
|
132
|
+
static const char hex[]="0123456789abcdef"; Str *s=AS_STR(v);
|
|
133
|
+
if(fputc('s',f)==EOF) return 0;
|
|
134
|
+
for(int i=0;i<s->len;i++){
|
|
135
|
+
unsigned char c=(unsigned char)s->s[i];
|
|
136
|
+
if(fputc(hex[c>>4],f)==EOF||fputc(hex[c&15],f)==EOF) return 0;
|
|
137
|
+
}
|
|
138
|
+
return fputc('\n',f)!=EOF;
|
|
139
|
+
}
|
|
140
|
+
if(v.t==LT_NUM){ Str *s=tostr(v); return fprintf(f,"n%.*s\n",s->len,s->s)>=0; }
|
|
141
|
+
if(v.t==LT_BOOL) return fprintf(f,"b%d\n",v.u.b)!=EOF;
|
|
142
|
+
if(v.t==LT_NIL) return fputs("z\n",f)!=EOF;
|
|
143
|
+
if(v.t==LT_LIST){
|
|
144
|
+
Table *t=AS_TAB(v);
|
|
145
|
+
if(fputs("l\n",f)==EOF) return 0;
|
|
146
|
+
for(int i=0;i<t->alen;i++) if(!nums_write_value(f,t->arr[i])) return 0;
|
|
147
|
+
return fputs("e\n",f)!=EOF;
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static int nums_read_value(FILE *f,Value *out,int depth){
|
|
153
|
+
if(depth>128){ luc_error("list.readList: nesting exceeds 128 levels"); return 0; }
|
|
154
|
+
char *line=NULL; size_t cap=0; long got;
|
|
155
|
+
#if defined(_WIN32)
|
|
156
|
+
char buf[65536];
|
|
157
|
+
if(!fgets(buf,sizeof buf,f)) return 0;
|
|
158
|
+
got=(long)strlen(buf);
|
|
159
|
+
line=(char*)malloc((size_t)got+1);
|
|
160
|
+
if(!line) luc_error("list.readList: out of memory");
|
|
161
|
+
memcpy(line,buf,(size_t)got+1);
|
|
162
|
+
#else
|
|
163
|
+
got=getline(&line,&cap,f);
|
|
164
|
+
if(got<0){ free(line); return 0; }
|
|
165
|
+
#endif
|
|
166
|
+
while(got>0&&(line[got-1]=='\n'||line[got-1]=='\r')) line[--got]=0;
|
|
167
|
+
if(got==0){ free(line); luc_error("list.readList: malformed value"); return 0; }
|
|
168
|
+
if(line[0]=='e'&&got==1){ free(line); return -1; }
|
|
169
|
+
if(line[0]=='s'){
|
|
170
|
+
int n=(int)got-1;
|
|
171
|
+
if(n%2){free(line);luc_error("list.readList: malformed string encoding");return 0;}
|
|
172
|
+
char *decoded=(char*)malloc((size_t)(n/2?n/2:1));
|
|
173
|
+
if(!decoded){free(line);luc_error("list.readList: out of memory");return 0;}
|
|
174
|
+
for(int i=0;i<n;i+=2){
|
|
175
|
+
int hi=hexval((unsigned char)line[i+1]),lo=hexval((unsigned char)line[i+2]);
|
|
176
|
+
if(hi<0||lo<0){free(decoded);free(line);luc_error("list.readList: malformed string encoding");return 0;}
|
|
177
|
+
decoded[i/2]=(char)((hi<<4)|lo);
|
|
178
|
+
}
|
|
179
|
+
*out=strv(decoded,n/2);free(decoded);
|
|
180
|
+
}
|
|
181
|
+
else if(line[0]=='n'){
|
|
182
|
+
double d;
|
|
183
|
+
if(!str2num(line+1,(int)got-1,&d)){ free(line); luc_error("list.readList: invalid number"); return 0; }
|
|
184
|
+
*out=mknum(d);
|
|
185
|
+
}else if(line[0]=='b'&&(got==2&&(line[1]=='0'||line[1]=='1'))) *out=mkbool(line[1]=='1');
|
|
186
|
+
else if(line[0]=='z'&&got==1) *out=NIL;
|
|
187
|
+
else if(line[0]=='l'&&got==1){
|
|
188
|
+
Table *list=tab_new(1); Value v;
|
|
189
|
+
free(line);
|
|
190
|
+
int state;
|
|
191
|
+
while((state=nums_read_value(f,&v,depth+1))>0){
|
|
192
|
+
list_push(list,v);
|
|
193
|
+
}
|
|
194
|
+
if(state==0){ luc_error("list.readList: unterminated nested list"); return 0; }
|
|
195
|
+
*out=mkobj(LT_LIST,list); return 1;
|
|
196
|
+
}else { free(line); luc_error("list.readList: malformed value"); return 0; }
|
|
197
|
+
free(line); return 1;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
LFN(f_nums_file_api){
|
|
201
|
+
const char *fn=self->name;
|
|
202
|
+
if(!strcmp(fn,"readText")||!strcmp(fn,"readBinary")){
|
|
203
|
+
Str *path=checkstr(L,base,nargs,0,fn); FILE *f=fopen(path->s,"rb");
|
|
204
|
+
if(!f) luc_error("%s: cannot open file",fn);
|
|
205
|
+
Str *data=read_all_str(f); int failed=ferror(f); fclose(f);
|
|
206
|
+
if(failed) luc_error("%s: read failed",fn);
|
|
207
|
+
RET(0,mkobj(LT_STR,data)); return 1;
|
|
208
|
+
}
|
|
209
|
+
if(!strcmp(fn,"writeText")||!strcmp(fn,"writeBinary")){
|
|
210
|
+
Str *path=checkstr(L,base,nargs,0,fn); Str *data=checkstr(L,base,nargs,1,fn);
|
|
211
|
+
FILE *f=fopen(path->s,"wb"); if(!f) luc_error("%s: cannot open file",fn);
|
|
212
|
+
int ok=fwrite(data->s,1,(size_t)data->len,f)==(size_t)data->len;
|
|
213
|
+
if(fclose(f)!=0) ok=0;
|
|
214
|
+
if(!ok) luc_error("%s: write failed",fn);
|
|
215
|
+
RET(0,AR(1)); return 1;
|
|
216
|
+
}
|
|
217
|
+
if(!strcmp(fn,"readList")||!strcmp(fn,"readData")){
|
|
218
|
+
Str *path=checkstr(L,base,nargs,0,fn); FILE *f=fopen(path->s,"rb");
|
|
219
|
+
if(!f) luc_error("%s: cannot open file",fn);
|
|
220
|
+
Table *out=tab_new(1); Value v;
|
|
221
|
+
int state;
|
|
222
|
+
while((state=nums_read_value(f,&v,0))>0) list_push(out,v);
|
|
223
|
+
if(state<0){fclose(f);luc_error("%s: unexpected list terminator",fn);}
|
|
224
|
+
int failed=ferror(f); fclose(f);
|
|
225
|
+
if(failed) luc_error("%s: read failed",fn);
|
|
226
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
227
|
+
}
|
|
228
|
+
if(!strcmp(fn,"writeCompressed")){
|
|
229
|
+
Str *path=checkstr(L,base,nargs,0,fn); FILE *raw=tmpfile();
|
|
230
|
+
if(!raw) luc_error("list.writeCompressed: cannot create temporary data");
|
|
231
|
+
int ok=1;
|
|
232
|
+
for(int a=1;a<nargs&&ok;a++){
|
|
233
|
+
Table *t=nums_list_arg(L,base,nargs,a,fn);
|
|
234
|
+
for(int i=0;i<t->alen&&ok;i++) ok=nums_write_value(raw,t->arr[i]);
|
|
235
|
+
}
|
|
236
|
+
if(!ok||fflush(raw)!=0||fseek(raw,0,SEEK_END)!=0){fclose(raw);luc_error("list.writeCompressed: staging failed");}
|
|
237
|
+
long length=ftell(raw);
|
|
238
|
+
if(length<0||fseek(raw,0,SEEK_SET)!=0){fclose(raw);luc_error("list.writeCompressed: staging failed");}
|
|
239
|
+
unsigned char *src=(unsigned char*)malloc((size_t)(length?length:1));
|
|
240
|
+
if(!src){fclose(raw);luc_error("list.writeCompressed: out of memory");}
|
|
241
|
+
if(fread(src,1,(size_t)length,raw)!=(size_t)length){free(src);fclose(raw);luc_error("list.writeCompressed: staging read failed");}
|
|
242
|
+
fclose(raw);
|
|
243
|
+
FILE *dst=fopen(path->s,"wb");
|
|
244
|
+
if(!dst){free(src);luc_error("list.writeCompressed: cannot open file");}
|
|
245
|
+
ok=fwrite("LUC-RLE-1\n",1,10,dst)==10;
|
|
246
|
+
for(long i=0;i<length&&ok;){
|
|
247
|
+
long run=1;
|
|
248
|
+
while(i+run<length&&src[i+run]==src[i]&&run<255) run++;
|
|
249
|
+
unsigned char pair[2]={(unsigned char)run,src[i]};
|
|
250
|
+
ok=fwrite(pair,1,2,dst)==2; i+=run;
|
|
251
|
+
}
|
|
252
|
+
free(src); if(fclose(dst)!=0) ok=0;
|
|
253
|
+
if(!ok) luc_error("list.writeCompressed: write failed");
|
|
254
|
+
RET(0,mkbool(1));return 1;
|
|
255
|
+
}
|
|
256
|
+
if(!strcmp(fn,"writeList")||!strcmp(fn,"writeBundle")){
|
|
257
|
+
Str *path=checkstr(L,base,nargs,0,fn); FILE *f=fopen(path->s,"wb");
|
|
258
|
+
if(!f) luc_error("%s: cannot open file",fn);
|
|
259
|
+
int first=!strcmp(fn,"writeList")?1:1, ok=1;
|
|
260
|
+
if(!strcmp(fn,"writeBundle")||!strcmp(fn,"writeCompressed"))
|
|
261
|
+
ok=fputs("LUC-BUNDLE-1\n",f)!=EOF;
|
|
262
|
+
for(int a=first;a<nargs&&ok;a++){
|
|
263
|
+
Table *t=nums_list_arg(L,base,nargs,a,fn);
|
|
264
|
+
if(!strcmp(fn,"writeBundle")||!strcmp(fn,"writeCompressed"))
|
|
265
|
+
ok=fprintf(f,"A%u\n",(unsigned)t->alen)>=0;
|
|
266
|
+
for(int i=0;i<t->alen&&ok;i++) ok=nums_write_value(f,t->arr[i]);
|
|
267
|
+
if(!strcmp(fn,"writeBundle")||!strcmp(fn,"writeCompressed"))
|
|
268
|
+
ok=ok&&fputs(".\n",f)!=EOF;
|
|
269
|
+
}
|
|
270
|
+
if(fclose(f)!=0) ok=0;
|
|
271
|
+
if(!ok) luc_error("%s: write failed",fn);
|
|
272
|
+
RET(0,mkbool(1)); return 1;
|
|
273
|
+
}
|
|
274
|
+
if(!strcmp(fn,"parseList")){
|
|
275
|
+
Str *src=checkstr(L,base,nargs,0,fn); Table *out=tab_new(1);
|
|
276
|
+
int i=0;
|
|
277
|
+
while(i<src->len){
|
|
278
|
+
while(i<src->len&&(isspace((unsigned char)src->s[i])||src->s[i]==','||src->s[i]=='['||src->s[i]==']')) i++;
|
|
279
|
+
int start=i;
|
|
280
|
+
while(i<src->len&&!isspace((unsigned char)src->s[i])&&src->s[i]!=','&&src->s[i]!=']') i++;
|
|
281
|
+
if(i==start) continue;
|
|
282
|
+
double d;
|
|
283
|
+
int width=i-start;
|
|
284
|
+
if(width==4&&!memcmp(src->s+start,"true",4)) list_push(out,mkbool(1));
|
|
285
|
+
else if(width==5&&!memcmp(src->s+start,"false",5)) list_push(out,mkbool(0));
|
|
286
|
+
else if(width==3&&!memcmp(src->s+start,"nil",3)) list_push(out,NIL);
|
|
287
|
+
else if(str2num(src->s+start,width,&d)) list_push(out,mknum(d));
|
|
288
|
+
else list_push(out,strv(src->s+start,i-start));
|
|
289
|
+
}
|
|
290
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
291
|
+
}
|
|
292
|
+
if(!strcmp(fn,"listString")){
|
|
293
|
+
Table *t=nums_list_arg(L,base,nargs,0,fn);
|
|
294
|
+
Str *sep=nargs>=2?checkstr(L,base,nargs,1,fn):str_fromc(",");
|
|
295
|
+
size_t cap=64,len=0; char *buf=(char*)malloc(cap);
|
|
296
|
+
if(!buf) luc_error("list.listString: out of memory");
|
|
297
|
+
for(int i=0;i<t->alen;i++){
|
|
298
|
+
Str *s=tostr(t->arr[i]); size_t need=len+(size_t)s->len+(i?(size_t)sep->len:0)+1;
|
|
299
|
+
if(need>cap){ while(need>cap) cap*=2; buf=(char*)realloc(buf,cap); if(!buf){luc_error("list.listString: out of memory");} }
|
|
300
|
+
if(i){memcpy(buf+len,sep->s,(size_t)sep->len);len+=(size_t)sep->len;}
|
|
301
|
+
memcpy(buf+len,s->s,(size_t)s->len);len+=(size_t)s->len;
|
|
302
|
+
}
|
|
303
|
+
Value result=strv(buf,(int)len); free(buf); RET(0,result); return 1;
|
|
304
|
+
}
|
|
305
|
+
luc_error("list.%s: not implemented",fn); return 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static int nums_prefix(Str *s,Str *p){
|
|
309
|
+
return p->len<=s->len&&!memcmp(s->s,p->s,(size_t)p->len);
|
|
310
|
+
}
|
|
311
|
+
static int nums_suffix(Str *s,Str *p){
|
|
312
|
+
return p->len<=s->len&&!memcmp(s->s+s->len-p->len,p->s,(size_t)p->len);
|
|
313
|
+
}
|
|
314
|
+
LFN(f_nums_text_api){
|
|
315
|
+
const char *fn=self->name;
|
|
316
|
+
Str *s,*a,*b; char *buf; size_t len,cap;
|
|
317
|
+
if(!strcmp(fn,"concat")||!strcmp(fn,"joinText")){
|
|
318
|
+
Table *t=NULL; Str *sep=str_fromc("");
|
|
319
|
+
int first=0;
|
|
320
|
+
if(!strcmp(fn,"joinText")){
|
|
321
|
+
t=nums_list_arg(L,base,nargs,0,fn); first=1;
|
|
322
|
+
if(nargs>=2) sep=checkstr(L,base,nargs,1,fn);
|
|
323
|
+
}
|
|
324
|
+
cap=64; len=0; buf=(char*)malloc(cap);
|
|
325
|
+
if(!buf) luc_error("string.concat: out of memory");
|
|
326
|
+
int n=t?t->alen:nargs;
|
|
327
|
+
for(int i=0;i<n;i++){
|
|
328
|
+
Value v=t?t->arr[i]:AR(i);
|
|
329
|
+
Str *part=tostr(v); size_t need=len+(size_t)part->len+(i?(size_t)sep->len:0)+1;
|
|
330
|
+
if(need>cap){ while(need>cap) cap*=2; buf=(char*)realloc(buf,cap); if(!buf) luc_error("string.concat: out of memory"); }
|
|
331
|
+
if(i){memcpy(buf+len,sep->s,(size_t)sep->len);len+=(size_t)sep->len;}
|
|
332
|
+
memcpy(buf+len,part->s,(size_t)part->len);len+=(size_t)part->len;
|
|
333
|
+
}
|
|
334
|
+
(void)first;
|
|
335
|
+
Value result=strv(buf,(int)len); free(buf); RET(0,result); return 1;
|
|
336
|
+
}
|
|
337
|
+
if(!strcmp(fn,"repeatText")){
|
|
338
|
+
s=checkstr(L,base,nargs,0,fn); int n=nums_nonnegative(checkint(L,base,nargs,1,fn),fn);
|
|
339
|
+
if((int64_t)s->len*n>100000000) luc_error("string.repeatText: result is too large");
|
|
340
|
+
size_t total=(size_t)s->len*(size_t)n; buf=(char*)malloc(total?total:1);
|
|
341
|
+
if(!buf) luc_error("string.repeatText: out of memory");
|
|
342
|
+
for(int i=0;i<n;i++) memcpy(buf+(size_t)i*s->len,s->s,(size_t)s->len);
|
|
343
|
+
Value v=strv(buf,(int)total); free(buf); RET(0,v); return 1;
|
|
344
|
+
}
|
|
345
|
+
if(!strcmp(fn,"capitalize")||!strcmp(fn,"lowercase")||!strcmp(fn,"uppercase")||
|
|
346
|
+
!strcmp(fn,"titleCase")||!strcmp(fn,"reverseCase")){
|
|
347
|
+
s=checkstr(L,base,nargs,0,fn); buf=(char*)malloc((size_t)s->len+1);
|
|
348
|
+
if(!buf) luc_error("string: out of memory");
|
|
349
|
+
int word=1;
|
|
350
|
+
for(int i=0;i<s->len;i++){
|
|
351
|
+
unsigned char c=(unsigned char)s->s[i];
|
|
352
|
+
if(!strcmp(fn,"lowercase")) buf[i]=(char)tolower(c);
|
|
353
|
+
else if(!strcmp(fn,"uppercase")) buf[i]=(char)toupper(c);
|
|
354
|
+
else if(!strcmp(fn,"reverseCase")) buf[i]=(char)(islower(c)?toupper(c):isupper(c)?tolower(c):c);
|
|
355
|
+
else if(!strcmp(fn,"capitalize")) buf[i]=(char)(i==0?toupper(c):c);
|
|
356
|
+
else { buf[i]=(char)(word?toupper(c):c); word=isspace(c)!=0; }
|
|
357
|
+
}
|
|
358
|
+
buf[s->len]=0; Value v=strv(buf,s->len); free(buf); RET(0,v); return 1;
|
|
359
|
+
}
|
|
360
|
+
if(!strcmp(fn,"alignCenter")){
|
|
361
|
+
s=checkstr(L,base,nargs,0,fn); int width=nums_nonnegative(checkint(L,base,nargs,1,fn),fn);
|
|
362
|
+
if(width<=s->len){ RET(0,AR(0)); return 1; }
|
|
363
|
+
int left=(width-s->len)/2,right=width-s->len-left;
|
|
364
|
+
len=(size_t)width; buf=(char*)malloc(len?len:1); if(!buf) luc_error("string.alignCenter: out of memory");
|
|
365
|
+
memset(buf,' ',(size_t)left); memcpy(buf+left,s->s,(size_t)s->len); memset(buf+left+s->len,' ',(size_t)right);
|
|
366
|
+
Value v=strv(buf,width); free(buf); RET(0,v); return 1;
|
|
367
|
+
}
|
|
368
|
+
if(!strcmp(fn,"trim")||!strcmp(fn,"trimLeft")||!strcmp(fn,"trimRight")){
|
|
369
|
+
s=checkstr(L,base,nargs,0,fn); int start=0,end=s->len;
|
|
370
|
+
if(strcmp(fn,"trimRight")) while(start<end&&isspace((unsigned char)s->s[start])) start++;
|
|
371
|
+
if(strcmp(fn,"trimLeft")) while(end>start&&isspace((unsigned char)s->s[end-1])) end--;
|
|
372
|
+
RET(0,strv(s->s+start,end-start)); return 1;
|
|
373
|
+
}
|
|
374
|
+
if(!strcmp(fn,"replaceText")){
|
|
375
|
+
s=checkstr(L,base,nargs,0,fn); a=checkstr(L,base,nargs,1,fn); b=checkstr(L,base,nargs,2,fn);
|
|
376
|
+
if(a->len==0) luc_error("string.replaceText: search text must not be empty");
|
|
377
|
+
cap=(size_t)s->len+1; len=0; buf=(char*)malloc(cap); if(!buf) luc_error("string.replaceText: out of memory");
|
|
378
|
+
for(int i=0;i<s->len;){
|
|
379
|
+
if(i+a->len<=s->len&&!memcmp(s->s+i,a->s,(size_t)a->len)){
|
|
380
|
+
size_t need=len+(size_t)b->len+1;
|
|
381
|
+
if(need>cap){while(need>cap)cap*=2;buf=(char*)realloc(buf,cap);if(!buf)luc_error("string.replaceText: out of memory");}
|
|
382
|
+
memcpy(buf+len,b->s,(size_t)b->len);len+=(size_t)b->len;i+=a->len;
|
|
383
|
+
}else{
|
|
384
|
+
if(len+2>cap){cap*=2;buf=(char*)realloc(buf,cap);if(!buf)luc_error("string.replaceText: out of memory");}
|
|
385
|
+
buf[len++]=s->s[i++];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
Value v=strv(buf,(int)len);free(buf);RET(0,v);return 1;
|
|
389
|
+
}
|
|
390
|
+
if(!strcmp(fn,"findText")){
|
|
391
|
+
s=checkstr(L,base,nargs,0,fn); a=checkstr(L,base,nargs,1,fn);
|
|
392
|
+
if(a->len==0){RET(0,mknum(1));return 1;}
|
|
393
|
+
for(int i=0;i+a->len<=s->len;i++) if(!memcmp(s->s+i,a->s,(size_t)a->len)){RET(0,mknum(i+1));return 1;}
|
|
394
|
+
RET(0,NIL);return 1;
|
|
395
|
+
}
|
|
396
|
+
if(!strcmp(fn,"countText")){
|
|
397
|
+
s=checkstr(L,base,nargs,0,fn); a=checkstr(L,base,nargs,1,fn);
|
|
398
|
+
if(a->len==0) luc_error("string.countText: search text must not be empty");
|
|
399
|
+
int count=0; for(int i=0;i+a->len<=s->len;) if(!memcmp(s->s+i,a->s,(size_t)a->len)){count++;i+=a->len;}else i++;
|
|
400
|
+
RET(0,mknum(count));return 1;
|
|
401
|
+
}
|
|
402
|
+
if(!strcmp(fn,"startsWith")||!strcmp(fn,"endsWith")){
|
|
403
|
+
s=checkstr(L,base,nargs,0,fn); a=checkstr(L,base,nargs,1,fn);
|
|
404
|
+
RET(0,mkbool(!strcmp(fn,"startsWith")?nums_prefix(s,a):nums_suffix(s,a)));return 1;
|
|
405
|
+
}
|
|
406
|
+
if(!strcmp(fn,"splitText")){
|
|
407
|
+
s=checkstr(L,base,nargs,0,fn); a=checkstr(L,base,nargs,1,fn);
|
|
408
|
+
if(a->len==0) luc_error("string.splitText: separator must not be empty");
|
|
409
|
+
Table *out=tab_new(1); int at=0;
|
|
410
|
+
for(int i=0;i+a->len<=s->len;){
|
|
411
|
+
if(!memcmp(s->s+i,a->s,(size_t)a->len)){list_push(out,strv(s->s+at,i-at));i+=a->len;at=i;}
|
|
412
|
+
else i++;
|
|
413
|
+
}
|
|
414
|
+
list_push(out,strv(s->s+at,s->len-at));RET(0,mkobj(LT_LIST,out));return 1;
|
|
415
|
+
}
|
|
416
|
+
if(!strcmp(fn,"isLetters")||!strcmp(fn,"isDigits")||!strcmp(fn,"isAlphanumeric")){
|
|
417
|
+
s=checkstr(L,base,nargs,0,fn); int ok=s->len>0;
|
|
418
|
+
for(int i=0;i<s->len;i++){
|
|
419
|
+
unsigned char c=(unsigned char)s->s[i];
|
|
420
|
+
if(!strcmp(fn,"isLetters")) ok=ok&&isalpha(c);
|
|
421
|
+
else if(!strcmp(fn,"isDigits")) ok=ok&&isdigit(c);
|
|
422
|
+
else ok=ok&&isalnum(c);
|
|
423
|
+
}
|
|
424
|
+
RET(0,mkbool(ok));return 1;
|
|
425
|
+
}
|
|
426
|
+
if(!strcmp(fn,"zeroPad")){
|
|
427
|
+
s=checkstr(L,base,nargs,0,fn); int width=checkint(L,base,nargs,1,fn);
|
|
428
|
+
if(width<0||width>10000000) luc_error("string.zeroPad: width out of range");
|
|
429
|
+
if(width<=s->len){RET(0,AR(0));return 1;}
|
|
430
|
+
buf=(char*)malloc((size_t)width);if(!buf)luc_error("string.zeroPad: out of memory");
|
|
431
|
+
memset(buf,'0',(size_t)(width-s->len));memcpy(buf+width-s->len,s->s,(size_t)s->len);
|
|
432
|
+
Value v=strv(buf,width);free(buf);RET(0,v);return 1;
|
|
433
|
+
}
|
|
434
|
+
if(!strcmp(fn,"encodeText")||!strcmp(fn,"decodeText")){
|
|
435
|
+
s=checkstr(L,base,nargs,0,fn);
|
|
436
|
+
static const char hex[]="0123456789abcdef";
|
|
437
|
+
if(!strcmp(fn,"encodeText")){
|
|
438
|
+
buf=(char*)malloc((size_t)s->len*2+1);if(!buf)luc_error("string.encodeText: out of memory");
|
|
439
|
+
for(int i=0;i<s->len;i++){unsigned char c=(unsigned char)s->s[i];buf[2*i]=hex[c>>4];buf[2*i+1]=hex[c&15];}
|
|
440
|
+
Value v=strv(buf,s->len*2);free(buf);RET(0,v);return 1;
|
|
441
|
+
}
|
|
442
|
+
if(s->len%2) luc_error("string.decodeText: hex string must have even length");
|
|
443
|
+
buf=(char*)malloc((size_t)s->len/2+1);if(!buf)luc_error("string.decodeText: out of memory");
|
|
444
|
+
for(int i=0;i<s->len;i+=2){int hi=hexval((unsigned char)s->s[i]),lo=hexval((unsigned char)s->s[i+1]);
|
|
445
|
+
if(hi<0||lo<0){free(buf);luc_error("string.decodeText: invalid hex string");return 0;}
|
|
446
|
+
buf[i/2]=(char)((hi<<4)|lo);}
|
|
447
|
+
Value v=strv(buf,s->len/2);free(buf);RET(0,v);return 1;
|
|
448
|
+
}
|
|
449
|
+
luc_error("string.%s: not implemented",fn);return 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
static int nums_timestamp(Value v,time_t *out){
|
|
453
|
+
if(v.t==LT_TABLE) v=tab_get(AS_TAB(v),cstrv("timestamp"));
|
|
454
|
+
if(v.t!=LT_NUM||!isfinite(v.u.n)) return 0;
|
|
455
|
+
if(v.u.n<-9223372036854775808.0||v.u.n>=9223372036854775808.0) return 0;
|
|
456
|
+
*out=(time_t)(int64_t)v.u.n;
|
|
457
|
+
return (double)*out==v.u.n;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
static Table *nums_datetime_table(time_t stamp){
|
|
461
|
+
struct tm tmv;
|
|
462
|
+
#if defined(_WIN32)
|
|
463
|
+
if(gmtime_s(&tmv,&stamp)!=0) return NULL;
|
|
464
|
+
#else
|
|
465
|
+
if(!gmtime_r(&stamp,&tmv)) return NULL;
|
|
466
|
+
#endif
|
|
467
|
+
Table *d=tab_new(0);
|
|
468
|
+
tab_set(d,cstrv("timestamp"),mknum((double)stamp));
|
|
469
|
+
tab_set(d,cstrv("year"),mknum(tmv.tm_year+1900));
|
|
470
|
+
tab_set(d,cstrv("month"),mknum(tmv.tm_mon+1));
|
|
471
|
+
tab_set(d,cstrv("day"),mknum(tmv.tm_mday));
|
|
472
|
+
tab_set(d,cstrv("hour"),mknum(tmv.tm_hour));
|
|
473
|
+
tab_set(d,cstrv("minute"),mknum(tmv.tm_min));
|
|
474
|
+
tab_set(d,cstrv("second"),mknum(tmv.tm_sec));
|
|
475
|
+
tab_set(d,cstrv("weekday"),mknum(tmv.tm_wday));
|
|
476
|
+
return d;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
LFN(f_nums_time_api){
|
|
480
|
+
const char *fn=self->name; time_t stamp;
|
|
481
|
+
if(!strcmp(fn,"dateTime")){
|
|
482
|
+
if(nargs==0) stamp=time(NULL);
|
|
483
|
+
else if(!nums_timestamp(AR(0),&stamp)){RET(0,NIL);return 1;}
|
|
484
|
+
Table *d=nums_datetime_table(stamp);
|
|
485
|
+
if(!d){RET(0,NIL);return 1;}
|
|
486
|
+
RET(0,mkobj(LT_TABLE,d));return 1;
|
|
487
|
+
}
|
|
488
|
+
if(!strcmp(fn,"timeSpan")){
|
|
489
|
+
double start=checknum(L,base,nargs,0,fn),end=nargs>=2?checknum(L,base,nargs,1,fn):start;
|
|
490
|
+
RET(0,mknum(end-start));return 1;
|
|
491
|
+
}
|
|
492
|
+
if(!strcmp(fn,"isInvalidDate")){
|
|
493
|
+
time_t t; RET(0,mkbool(!nums_timestamp(AR(0),&t)||nums_datetime_table(t)==NULL));return 1;
|
|
494
|
+
}
|
|
495
|
+
if(!strcmp(fn,"dateString")){
|
|
496
|
+
if(!nums_timestamp(AR(0),&stamp)){RET(0,NIL);return 1;}
|
|
497
|
+
struct tm tmv;
|
|
498
|
+
#if defined(_WIN32)
|
|
499
|
+
if(gmtime_s(&tmv,&stamp)!=0){RET(0,NIL);return 1;}
|
|
500
|
+
#else
|
|
501
|
+
if(!gmtime_r(&stamp,&tmv)){RET(0,NIL);return 1;}
|
|
502
|
+
#endif
|
|
503
|
+
Str *fmt=nargs>=2?checkstr(L,base,nargs,1,fn):str_fromc("%Y-%m-%d %H:%M:%S");
|
|
504
|
+
char buf[512]; size_t n=strftime(buf,sizeof buf,fmt->s,&tmv);
|
|
505
|
+
if(!n){RET(0,NIL);return 1;}
|
|
506
|
+
RET(0,strv(buf,(int)n));return 1;
|
|
507
|
+
}
|
|
508
|
+
if(!strcmp(fn,"isWorkDay")){
|
|
509
|
+
if(!nums_timestamp(AR(0),&stamp)){RET(0,mkbool(0));return 1;}
|
|
510
|
+
struct tm tmv;
|
|
511
|
+
#if defined(_WIN32)
|
|
512
|
+
if(gmtime_s(&tmv,&stamp)!=0){RET(0,mkbool(0));return 1;}
|
|
513
|
+
#else
|
|
514
|
+
if(!gmtime_r(&stamp,&tmv)){RET(0,mkbool(0));return 1;}
|
|
515
|
+
#endif
|
|
516
|
+
RET(0,mkbool(tmv.tm_wday!=0&&tmv.tm_wday!=6));return 1;
|
|
517
|
+
}
|
|
518
|
+
if(!strcmp(fn,"workDaysBetween")){
|
|
519
|
+
time_t a,b;
|
|
520
|
+
if(!nums_timestamp(AR(0),&a)||!nums_timestamp(AR(1),&b))
|
|
521
|
+
luc_error("time.workDaysBetween: expected valid Unix timestamps");
|
|
522
|
+
int64_t start_day=(int64_t)floorl((long double)a/86400.0L);
|
|
523
|
+
int64_t end_day=(int64_t)floorl((long double)b/86400.0L);
|
|
524
|
+
if(fabsl((long double)end_day-(long double)start_day)>1000000.0L)
|
|
525
|
+
luc_error("time.workDaysBetween: range is too large");
|
|
526
|
+
int count=0;
|
|
527
|
+
if(start_day<end_day){
|
|
528
|
+
for(int64_t day=start_day+1;day<=end_day;day++){
|
|
529
|
+
int weekday=(int)((day+4)%7); if(weekday<0) weekday+=7;
|
|
530
|
+
if(weekday!=0&&weekday!=6) count++;
|
|
531
|
+
}
|
|
532
|
+
}else{
|
|
533
|
+
for(int64_t day=start_day;day>end_day;day--){
|
|
534
|
+
int weekday=(int)((day+4)%7); if(weekday<0) weekday+=7;
|
|
535
|
+
if(weekday!=0&&weekday!=6) count--;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
RET(0,mknum((double)count));return 1;
|
|
539
|
+
}
|
|
540
|
+
if(!strcmp(fn,"workDayOffset")){
|
|
541
|
+
if(!nums_timestamp(AR(0),&stamp)) luc_error("time.workDayOffset: expected a valid Unix timestamp");
|
|
542
|
+
int days=checkint(L,base,nargs,1,fn),dir=days<0?-1:1;
|
|
543
|
+
if(days==INT_MIN) luc_error("time.workDayOffset: offset is out of range");
|
|
544
|
+
if(days>1000000||days< -1000000) luc_error("time.workDayOffset: offset is too large");
|
|
545
|
+
int left=days<0?-days:days; int64_t x=(int64_t)stamp;
|
|
546
|
+
while(left>0){
|
|
547
|
+
x+=(int64_t)dir*86400; time_t day=(time_t)x; struct tm tmv;
|
|
548
|
+
#if defined(_WIN32)
|
|
549
|
+
if(gmtime_s(&tmv,&day)!=0) luc_error("time.workDayOffset: timestamp out of range");
|
|
550
|
+
#else
|
|
551
|
+
if(!gmtime_r(&day,&tmv)) luc_error("time.workDayOffset: timestamp out of range");
|
|
552
|
+
#endif
|
|
553
|
+
if(tmv.tm_wday!=0&&tmv.tm_wday!=6) left--;
|
|
554
|
+
}
|
|
555
|
+
RET(0,mknum((double)x));return 1;
|
|
556
|
+
}
|
|
557
|
+
if(!strcmp(fn,"workCalendar")){
|
|
558
|
+
time_t a,b;
|
|
559
|
+
if(!nums_timestamp(AR(0),&a)||!nums_timestamp(AR(1),&b))
|
|
560
|
+
luc_error("time.workCalendar: expected valid Unix timestamps");
|
|
561
|
+
int64_t day_a=(int64_t)floorl((long double)a/86400.0L);
|
|
562
|
+
int64_t day_b=(int64_t)floorl((long double)b/86400.0L);
|
|
563
|
+
int64_t lo=day_a<day_b?day_a:day_b,hi=day_a<day_b?day_b:day_a;
|
|
564
|
+
if((long double)hi-(long double)lo>1000000.0L)
|
|
565
|
+
luc_error("time.workCalendar: requested range is too large");
|
|
566
|
+
Table *days=tab_new(1);
|
|
567
|
+
for(int64_t day=lo;;day++){
|
|
568
|
+
int weekday=(int)((day+4)%7); if(weekday<0) weekday+=7;
|
|
569
|
+
Table *item=tab_new(0);
|
|
570
|
+
tab_set(item,cstrv("timestamp"),mknum((double)day*86400.0));
|
|
571
|
+
tab_set(item,cstrv("workday"),mkbool(weekday!=0&&weekday!=6));
|
|
572
|
+
list_push(days,mkobj(LT_TABLE,item));
|
|
573
|
+
if(day>=hi) break;
|
|
574
|
+
}
|
|
575
|
+
RET(0,mkobj(LT_LIST,days));return 1;
|
|
576
|
+
}
|
|
577
|
+
luc_error("time.%s: not implemented",fn);return 0;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
typedef struct { int rows,cols; double *v; } NumsMatrix;
|
|
581
|
+
|
|
582
|
+
static void nums_matrix_free(NumsMatrix *m){free(m->v);m->v=NULL;m->rows=m->cols=0;}
|
|
583
|
+
|
|
584
|
+
static int nums_matrix_read(LucState *L,int base,int nargs,int arg,const char *fn,NumsMatrix *m){
|
|
585
|
+
Value value=AR(arg);
|
|
586
|
+
if(value.t!=LT_LIST){luc_error("matrix.%s: expected a nested list",fn);return 0;}
|
|
587
|
+
int shape[16]={0},rank=nums_shape_value(value,shape,0);
|
|
588
|
+
if(rank!=2){luc_error("matrix.%s: expected a 2D matrix",fn);return 0;}
|
|
589
|
+
if(shape[0]>1000000||shape[1]>1000000||
|
|
590
|
+
(shape[0]&&shape[1]>10000000/shape[0])){
|
|
591
|
+
luc_error("matrix.%s: matrix is too large",fn);return 0;
|
|
592
|
+
}
|
|
593
|
+
Table *flat=tab_new(1);nums_flatten_into(AS_TAB(value),flat,0);
|
|
594
|
+
m->rows=shape[0];m->cols=shape[1];
|
|
595
|
+
m->v=(double*)malloc((size_t)(flat->alen?flat->alen:1)*sizeof(double));
|
|
596
|
+
if(!m->v){luc_error("matrix.%s: out of memory",fn);return 0;}
|
|
597
|
+
for(int i=0;i<flat->alen;i++){
|
|
598
|
+
if(flat->arr[i].t!=LT_NUM||!isfinite(flat->arr[i].u.n)){
|
|
599
|
+
nums_matrix_free(m);luc_error("matrix.%s: entries must be finite numbers",fn);return 0;
|
|
600
|
+
}
|
|
601
|
+
m->v[i]=flat->arr[i].u.n;
|
|
602
|
+
}
|
|
603
|
+
return 1;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
static Table *nums_matrix_list(const double *v,int rows,int cols){
|
|
607
|
+
Table *out=tab_new(1);
|
|
608
|
+
for(int i=0;i<rows;i++){
|
|
609
|
+
Table *row=tab_new(1);
|
|
610
|
+
for(int j=0;j<cols;j++) list_push(row,mknum(v[i*cols+j]));
|
|
611
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
612
|
+
}
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
static int nums_vector_read(LucState *L,int base,int nargs,int arg,const char *fn,double **out,int *n){
|
|
617
|
+
Value value=AR(arg);
|
|
618
|
+
if(value.t!=LT_LIST){luc_error("matrix.%s: expected a vector list",fn);return 0;}
|
|
619
|
+
Table *t=AS_TAB(value);
|
|
620
|
+
if(t->alen>10000000){luc_error("matrix.%s: vector is too large",fn);return 0;}
|
|
621
|
+
double *v=(double*)malloc((size_t)(t->alen?t->alen:1)*sizeof(double));
|
|
622
|
+
if(!v){luc_error("matrix.%s: out of memory",fn);return 0;}
|
|
623
|
+
for(int i=0;i<t->alen;i++){
|
|
624
|
+
if(t->arr[i].t!=LT_NUM||!isfinite(t->arr[i].u.n)){
|
|
625
|
+
free(v);luc_error("matrix.%s: vector entries must be finite numbers",fn);return 0;
|
|
626
|
+
}
|
|
627
|
+
v[i]=t->arr[i].u.n;
|
|
628
|
+
}
|
|
629
|
+
*out=v;*n=t->alen;return 1;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
static void nums_matmul(const double *a,const double *b,double *out,int m,int k,int n){
|
|
633
|
+
for(int i=0;i<m;i++) for(int j=0;j<n;j++){
|
|
634
|
+
double sum=0;
|
|
635
|
+
for(int x=0;x<k;x++) sum+=a[i*k+x]*b[x*n+j];
|
|
636
|
+
out[i*n+j]=sum;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
static int nums_matrix_inverse(const double *a,double *out,int n){
|
|
641
|
+
if(n==0) return 0;
|
|
642
|
+
double scale=0;
|
|
643
|
+
for(int i=0;i<n*n;i++)if(fabs(a[i])>scale)scale=fabs(a[i]);
|
|
644
|
+
double tolerance=scale*(2.2204460492503131e-16)*(double)n*16.0;
|
|
645
|
+
double *aug=(double*)calloc((size_t)n*(size_t)(2*n),sizeof(double));
|
|
646
|
+
if(!aug) return 0;
|
|
647
|
+
int width=2*n;
|
|
648
|
+
for(int i=0;i<n;i++) for(int j=0;j<n;j++){
|
|
649
|
+
aug[i*width+j]=a[i*n+j];aug[i*width+n+j]=(i==j?1.0:0.0);
|
|
650
|
+
}
|
|
651
|
+
for(int c=0;c<n;c++){
|
|
652
|
+
int pivot=c;
|
|
653
|
+
for(int r=c+1;r<n;r++) if(fabs(aug[r*width+c])>fabs(aug[pivot*width+c]))pivot=r;
|
|
654
|
+
if(fabs(aug[pivot*width+c])<=tolerance){free(aug);return 0;}
|
|
655
|
+
if(pivot!=c) for(int j=0;j<width;j++){
|
|
656
|
+
double tmp=aug[c*width+j];aug[c*width+j]=aug[pivot*width+j];aug[pivot*width+j]=tmp;
|
|
657
|
+
}
|
|
658
|
+
double p=aug[c*width+c];
|
|
659
|
+
for(int j=0;j<width;j++)aug[c*width+j]/=p;
|
|
660
|
+
for(int r=0;r<n;r++) if(r!=c){
|
|
661
|
+
double factor=aug[r*width+c];
|
|
662
|
+
for(int j=0;j<width;j++)aug[r*width+j]-=factor*aug[c*width+j];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
for(int i=0;i<n;i++)for(int j=0;j<n;j++)out[i*n+j]=aug[i*width+n+j];
|
|
666
|
+
free(aug);return 1;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
static int nums_symmetric_eigen(const double *a,int n,double *eval,double *evec){
|
|
670
|
+
double *d=(double*)malloc((size_t)n*(size_t)n*sizeof(double));
|
|
671
|
+
if(!d)return 0;
|
|
672
|
+
memcpy(d,a,(size_t)n*(size_t)n*sizeof(double));
|
|
673
|
+
memset(evec,0,(size_t)n*(size_t)n*sizeof(double));
|
|
674
|
+
double scale=0;
|
|
675
|
+
for(int i=0;i<n*n;i++)if(fabs(d[i])>scale)scale=fabs(d[i]);
|
|
676
|
+
double tolerance=scale*1e-13;
|
|
677
|
+
for(int i=0;i<n;i++)evec[i*n+i]=1.0;
|
|
678
|
+
for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)
|
|
679
|
+
if(fabs(a[i*n+j]-a[j*n+i])>1e-10*(1.0+fabs(a[i*n+j])+fabs(a[j*n+i]))){free(d);return -1;}
|
|
680
|
+
int converged=0;
|
|
681
|
+
for(int iter=0;iter<100*n*n+1;iter++){
|
|
682
|
+
int p=0,q=0;double largest=0;
|
|
683
|
+
for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)
|
|
684
|
+
if(fabs(d[i*n+j])>largest){largest=fabs(d[i*n+j]);p=i;q=j;}
|
|
685
|
+
if(largest<=tolerance){converged=1;break;}
|
|
686
|
+
double app=d[p*n+p],aqq=d[q*n+q],apq=d[p*n+q];
|
|
687
|
+
double tau=(aqq-app)/(2.0*apq);
|
|
688
|
+
double t=(tau>=0?1.0:-1.0)/(fabs(tau)+sqrt(1.0+tau*tau));
|
|
689
|
+
double c=1.0/sqrt(1.0+t*t),s=t*c;
|
|
690
|
+
for(int k=0;k<n;k++)if(k!=p&&k!=q){
|
|
691
|
+
double dkp=d[k*n+p],dkq=d[k*n+q];
|
|
692
|
+
d[k*n+p]=d[p*n+k]=c*dkp-s*dkq;
|
|
693
|
+
d[k*n+q]=d[q*n+k]=s*dkp+c*dkq;
|
|
694
|
+
}
|
|
695
|
+
d[p*n+p]=app-t*apq;d[q*n+q]=aqq+t*apq;
|
|
696
|
+
d[p*n+q]=d[q*n+p]=0.0;
|
|
697
|
+
for(int k=0;k<n;k++){
|
|
698
|
+
double vkp=evec[k*n+p],vkq=evec[k*n+q];
|
|
699
|
+
evec[k*n+p]=c*vkp-s*vkq;evec[k*n+q]=s*vkp+c*vkq;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if(!converged){free(d);return 0;}
|
|
703
|
+
for(int i=0;i<n;i++)eval[i]=d[i*n+i];
|
|
704
|
+
for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)if(eval[j]>eval[i]){
|
|
705
|
+
double x=eval[i];eval[i]=eval[j];eval[j]=x;
|
|
706
|
+
for(int r=0;r<n;r++){x=evec[r*n+i];evec[r*n+i]=evec[r*n+j];evec[r*n+j]=x;}
|
|
707
|
+
}
|
|
708
|
+
free(d);return 1;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
LFN(f_nums_matrix_api){
|
|
712
|
+
const char *fn=self->name;
|
|
713
|
+
NumsMatrix a={0,0,NULL},b={0,0,NULL};
|
|
714
|
+
double *x=NULL,*y=NULL,*out=NULL;
|
|
715
|
+
int n=0,m=0,k=0;
|
|
716
|
+
if(!strcmp(fn,"dot")){
|
|
717
|
+
if(!nums_vector_read(L,base,nargs,0,fn,&x,&n)||
|
|
718
|
+
!nums_vector_read(L,base,nargs,1,fn,&y,&m)){free(x);free(y);return 0;}
|
|
719
|
+
if(n!=m){free(x);free(y);luc_error("matrix.dot: vector lengths must match");return 0;}
|
|
720
|
+
double result=0;for(int i=0;i<n;i++)result+=x[i]*y[i];
|
|
721
|
+
free(x);free(y);RET(0,mknum(result));return 1;
|
|
722
|
+
}
|
|
723
|
+
if(!strcmp(fn,"cross")){
|
|
724
|
+
if(!nums_vector_read(L,base,nargs,0,fn,&x,&n)||
|
|
725
|
+
!nums_vector_read(L,base,nargs,1,fn,&y,&m)){free(x);free(y);return 0;}
|
|
726
|
+
if(n!=3||m!=3){free(x);free(y);luc_error("matrix.cross: both vectors must have length 3");return 0;}
|
|
727
|
+
out=(double*)malloc(3*sizeof(double));if(!out){free(x);free(y);luc_error("matrix.cross: out of memory");return 0;}
|
|
728
|
+
out[0]=x[1]*y[2]-x[2]*y[1];out[1]=x[2]*y[0]-x[0]*y[2];out[2]=x[0]*y[1]-x[1]*y[0];
|
|
729
|
+
Table *v=tab_new(1);for(int i=0;i<3;i++)list_push(v,mknum(out[i]));
|
|
730
|
+
free(x);free(y);free(out);RET(0,mkobj(LT_LIST,v));return 1;
|
|
731
|
+
}
|
|
732
|
+
if(!strcmp(fn,"matMul")){
|
|
733
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a)||!nums_matrix_read(L,base,nargs,1,fn,&b)){
|
|
734
|
+
nums_matrix_free(&a);nums_matrix_free(&b);return 0;
|
|
735
|
+
}
|
|
736
|
+
if(a.cols!=b.rows){nums_matrix_free(&a);nums_matrix_free(&b);luc_error("matrix.matMul: inner dimensions must match");return 0;}
|
|
737
|
+
if(a.rows&&b.cols>10000000/a.rows){nums_matrix_free(&a);nums_matrix_free(&b);luc_error("matrix.matMul: result is too large");return 0;}
|
|
738
|
+
out=(double*)calloc((size_t)(a.rows?a.rows:1)*(size_t)(b.cols?b.cols:1),sizeof(double));
|
|
739
|
+
if(!out){nums_matrix_free(&a);nums_matrix_free(&b);luc_error("matrix.matMul: out of memory");return 0;}
|
|
740
|
+
nums_matmul(a.v,b.v,out,a.rows,a.cols,b.cols);
|
|
741
|
+
Table *result=nums_matrix_list(out,a.rows,b.cols);
|
|
742
|
+
nums_matrix_free(&a);nums_matrix_free(&b);free(out);RET(0,mkobj(LT_LIST,result));return 1;
|
|
743
|
+
}
|
|
744
|
+
if(!strcmp(fn,"transpose")){
|
|
745
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a))return 0;
|
|
746
|
+
out=(double*)malloc((size_t)(a.rows*a.cols?a.rows*a.cols:1)*sizeof(double));
|
|
747
|
+
if(!out){nums_matrix_free(&a);luc_error("matrix.transpose: out of memory");return 0;}
|
|
748
|
+
for(int i=0;i<a.rows;i++)for(int j=0;j<a.cols;j++)out[j*a.rows+i]=a.v[i*a.cols+j];
|
|
749
|
+
Table *result=nums_matrix_list(out,a.cols,a.rows);
|
|
750
|
+
nums_matrix_free(&a);free(out);RET(0,mkobj(LT_LIST,result));return 1;
|
|
751
|
+
}
|
|
752
|
+
if(!strcmp(fn,"norm")){
|
|
753
|
+
Value v=AR(0);Table *flat=tab_new(1);
|
|
754
|
+
if(v.t!=LT_LIST){luc_error("matrix.norm: expected a vector or matrix list");return 0;}
|
|
755
|
+
nums_flatten_into(AS_TAB(v),flat,0);double sum=0;
|
|
756
|
+
for(int i=0;i<flat->alen;i++){
|
|
757
|
+
if(flat->arr[i].t!=LT_NUM||!isfinite(flat->arr[i].u.n)){luc_error("matrix.norm: entries must be finite numbers");return 0;}
|
|
758
|
+
sum+=flat->arr[i].u.n*flat->arr[i].u.n;
|
|
759
|
+
}
|
|
760
|
+
RET(0,mknum(sqrt(sum)));return 1;
|
|
761
|
+
}
|
|
762
|
+
if(!strcmp(fn,"det")||!strcmp(fn,"trace")||!strcmp(fn,"inverse")||
|
|
763
|
+
!strcmp(fn,"matrixPower")||!strcmp(fn,"eigen")||!strcmp(fn,"cholesky")){
|
|
764
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a))return 0;
|
|
765
|
+
if(a.rows!=a.cols){nums_matrix_free(&a);luc_error("matrix.%s: matrix must be square",fn);return 0;}
|
|
766
|
+
n=a.rows;
|
|
767
|
+
if(!strcmp(fn,"trace")){
|
|
768
|
+
double tr=0;for(int i=0;i<n;i++)tr+=a.v[i*n+i];
|
|
769
|
+
nums_matrix_free(&a);RET(0,mknum(tr));return 1;
|
|
770
|
+
}
|
|
771
|
+
if(!strcmp(fn,"det")){
|
|
772
|
+
out=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
773
|
+
if(!out){nums_matrix_free(&a);luc_error("matrix.det: out of memory");return 0;}
|
|
774
|
+
memcpy(out,a.v,(size_t)n*(size_t)n*sizeof(double));
|
|
775
|
+
double scale=0;for(int i=0;i<n*n;i++)if(fabs(out[i])>scale)scale=fabs(out[i]);
|
|
776
|
+
double tolerance=scale*(2.2204460492503131e-16)*(double)n*16.0;
|
|
777
|
+
double d=1;int sign=1;
|
|
778
|
+
for(int c=0;c<n;c++){
|
|
779
|
+
int p=c;for(int r=c+1;r<n;r++)if(fabs(out[r*n+c])>fabs(out[p*n+c]))p=r;
|
|
780
|
+
if(fabs(out[p*n+c])<=tolerance){d=0;break;}
|
|
781
|
+
if(p!=c){for(int j=c;j<n;j++){double z=out[c*n+j];out[c*n+j]=out[p*n+j];out[p*n+j]=z;}sign=-sign;}
|
|
782
|
+
double pivot=out[c*n+c];d*=pivot;
|
|
783
|
+
for(int r=c+1;r<n;r++){double f=out[r*n+c]/pivot;for(int j=c+1;j<n;j++)out[r*n+j]-=f*out[c*n+j];}
|
|
784
|
+
}
|
|
785
|
+
free(out);nums_matrix_free(&a);RET(0,mknum(d*sign));return 1;
|
|
786
|
+
}
|
|
787
|
+
if(!strcmp(fn,"inverse")){
|
|
788
|
+
out=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
789
|
+
if(!out){nums_matrix_free(&a);luc_error("matrix.inverse: out of memory");return 0;}
|
|
790
|
+
if(!nums_matrix_inverse(a.v,out,n)){free(out);nums_matrix_free(&a);luc_error("matrix.inverse: matrix is singular");return 0;}
|
|
791
|
+
Table *result=nums_matrix_list(out,n,n);free(out);nums_matrix_free(&a);
|
|
792
|
+
RET(0,mkobj(LT_LIST,result));return 1;
|
|
793
|
+
}
|
|
794
|
+
if(!strcmp(fn,"matrixPower")){
|
|
795
|
+
int exponent=checkint(L,base,nargs,1,fn);
|
|
796
|
+
out=(double*)calloc((size_t)(n*n?n*n:1),sizeof(double));
|
|
797
|
+
double *basev=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
798
|
+
double *tmp=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
799
|
+
if(!out||!basev||!tmp){free(out);free(basev);free(tmp);nums_matrix_free(&a);luc_error("matrix.matrixPower: out of memory");return 0;}
|
|
800
|
+
for(int i=0;i<n;i++)out[i*n+i]=1;
|
|
801
|
+
if(exponent<0){
|
|
802
|
+
if(!nums_matrix_inverse(a.v,basev,n)){free(out);free(basev);free(tmp);nums_matrix_free(&a);luc_error("matrix.matrixPower: matrix is singular");return 0;}
|
|
803
|
+
if(exponent==INT_MIN){free(out);free(basev);free(tmp);nums_matrix_free(&a);luc_error("matrix.matrixPower: exponent out of range");return 0;}
|
|
804
|
+
exponent=-exponent;
|
|
805
|
+
}else memcpy(basev,a.v,(size_t)n*(size_t)n*sizeof(double));
|
|
806
|
+
while(exponent){
|
|
807
|
+
if(exponent&1){nums_matmul(out,basev,tmp,n,n,n);memcpy(out,tmp,(size_t)n*(size_t)n*sizeof(double));}
|
|
808
|
+
exponent>>=1;
|
|
809
|
+
if(exponent){nums_matmul(basev,basev,tmp,n,n,n);memcpy(basev,tmp,(size_t)n*(size_t)n*sizeof(double));}
|
|
810
|
+
}
|
|
811
|
+
Table *result=nums_matrix_list(out,n,n);free(out);free(basev);free(tmp);nums_matrix_free(&a);
|
|
812
|
+
RET(0,mkobj(LT_LIST,result));return 1;
|
|
813
|
+
}
|
|
814
|
+
if(!strcmp(fn,"cholesky")){
|
|
815
|
+
for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)
|
|
816
|
+
if(fabs(a.v[i*n+j]-a.v[j*n+i])>1e-10*(1.0+fabs(a.v[i*n+j])+fabs(a.v[j*n+i]))){
|
|
817
|
+
nums_matrix_free(&a);luc_error("matrix.cholesky: matrix must be symmetric");return 0;
|
|
818
|
+
}
|
|
819
|
+
out=(double*)calloc((size_t)(n*n?n*n:1),sizeof(double));
|
|
820
|
+
if(!out){nums_matrix_free(&a);luc_error("matrix.cholesky: out of memory");return 0;}
|
|
821
|
+
for(int i=0;i<n;i++)for(int j=0;j<=i;j++){
|
|
822
|
+
double sum=a.v[i*n+j];
|
|
823
|
+
for(int k=0;k<j;k++)sum-=out[i*n+k]*out[j*n+k];
|
|
824
|
+
if(i==j){if(sum<=0){free(out);nums_matrix_free(&a);luc_error("matrix.cholesky: matrix must be positive definite");return 0;}out[i*n+j]=sqrt(sum);}
|
|
825
|
+
else out[i*n+j]=sum/out[j*n+j];
|
|
826
|
+
}
|
|
827
|
+
Table *result=nums_matrix_list(out,n,n);free(out);nums_matrix_free(&a);
|
|
828
|
+
RET(0,mkobj(LT_LIST,result));return 1;
|
|
829
|
+
}
|
|
830
|
+
if(!strcmp(fn,"eigen")){
|
|
831
|
+
if(n>32){nums_matrix_free(&a);luc_error("matrix.eigen: matrix size is limited to 32");return 0;}
|
|
832
|
+
double *eval=(double*)malloc((size_t)(n?n:1)*sizeof(double));
|
|
833
|
+
double *evec=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
834
|
+
if(!eval||!evec){free(eval);free(evec);nums_matrix_free(&a);luc_error("matrix.eigen: out of memory");return 0;}
|
|
835
|
+
int status=nums_symmetric_eigen(a.v,n,eval,evec);
|
|
836
|
+
if(status<=0){free(eval);free(evec);nums_matrix_free(&a);luc_error(status<0?"matrix.eigen: matrix must be symmetric":"matrix.eigen: solver did not converge");return 0;}
|
|
837
|
+
Table *values=tab_new(1);for(int i=0;i<n;i++)list_push(values,mknum(eval[i]));
|
|
838
|
+
Table *vectors=nums_matrix_list(evec,n,n);
|
|
839
|
+
free(eval);free(evec);nums_matrix_free(&a);
|
|
840
|
+
RET(0,mkobj(LT_LIST,values));RET(1,mkobj(LT_LIST,vectors));return 2;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if(!strcmp(fn,"solve")){
|
|
844
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a)||
|
|
845
|
+
!nums_vector_read(L,base,nargs,1,fn,&x,&n)){nums_matrix_free(&a);free(x);return 0;}
|
|
846
|
+
if(a.rows!=a.cols||a.rows!=n){nums_matrix_free(&a);free(x);luc_error("matrix.solve: square matrix and matching vector required");return 0;}
|
|
847
|
+
out=(double*)malloc((size_t)(n?n:1)*sizeof(double));
|
|
848
|
+
double *mat=(double*)malloc((size_t)(n*n?n*n:1)*sizeof(double));
|
|
849
|
+
if(!out||!mat){free(out);free(mat);free(x);nums_matrix_free(&a);luc_error("matrix.solve: out of memory");return 0;}
|
|
850
|
+
memcpy(mat,a.v,(size_t)n*(size_t)n*sizeof(double));memcpy(out,x,(size_t)n*sizeof(double));
|
|
851
|
+
double scale=0;for(int i=0;i<n*n;i++)if(fabs(mat[i])>scale)scale=fabs(mat[i]);
|
|
852
|
+
double tolerance=scale*(2.2204460492503131e-16)*(double)n*16.0;
|
|
853
|
+
for(int c=0;c<n;c++){
|
|
854
|
+
int p=c;for(int r=c+1;r<n;r++)if(fabs(mat[r*n+c])>fabs(mat[p*n+c]))p=r;
|
|
855
|
+
if(fabs(mat[p*n+c])<=tolerance){free(out);free(mat);free(x);nums_matrix_free(&a);luc_error("matrix.solve: matrix is singular");return 0;}
|
|
856
|
+
if(p!=c){for(int j=c;j<n;j++){double z=mat[c*n+j];mat[c*n+j]=mat[p*n+j];mat[p*n+j]=z;}double z=out[c];out[c]=out[p];out[p]=z;}
|
|
857
|
+
double pivot=mat[c*n+c];for(int j=c;j<n;j++)mat[c*n+j]/=pivot;out[c]/=pivot;
|
|
858
|
+
for(int r=0;r<n;r++)if(r!=c){double f=mat[r*n+c];for(int j=c;j<n;j++)mat[r*n+j]-=f*mat[c*n+j];out[r]-=f*out[c];}
|
|
859
|
+
}
|
|
860
|
+
Table *result=tab_new(1);for(int i=0;i<n;i++)list_push(result,mknum(out[i]));
|
|
861
|
+
free(out);free(mat);free(x);nums_matrix_free(&a);RET(0,mkobj(LT_LIST,result));return 1;
|
|
862
|
+
}
|
|
863
|
+
if(!strcmp(fn,"rank")){
|
|
864
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a))return 0;
|
|
865
|
+
double tol=nargs>=2?checknum(L,base,nargs,1,fn):1e-10;
|
|
866
|
+
if(!isfinite(tol)||tol<0){nums_matrix_free(&a);luc_error("matrix.rank: tolerance must be finite and >= 0");return 0;}
|
|
867
|
+
double *d=(double*)malloc((size_t)(a.rows*a.cols?a.rows*a.cols:1)*sizeof(double));
|
|
868
|
+
if(!d){nums_matrix_free(&a);luc_error("matrix.rank: out of memory");return 0;}
|
|
869
|
+
memcpy(d,a.v,(size_t)a.rows*(size_t)a.cols*sizeof(double));
|
|
870
|
+
if(nargs<2){double scale=0;for(int i=0;i<a.rows*a.cols;i++)if(fabs(d[i])>scale)scale=fabs(d[i]);tol*=scale;}
|
|
871
|
+
int rank=0,row=0;
|
|
872
|
+
for(int c=0;c<a.cols&&row<a.rows;c++){
|
|
873
|
+
int p=row;for(int r=row+1;r<a.rows;r++)if(fabs(d[r*a.cols+c])>fabs(d[p*a.cols+c]))p=r;
|
|
874
|
+
if(fabs(d[p*a.cols+c])<=tol)continue;
|
|
875
|
+
if(p!=row)for(int j=c;j<a.cols;j++){double z=d[row*a.cols+j];d[row*a.cols+j]=d[p*a.cols+j];d[p*a.cols+j]=z;}
|
|
876
|
+
double pivot=d[row*a.cols+c];
|
|
877
|
+
for(int r=row+1;r<a.rows;r++){double f=d[r*a.cols+c]/pivot;for(int j=c;j<a.cols;j++)d[r*a.cols+j]-=f*d[row*a.cols+j];}
|
|
878
|
+
row++;rank++;
|
|
879
|
+
}
|
|
880
|
+
free(d);nums_matrix_free(&a);RET(0,mknum(rank));return 1;
|
|
881
|
+
}
|
|
882
|
+
if(!strcmp(fn,"qr")){
|
|
883
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a))return 0;
|
|
884
|
+
int k=a.rows<a.cols?a.rows:a.cols;
|
|
885
|
+
double *q=(double*)calloc((size_t)(a.rows*k?a.rows*k:1),sizeof(double));
|
|
886
|
+
double *r=(double*)calloc((size_t)(k*a.cols?k*a.cols:1),sizeof(double));
|
|
887
|
+
double *col=(double*)malloc((size_t)(a.rows?a.rows:1)*sizeof(double));
|
|
888
|
+
if(!q||!r||!col){free(q);free(r);free(col);nums_matrix_free(&a);luc_error("matrix.qr: out of memory");return 0;}
|
|
889
|
+
for(int j=0;j<a.cols;j++){
|
|
890
|
+
for(int i=0;i<a.rows;i++)col[i]=a.v[i*a.cols+j];
|
|
891
|
+
int upto=j<k?j:k;
|
|
892
|
+
for(int c=0;c<upto;c++){
|
|
893
|
+
double dot=0;for(int i=0;i<a.rows;i++)dot+=q[i*k+c]*col[i];
|
|
894
|
+
r[c*a.cols+j]=dot;for(int i=0;i<a.rows;i++)col[i]-=dot*q[i*k+c];
|
|
895
|
+
}
|
|
896
|
+
if(j<k){
|
|
897
|
+
double norm=0;for(int i=0;i<a.rows;i++)norm+=col[i]*col[i];norm=sqrt(norm);
|
|
898
|
+
r[j*a.cols+j]=norm;
|
|
899
|
+
if(norm>1e-14)for(int i=0;i<a.rows;i++)q[i*k+j]=col[i]/norm;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
Table *qout=nums_matrix_list(q,a.rows,k),*rout=nums_matrix_list(r,k,a.cols);
|
|
903
|
+
free(q);free(r);free(col);nums_matrix_free(&a);
|
|
904
|
+
RET(0,mkobj(LT_LIST,qout));RET(1,mkobj(LT_LIST,rout));return 2;
|
|
905
|
+
}
|
|
906
|
+
if(!strcmp(fn,"svd")){
|
|
907
|
+
if(!nums_matrix_read(L,base,nargs,0,fn,&a))return 0;
|
|
908
|
+
int m=a.rows,cols=a.cols,k=m<cols?m:cols;
|
|
909
|
+
int nn=cols;
|
|
910
|
+
if(nn>32){nums_matrix_free(&a);luc_error("matrix.svd: column count is limited to 32");return 0;}
|
|
911
|
+
if(k==0){
|
|
912
|
+
Table *uout=nums_matrix_list(NULL,m,0),*sv=tab_new(1),*vto=nums_matrix_list(NULL,0,nn);
|
|
913
|
+
nums_matrix_free(&a);RET(0,mkobj(LT_LIST,uout));RET(1,mkobj(LT_LIST,sv));RET(2,mkobj(LT_LIST,vto));return 3;
|
|
914
|
+
}
|
|
915
|
+
double *ata=(double*)calloc((size_t)(nn*nn?nn*nn:1),sizeof(double));
|
|
916
|
+
double *eval=(double*)malloc((size_t)(nn?nn:1)*sizeof(double));
|
|
917
|
+
double *v=(double*)malloc((size_t)(nn*nn?nn*nn:1)*sizeof(double));
|
|
918
|
+
double *u=(double*)calloc((size_t)(m*k?m*k:1),sizeof(double));
|
|
919
|
+
double *vt=(double*)malloc((size_t)(k*nn?k*nn:1)*sizeof(double));
|
|
920
|
+
double *svals=(double*)malloc((size_t)(k?k:1)*sizeof(double));
|
|
921
|
+
if(!ata||!eval||!v||!u||!vt||!svals){free(ata);free(eval);free(v);free(u);free(vt);free(svals);nums_matrix_free(&a);luc_error("matrix.svd: out of memory");return 0;}
|
|
922
|
+
for(int i=0;i<nn;i++)for(int j=0;j<nn;j++)for(int r=0;r<m;r++)ata[i*nn+j]+=a.v[r*nn+i]*a.v[r*nn+j];
|
|
923
|
+
int status=nums_symmetric_eigen(ata,nn,eval,v);
|
|
924
|
+
if(status<=0){free(ata);free(eval);free(v);free(u);free(vt);free(svals);nums_matrix_free(&a);luc_error("matrix.svd: eigensolver did not converge");return 0;}
|
|
925
|
+
for(int c=0;c<k;c++){
|
|
926
|
+
svals[c]=sqrt(eval[c]>0?eval[c]:0);
|
|
927
|
+
for(int j=0;j<nn;j++)vt[c*nn+j]=v[j*nn+c];
|
|
928
|
+
if(svals[c]>svals[0]*1e-14)for(int i=0;i<m;i++){
|
|
929
|
+
double sum=0;for(int j=0;j<nn;j++)sum+=a.v[i*nn+j]*v[j*nn+c];
|
|
930
|
+
u[i*k+c]=sum/svals[c];
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
Table *uout=nums_matrix_list(u,m,k),*sv=tab_new(1),*vto=nums_matrix_list(vt,k,nn);
|
|
934
|
+
for(int i=0;i<k;i++)list_push(sv,mknum(svals[i]));
|
|
935
|
+
free(ata);free(eval);free(v);free(u);free(vt);free(svals);nums_matrix_free(&a);
|
|
936
|
+
RET(0,mkobj(LT_LIST,uout));RET(1,mkobj(LT_LIST,sv));RET(2,mkobj(LT_LIST,vto));return 3;
|
|
937
|
+
}
|
|
938
|
+
luc_error("matrix.%s: not implemented",fn);return 0;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
static int nums_list_has(Table *list,Value value){
|
|
942
|
+
for(int i=0;i<list->alen;i++)if(val_rawequal(list->arr[i],value))return 1;
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
LFN(f_nums_sets_api){
|
|
947
|
+
const char *fn=self->name;
|
|
948
|
+
Table *a=nums_list_arg(L,base,nargs,0,fn);
|
|
949
|
+
Table *b=NULL,*out=tab_new(1);
|
|
950
|
+
if(nargs>=2)b=nums_list_arg(L,base,nargs,1,fn);
|
|
951
|
+
if(!strcmp(fn,"union")){
|
|
952
|
+
if(!b){luc_error("sets.union: expected two lists");return 0;}
|
|
953
|
+
for(int i=0;i<a->alen;i++)if(!nums_list_has(out,a->arr[i]))list_push(out,a->arr[i]);
|
|
954
|
+
for(int i=0;i<b->alen;i++){
|
|
955
|
+
if(!nums_list_has(out,b->arr[i]))list_push(out,b->arr[i]);
|
|
956
|
+
}
|
|
957
|
+
RET(0,mkobj(LT_LIST,out));return 1;
|
|
958
|
+
}
|
|
959
|
+
if(!b){luc_error("sets.%s: expected two lists",fn);return 0;}
|
|
960
|
+
if(!strcmp(fn,"intersect")||!strcmp(fn,"difference")||!strcmp(fn,"symmetricDiff")){
|
|
961
|
+
for(int i=0;i<a->alen;i++){
|
|
962
|
+
int seen=0,already=0;
|
|
963
|
+
for(int j=0;j<b->alen;j++)if(val_rawequal(a->arr[i],b->arr[j])){seen=1;break;}
|
|
964
|
+
for(int j=0;j<out->alen;j++)if(val_rawequal(a->arr[i],out->arr[j])){already=1;break;}
|
|
965
|
+
if(!already&&((!strcmp(fn,"intersect")&&seen)||(!strcmp(fn,"difference")&&!seen)||
|
|
966
|
+
(!strcmp(fn,"symmetricDiff")&&!seen)))list_push(out,a->arr[i]);
|
|
967
|
+
}
|
|
968
|
+
if(!strcmp(fn,"symmetricDiff"))for(int i=0;i<b->alen;i++){
|
|
969
|
+
int seen=0,already=0;
|
|
970
|
+
for(int j=0;j<a->alen;j++)if(val_rawequal(b->arr[i],a->arr[j])){seen=1;break;}
|
|
971
|
+
for(int j=0;j<out->alen;j++)if(val_rawequal(b->arr[i],out->arr[j])){already=1;break;}
|
|
972
|
+
if(!seen&&!already)list_push(out,b->arr[i]);
|
|
973
|
+
}
|
|
974
|
+
RET(0,mkobj(LT_LIST,out));return 1;
|
|
975
|
+
}
|
|
976
|
+
int subset=1,disjoint=1;
|
|
977
|
+
for(int i=0;i<a->alen;i++){
|
|
978
|
+
int seen=nums_list_has(b,a->arr[i]);
|
|
979
|
+
if(!seen)subset=0;else disjoint=0;
|
|
980
|
+
}
|
|
981
|
+
if(!strcmp(fn,"isSubset"))RET(0,mkbool(subset));
|
|
982
|
+
else if(!strcmp(fn,"isSuperset")){
|
|
983
|
+
int superset=1;
|
|
984
|
+
for(int i=0;i<b->alen;i++)if(!nums_list_has(a,b->arr[i])){superset=0;break;}
|
|
985
|
+
RET(0,mkbool(superset));
|
|
986
|
+
}else if(!strcmp(fn,"isDisjoint"))RET(0,mkbool(disjoint));
|
|
987
|
+
else if(!strcmp(fn,"setEqual")){
|
|
988
|
+
int equal=subset;
|
|
989
|
+
for(int i=0;i<b->alen&&equal;i++)if(!nums_list_has(a,b->arr[i]))equal=0;
|
|
990
|
+
RET(0,mkbool(equal));
|
|
991
|
+
}
|
|
992
|
+
else {luc_error("sets.%s: not implemented",fn);return 0;}
|
|
993
|
+
return 1;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
static double finance_factor(double rate,double periods,double type){
|
|
997
|
+
if(rate==0)return periods;
|
|
998
|
+
return (1.0+rate*type)*expm1(periods*log1p(rate))/rate;
|
|
999
|
+
}
|
|
1000
|
+
static double finance_payment_value(double rate,double periods,double present,double future,double type){
|
|
1001
|
+
if(rate==0)return -(present+future)/periods;
|
|
1002
|
+
return -(future+present*pow(1.0+rate,periods))/finance_factor(rate,periods,type);
|
|
1003
|
+
}
|
|
1004
|
+
static int finance_check(double rate,double periods,double type,const char *fn){
|
|
1005
|
+
if(!isfinite(rate)||rate<=-1.0){luc_error("finance.%s: rate must be finite and > -1",fn);return 0;}
|
|
1006
|
+
if(!isfinite(periods)||periods<=0){luc_error("finance.%s: periods must be finite and > 0",fn);return 0;}
|
|
1007
|
+
if(type!=0.0&&type!=1.0){luc_error("finance.%s: payment timing must be 0 (end) or 1 (beginning)",fn);return 0;}
|
|
1008
|
+
return 1;
|
|
1009
|
+
}
|
|
1010
|
+
static double finance_npv_values(double rate,const double *flows,int n,int first_period){
|
|
1011
|
+
double sum=0,discount=pow(1.0+rate,(double)first_period);
|
|
1012
|
+
for(int i=0;i<n;i++){
|
|
1013
|
+
if(i)discount*=1.0+rate;
|
|
1014
|
+
sum+=flows[i]/discount;
|
|
1015
|
+
}
|
|
1016
|
+
return sum;
|
|
1017
|
+
}
|
|
1018
|
+
LFN(f_nums_finance_api){
|
|
1019
|
+
const char *fn=self->name;
|
|
1020
|
+
if(!strcmp(fn,"presentValue")||!strcmp(fn,"futureValue")||!strcmp(fn,"payment")){
|
|
1021
|
+
double rate=checknum(L,base,nargs,0,fn),periods=checknum(L,base,nargs,1,fn);
|
|
1022
|
+
double third=checknum(L,base,nargs,2,fn);
|
|
1023
|
+
double future=0,type=0,result;
|
|
1024
|
+
if(!finance_check(rate,periods,nargs>=5?checknum(L,base,nargs,4,fn):0,fn))return 0;
|
|
1025
|
+
type=nargs>=5?AR(4).u.n:0;
|
|
1026
|
+
if(!strcmp(fn,"presentValue")){
|
|
1027
|
+
double pmt=third;future=nargs>=4?checknum(L,base,nargs,3,fn):0;
|
|
1028
|
+
result=rate==0?-(future+pmt*periods):
|
|
1029
|
+
-(future+pmt*finance_factor(rate,periods,type))/pow(1.0+rate,periods);
|
|
1030
|
+
}else if(!strcmp(fn,"futureValue")){
|
|
1031
|
+
double pmt=third,present=nargs>=4?checknum(L,base,nargs,3,fn):0;
|
|
1032
|
+
result=-(present*pow(1.0+rate,periods)+pmt*finance_factor(rate,periods,type));
|
|
1033
|
+
}else{
|
|
1034
|
+
double present=third;future=nargs>=4?checknum(L,base,nargs,3,fn):0;
|
|
1035
|
+
result=finance_payment_value(rate,periods,present,future,type);
|
|
1036
|
+
}
|
|
1037
|
+
RET(0,mknum(result));return 1;
|
|
1038
|
+
}
|
|
1039
|
+
if(!strcmp(fn,"netPresentValue")){
|
|
1040
|
+
double rate=checknum(L,base,nargs,0,fn);double *flows=NULL;int n=0;
|
|
1041
|
+
if(!isfinite(rate)||rate<=-1.0){luc_error("finance.netPresentValue: rate must be finite and > -1");return 0;}
|
|
1042
|
+
if(!nums_vector_read(L,base,nargs,1,fn,&flows,&n))return 0;
|
|
1043
|
+
double value=finance_npv_values(rate,flows,n,1);
|
|
1044
|
+
free(flows);RET(0,mknum(value));return 1;
|
|
1045
|
+
}
|
|
1046
|
+
if(!strcmp(fn,"internalRate")){
|
|
1047
|
+
double *flows=NULL;int n=0;
|
|
1048
|
+
if(!nums_vector_read(L,base,nargs,0,fn,&flows,&n))return 0;
|
|
1049
|
+
if(n<2){free(flows);luc_error("finance.internalRate: need at least two cash flows");return 0;}
|
|
1050
|
+
int positive=0,negative=0;
|
|
1051
|
+
for(int i=0;i<n;i++){if(flows[i]>0)positive=1;else if(flows[i]<0)negative=1;}
|
|
1052
|
+
if(!positive||!negative){free(flows);luc_error("finance.internalRate: cash flows must include both signs");return 0;}
|
|
1053
|
+
double rate=nargs>=2?checknum(L,base,nargs,1,fn):0.1;
|
|
1054
|
+
if(!isfinite(rate)||rate<=-1.0){free(flows);luc_error("finance.internalRate: guess must be finite and > -1");return 0;}
|
|
1055
|
+
int converged=0;
|
|
1056
|
+
for(int iter=0;iter<100;iter++){
|
|
1057
|
+
double value=finance_npv_values(rate,flows,n,0),derivative=0;
|
|
1058
|
+
double base=1.0+rate,disc=1.0;
|
|
1059
|
+
for(int i=1;i<n;i++){disc*=base;derivative-=(double)i*flows[i]/(disc*base);}
|
|
1060
|
+
if(fabs(value)<1e-10){converged=1;break;}
|
|
1061
|
+
if(!isfinite(derivative)||fabs(derivative)<1e-14)break;
|
|
1062
|
+
double next=rate-value/derivative;
|
|
1063
|
+
if(!isfinite(next)||next<=-1.0||fabs(next-rate)>100.0)break;
|
|
1064
|
+
if(fabs(next-rate)<1e-12){rate=next;converged=1;break;}
|
|
1065
|
+
rate=next;
|
|
1066
|
+
}
|
|
1067
|
+
free(flows);
|
|
1068
|
+
if(!converged){luc_error("finance.internalRate: failed to converge");return 0;}
|
|
1069
|
+
RET(0,mknum(rate));return 1;
|
|
1070
|
+
}
|
|
1071
|
+
if(!strcmp(fn,"periodCount")){
|
|
1072
|
+
double rate=checknum(L,base,nargs,0,fn),pmt=checknum(L,base,nargs,1,fn);
|
|
1073
|
+
double present=checknum(L,base,nargs,2,fn),future=nargs>=4?checknum(L,base,nargs,3,fn):0;
|
|
1074
|
+
double type=nargs>=5?checknum(L,base,nargs,4,fn):0;
|
|
1075
|
+
if(!isfinite(rate)||rate<=-1||!isfinite(type)||(type!=0&&type!=1)){
|
|
1076
|
+
luc_error("finance.periodCount: invalid rate or payment timing");return 0;
|
|
1077
|
+
}
|
|
1078
|
+
double result;
|
|
1079
|
+
if(rate==0){
|
|
1080
|
+
if(pmt==0){luc_error("finance.periodCount: payment cannot be zero when rate is zero");return 0;}
|
|
1081
|
+
result=-(present+future)/pmt;
|
|
1082
|
+
}else{
|
|
1083
|
+
double factor=pmt*(1.0+rate*type)/rate;
|
|
1084
|
+
double denominator=present+factor;
|
|
1085
|
+
double growth=(factor-future)/denominator;
|
|
1086
|
+
if(denominator==0||growth<=0||!isfinite(growth)){
|
|
1087
|
+
luc_error("finance.periodCount: no real period count for these values");return 0;
|
|
1088
|
+
}
|
|
1089
|
+
result=log(growth)/log1p(rate);
|
|
1090
|
+
}
|
|
1091
|
+
if(!isfinite(result)||result<=0){luc_error("finance.periodCount: result must be positive");return 0;}
|
|
1092
|
+
RET(0,mknum(result));return 1;
|
|
1093
|
+
}
|
|
1094
|
+
if(!strcmp(fn,"interestRate")){
|
|
1095
|
+
double periods=checknum(L,base,nargs,0,fn),pmt=checknum(L,base,nargs,1,fn);
|
|
1096
|
+
double present=checknum(L,base,nargs,2,fn),future=nargs>=4?checknum(L,base,nargs,3,fn):0;
|
|
1097
|
+
double type=nargs>=5?checknum(L,base,nargs,4,fn):0;
|
|
1098
|
+
double rate=nargs>=6?checknum(L,base,nargs,5,fn):0.1;
|
|
1099
|
+
if(!isfinite(periods)||periods<=0||!isfinite(type)||(type!=0&&type!=1)||!isfinite(rate)||rate<=-1){
|
|
1100
|
+
luc_error("finance.interestRate: invalid periods, timing or guess");return 0;
|
|
1101
|
+
}
|
|
1102
|
+
int converged=0;
|
|
1103
|
+
for(int iter=0;iter<100;iter++){
|
|
1104
|
+
if(rate<=-1)break;
|
|
1105
|
+
double value=present*pow(1.0+rate,periods)+pmt*finance_factor(rate,periods,type)+future;
|
|
1106
|
+
if(fabs(value)<1e-10*(1.0+fabs(present)+fabs(future))){converged=1;break;}
|
|
1107
|
+
double h=1e-6*(1.0+fabs(rate));
|
|
1108
|
+
double rp=rate+h;
|
|
1109
|
+
double vp=present*pow(1.0+rp,periods)+pmt*finance_factor(rp,periods,type)+future;
|
|
1110
|
+
double derivative=(vp-value)/h;
|
|
1111
|
+
if(!isfinite(derivative)||fabs(derivative)<1e-14)break;
|
|
1112
|
+
double next=rate-value/derivative;
|
|
1113
|
+
if(!isfinite(next)||next<=-1||fabs(next-rate)>100)break;
|
|
1114
|
+
if(fabs(next-rate)<1e-12){rate=next;converged=1;break;}
|
|
1115
|
+
rate=next;
|
|
1116
|
+
}
|
|
1117
|
+
if(!converged){luc_error("finance.interestRate: failed to converge");return 0;}
|
|
1118
|
+
RET(0,mknum(rate));return 1;
|
|
1119
|
+
}
|
|
1120
|
+
if(!strcmp(fn,"principal")||!strcmp(fn,"interest")){
|
|
1121
|
+
double rate=checknum(L,base,nargs,0,fn);
|
|
1122
|
+
int period=checkint(L,base,nargs,1,fn);
|
|
1123
|
+
double periods=checknum(L,base,nargs,2,fn),present=checknum(L,base,nargs,3,fn);
|
|
1124
|
+
double future=nargs>=5?checknum(L,base,nargs,4,fn):0;
|
|
1125
|
+
double type=nargs>=6?checknum(L,base,nargs,5,fn):0;
|
|
1126
|
+
if(!finance_check(rate,periods,type,fn))return 0;
|
|
1127
|
+
if(period<1||period>periods){luc_error("finance.%s: period must be an integer in 1..periods",fn);return 0;}
|
|
1128
|
+
double pmt=finance_payment_value(rate,periods,present,future,type);
|
|
1129
|
+
double balance=present,previous_begin=present,interest_part=0;
|
|
1130
|
+
for(int p=1;p<=period;p++){
|
|
1131
|
+
if(type==0){
|
|
1132
|
+
interest_part=-balance*rate;
|
|
1133
|
+
balance=balance*(1.0+rate)+pmt;
|
|
1134
|
+
}else{
|
|
1135
|
+
interest_part=p==1?0.0:-previous_begin*rate;
|
|
1136
|
+
balance+=pmt;previous_begin=balance;balance*=1.0+rate;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
RET(0,mknum(!strcmp(fn,"interest")?interest_part:pmt-interest_part));return 1;
|
|
1140
|
+
}
|
|
1141
|
+
if(!strcmp(fn,"compound")){
|
|
1142
|
+
double principal=checknum(L,base,nargs,0,fn),rate=checknum(L,base,nargs,1,fn);
|
|
1143
|
+
double periods=checknum(L,base,nargs,2,fn),frequency=nargs>=4?checknum(L,base,nargs,3,fn):1;
|
|
1144
|
+
if(!isfinite(rate)||!isfinite(periods)||periods<0||!isfinite(frequency)||frequency<=0||
|
|
1145
|
+
1.0+rate/frequency<=0){luc_error("finance.compound: invalid principal, rate, periods or frequency");return 0;}
|
|
1146
|
+
RET(0,mknum(principal*pow(1.0+rate/frequency,frequency*periods)));return 1;
|
|
1147
|
+
}
|
|
1148
|
+
if(!strcmp(fn,"discount")){
|
|
1149
|
+
double future=checknum(L,base,nargs,0,fn),rate=checknum(L,base,nargs,1,fn),periods=checknum(L,base,nargs,2,fn);
|
|
1150
|
+
if(!isfinite(rate)||rate<=-1||!isfinite(periods)||periods<0){luc_error("finance.discount: rate must be > -1 and periods >= 0");return 0;}
|
|
1151
|
+
RET(0,mknum(future/pow(1.0+rate,periods)));return 1;
|
|
1152
|
+
}
|
|
1153
|
+
luc_error("finance.%s: not implemented",fn);return 0;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
LFN(f_nums_list_api){
|
|
1157
|
+
const char *fn=self->name;
|
|
1158
|
+
Table *t,*out;
|
|
1159
|
+
int n,i,j;
|
|
1160
|
+
if(!strcmp(fn,"make")){
|
|
1161
|
+
out=tab_new(1); for(i=0;i<nargs;i++) list_push(out,AR(i));
|
|
1162
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1163
|
+
}
|
|
1164
|
+
if(!strcmp(fn,"toList")){
|
|
1165
|
+
out=nums_list_from_value(AR(0),fn);
|
|
1166
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1167
|
+
}
|
|
1168
|
+
if(!strcmp(fn,"rangeList")){
|
|
1169
|
+
int start=nums_int_arg(L,base,nargs,0,"list.rangeList");
|
|
1170
|
+
int end=nums_int_arg(L,base,nargs,1,"list.rangeList");
|
|
1171
|
+
int step=nargs>=3?nums_int_arg(L,base,nargs,2,"list.rangeList"):(start<=end?1:-1);
|
|
1172
|
+
if(!step) luc_error("list.rangeList: step cannot be zero");
|
|
1173
|
+
out=tab_new(1);
|
|
1174
|
+
for(int64_t x=start;step>0?x<=end:x>=end;x+=step){
|
|
1175
|
+
if(out->alen>=10000000) luc_error("list.rangeList: result is too large");
|
|
1176
|
+
list_push(out,mknum((double)x));
|
|
1177
|
+
}
|
|
1178
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1179
|
+
}
|
|
1180
|
+
if(!strcmp(fn,"spread")){
|
|
1181
|
+
double start=checknum(L,base,nargs,0,"list.spread");
|
|
1182
|
+
double end=checknum(L,base,nargs,1,"list.spread");
|
|
1183
|
+
n=nums_nonnegative(nums_int_arg(L,base,nargs,2,"list.spread"),"list.spread");
|
|
1184
|
+
if(n>10000000) luc_error("list.spread: result is too large");
|
|
1185
|
+
out=tab_new(1);
|
|
1186
|
+
for(i=0;i<n;i++) list_push(out,mknum(n==1?start:start+(end-start)*(double)i/(double)(n-1)));
|
|
1187
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1188
|
+
}
|
|
1189
|
+
if(!strcmp(fn,"zeroList")||!strcmp(fn,"oneList")||!strcmp(fn,"blankList")||
|
|
1190
|
+
!strcmp(fn,"fillList")){
|
|
1191
|
+
n=nums_nonnegative(nums_int_arg(L,base,nargs,0,"list.fillList"),"list.fillList");
|
|
1192
|
+
if(n>10000000) luc_error("list.fillList: result is too large");
|
|
1193
|
+
Value v=!strcmp(fn,"oneList")?mknum(1):
|
|
1194
|
+
!strcmp(fn,"blankList")?NIL:
|
|
1195
|
+
!strcmp(fn,"fillList")?AR(1):mknum(0);
|
|
1196
|
+
out=tab_new(1); for(i=0;i<n;i++) list_push(out,v);
|
|
1197
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1198
|
+
}
|
|
1199
|
+
if(!strcmp(fn,"identity")){
|
|
1200
|
+
n=nums_nonnegative(nums_int_arg(L,base,nargs,0,"list.identity"),"list.identity");
|
|
1201
|
+
if(n>10000) luc_error("list.identity: matrix is too large");
|
|
1202
|
+
out=tab_new(1);
|
|
1203
|
+
for(i=0;i<n;i++){
|
|
1204
|
+
Table *row=tab_new(1);
|
|
1205
|
+
for(j=0;j<n;j++) list_push(row,mknum(i==j?1:0));
|
|
1206
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
1207
|
+
}
|
|
1208
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1209
|
+
}
|
|
1210
|
+
if(!strcmp(fn,"diagonal")){
|
|
1211
|
+
t=nums_list_arg(L,base,nargs,0,"list.diagonal"); n=t->alen;
|
|
1212
|
+
out=tab_new(1);
|
|
1213
|
+
for(i=0;i<n;i++){
|
|
1214
|
+
Table *row=tab_new(1);
|
|
1215
|
+
for(j=0;j<n;j++) list_push(row,i==j?t->arr[i]:mknum(0));
|
|
1216
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
1217
|
+
}
|
|
1218
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1219
|
+
}
|
|
1220
|
+
if(!strcmp(fn,"gridList")){
|
|
1221
|
+
int rows=nums_nonnegative(checkint(L,base,nargs,0,"list.gridList"),"list.gridList");
|
|
1222
|
+
int cols=nums_nonnegative(checkint(L,base,nargs,1,"list.gridList"),"list.gridList");
|
|
1223
|
+
if((int64_t)rows*cols>10000000) luc_error("list.gridList: grid is too large");
|
|
1224
|
+
out=tab_new(1);
|
|
1225
|
+
for(i=0;i<rows;i++){
|
|
1226
|
+
Table *row=tab_new(1);
|
|
1227
|
+
for(j=0;j<cols;j++){
|
|
1228
|
+
Table *coord=tab_new(1);
|
|
1229
|
+
list_push(coord,mknum((double)i)); list_push(coord,mknum((double)j));
|
|
1230
|
+
list_push(row,mkobj(LT_LIST,coord));
|
|
1231
|
+
}
|
|
1232
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
1233
|
+
}
|
|
1234
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1235
|
+
}
|
|
1236
|
+
if(!strcmp(fn,"fromIterator")){
|
|
1237
|
+
Value iter=AR(0);
|
|
1238
|
+
if(iter.t==LT_FUNC||iter.t==LT_CFUNC){
|
|
1239
|
+
out=tab_new(1); int scratch=base+nargs+4;
|
|
1240
|
+
for(i=0;i<10000000;i++){
|
|
1241
|
+
ensure_stack(L,scratch+4); L->stack[scratch]=iter;
|
|
1242
|
+
vm_call(L,scratch,0,1);
|
|
1243
|
+
Value item=L->stack[scratch];
|
|
1244
|
+
if(item.t==LT_NIL) break;
|
|
1245
|
+
list_push(out,item);
|
|
1246
|
+
}
|
|
1247
|
+
if(i==10000000) luc_error("list.fromIterator: iterator did not finish");
|
|
1248
|
+
}else out=nums_list_from_value(iter,fn);
|
|
1249
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1250
|
+
}
|
|
1251
|
+
if(!strcmp(fn,"flatten")||!strcmp(fn,"flatCopy")){
|
|
1252
|
+
t=nums_list_arg(L,base,nargs,0,"list.flatten"); out=tab_new(1);
|
|
1253
|
+
nums_flatten_into(t,out,0);
|
|
1254
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1255
|
+
}
|
|
1256
|
+
if(!strcmp(fn,"reshape")){
|
|
1257
|
+
t=nums_list_arg(L,base,nargs,0,"list.reshape");
|
|
1258
|
+
Table *dims=nums_list_arg(L,base,nargs,1,"list.reshape");
|
|
1259
|
+
if(dims->alen<1||dims->alen>16) luc_error("list.reshape: provide 1 to 16 dimensions");
|
|
1260
|
+
int shape[16];
|
|
1261
|
+
for(i=0;i<dims->alen;i++){
|
|
1262
|
+
if(dims->arr[i].t!=LT_NUM||dims->arr[i].u.n!=floor(dims->arr[i].u.n)||
|
|
1263
|
+
dims->arr[i].u.n<0||dims->arr[i].u.n>10000000)
|
|
1264
|
+
luc_error("list.reshape: dimensions must be nonnegative integers");
|
|
1265
|
+
shape[i]=(int)dims->arr[i].u.n;
|
|
1266
|
+
}
|
|
1267
|
+
Table *flat=tab_new(1); nums_flatten_into(t,flat,0);
|
|
1268
|
+
if(nums_list_product(shape,dims->alen)!=flat->alen)
|
|
1269
|
+
luc_error("list.reshape: dimensions do not match the number of values");
|
|
1270
|
+
i=0; out=nums_reshape_level(flat,shape,dims->alen,0,&i);
|
|
1271
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1272
|
+
}
|
|
1273
|
+
if(!strcmp(fn,"transpose")||!strcmp(fn,"axisSwap")||!strcmp(fn,"axisMove")){
|
|
1274
|
+
t=nums_list_arg(L,base,nargs,0,"list.transpose");
|
|
1275
|
+
int shape[16]={0},newshape[16]={0},perm[16],coords[16]={0},oldcoords[16]={0};
|
|
1276
|
+
int rank=nums_shape_value(AR(0),shape,0);
|
|
1277
|
+
if(rank<2) luc_error("list.%s: expected a list with at least two dimensions",fn);
|
|
1278
|
+
for(i=0;i<rank;i++) perm[i]=i;
|
|
1279
|
+
int a=0,b=1;
|
|
1280
|
+
if(!strcmp(fn,"axisSwap")){
|
|
1281
|
+
a=checkint(L,base,nargs,1,fn); b=checkint(L,base,nargs,2,fn);
|
|
1282
|
+
}else if(!strcmp(fn,"axisMove")){
|
|
1283
|
+
a=checkint(L,base,nargs,1,fn); b=checkint(L,base,nargs,2,fn);
|
|
1284
|
+
}
|
|
1285
|
+
if(a<0) a+=rank;
|
|
1286
|
+
if(b<0) b+=rank;
|
|
1287
|
+
if(a<0||a>=rank||b<0||b>=rank) luc_error("list.%s: axis out of range",fn);
|
|
1288
|
+
if(!strcmp(fn,"axisMove")){
|
|
1289
|
+
int moved=perm[a];
|
|
1290
|
+
if(a<b) for(i=a;i<b;i++) perm[i]=perm[i+1];
|
|
1291
|
+
else for(i=a;i>b;i--) perm[i]=perm[i-1];
|
|
1292
|
+
perm[b]=moved;
|
|
1293
|
+
}else{
|
|
1294
|
+
int temp=perm[a];perm[a]=perm[b];perm[b]=temp;
|
|
1295
|
+
}
|
|
1296
|
+
for(i=0;i<rank;i++) newshape[i]=shape[perm[i]];
|
|
1297
|
+
Table *flat=tab_new(1); nums_flatten_into(t,flat,0);
|
|
1298
|
+
out=tab_new(1);
|
|
1299
|
+
int total=nums_list_product(newshape,rank);
|
|
1300
|
+
for(int linear=0;linear<total;linear++){
|
|
1301
|
+
int rem=linear;
|
|
1302
|
+
for(i=rank-1;i>=0;i--){
|
|
1303
|
+
int d=newshape[i]; coords[i]=d?rem%d:0; if(d) rem/=d;
|
|
1304
|
+
oldcoords[perm[i]]=coords[i];
|
|
1305
|
+
}
|
|
1306
|
+
int old_linear=0,stride=1;
|
|
1307
|
+
for(i=rank-1;i>=0;i--){
|
|
1308
|
+
old_linear+=oldcoords[i]*stride;
|
|
1309
|
+
stride*=shape[i];
|
|
1310
|
+
}
|
|
1311
|
+
list_push(out,flat->arr[old_linear]);
|
|
1312
|
+
}
|
|
1313
|
+
int at=0;
|
|
1314
|
+
Table *reshaped=nums_reshape_level(out,newshape,rank,0,&at);
|
|
1315
|
+
RET(0,mkobj(LT_LIST,reshaped));return 1;
|
|
1316
|
+
}
|
|
1317
|
+
if(!strcmp(fn,"join")){
|
|
1318
|
+
out=tab_new(1);
|
|
1319
|
+
if(nargs==1&&AR(0).t==LT_LIST&&AS_TAB(AR(0))->alen>0&&AS_TAB(AR(0))->arr[0].t==LT_LIST){
|
|
1320
|
+
Table *parts=AS_TAB(AR(0));
|
|
1321
|
+
for(i=0;i<parts->alen;i++){
|
|
1322
|
+
if(parts->arr[i].t!=LT_LIST) luc_error("list.join: expected only lists");
|
|
1323
|
+
t=AS_TAB(parts->arr[i]); for(j=0;j<t->alen;j++) list_push(out,t->arr[j]);
|
|
1324
|
+
}
|
|
1325
|
+
}else{
|
|
1326
|
+
for(i=0;i<nargs;i++){
|
|
1327
|
+
if(AR(i).t==LT_LIST){t=AS_TAB(AR(i));for(j=0;j<t->alen;j++)list_push(out,t->arr[j]);}
|
|
1328
|
+
else list_push(out,AR(i));
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1332
|
+
}
|
|
1333
|
+
if(!strcmp(fn,"stack")||!strcmp(fn,"depthJoin")){
|
|
1334
|
+
out=tab_new(1);
|
|
1335
|
+
for(i=0;i<nargs;i++){
|
|
1336
|
+
if(AR(i).t==LT_LIST) list_push(out,mkobj(LT_LIST,nums_list_copy(AS_TAB(AR(i)))));
|
|
1337
|
+
else luc_error("list.stack: expected lists");
|
|
1338
|
+
}
|
|
1339
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1340
|
+
}
|
|
1341
|
+
if(!strcmp(fn,"horizontalJoin")||!strcmp(fn,"verticalJoin")){
|
|
1342
|
+
if(nargs<2) luc_error("list.%s: expected at least two matrices",fn);
|
|
1343
|
+
Table *first=nums_list_arg(L,base,nargs,0,fn); int rows=first->alen,cols=0;
|
|
1344
|
+
if(rows){
|
|
1345
|
+
if(first->arr[0].t!=LT_LIST) luc_error("list.%s: matrices must be nested lists",fn);
|
|
1346
|
+
cols=AS_TAB(first->arr[0])->alen;
|
|
1347
|
+
for(i=1;i<rows;i++)
|
|
1348
|
+
if(first->arr[i].t!=LT_LIST||AS_TAB(first->arr[i])->alen!=cols)
|
|
1349
|
+
luc_error("list.%s: matrices must be rectangular",fn);
|
|
1350
|
+
}
|
|
1351
|
+
for(int m=0;m<nargs;m++){
|
|
1352
|
+
Table *mat=nums_list_arg(L,base,nargs,m,fn);
|
|
1353
|
+
if(!strcmp(fn,"horizontalJoin")&&mat->alen!=rows) luc_error("list.horizontalJoin: row count mismatch");
|
|
1354
|
+
for(i=0;i<mat->alen;i++){
|
|
1355
|
+
if(mat->arr[i].t!=LT_LIST) luc_error("list.%s: matrices must be nested lists",fn);
|
|
1356
|
+
if(AS_TAB(mat->arr[i])->alen!=(mat->alen?AS_TAB(mat->arr[0])->alen:0))
|
|
1357
|
+
luc_error("list.%s: matrices must be rectangular",fn);
|
|
1358
|
+
}
|
|
1359
|
+
if(!strcmp(fn,"verticalJoin")&&mat->alen&&AS_TAB(mat->arr[0])->alen!=cols)
|
|
1360
|
+
luc_error("list.verticalJoin: column count mismatch");
|
|
1361
|
+
}
|
|
1362
|
+
out=tab_new(1);
|
|
1363
|
+
if(!strcmp(fn,"verticalJoin")){
|
|
1364
|
+
for(int m=0;m<nargs;m++){
|
|
1365
|
+
Table *mat=AS_TAB(AR(m));
|
|
1366
|
+
for(i=0;i<mat->alen;i++) list_push(out,mkobj(LT_LIST,nums_list_copy(AS_TAB(mat->arr[i]))));
|
|
1367
|
+
}
|
|
1368
|
+
}else{
|
|
1369
|
+
for(i=0;i<rows;i++){
|
|
1370
|
+
Table *row=tab_new(1);
|
|
1371
|
+
for(int m=0;m<nargs;m++){
|
|
1372
|
+
Table *mat=AS_TAB(AR(m));
|
|
1373
|
+
if(mat->arr[i].t!=LT_LIST) luc_error("list.horizontalJoin: matrices must be rectangular");
|
|
1374
|
+
Table *part=AS_TAB(mat->arr[i]);
|
|
1375
|
+
for(j=0;j<part->alen;j++) list_push(row,part->arr[j]);
|
|
1376
|
+
}
|
|
1377
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
RET(0,mkobj(LT_LIST,out));return 1;
|
|
1381
|
+
}
|
|
1382
|
+
if(!strcmp(fn,"append")){
|
|
1383
|
+
t=nums_list_arg(L,base,nargs,0,"list.append");
|
|
1384
|
+
for(i=1;i<nargs;i++) list_push(t,AR(i));
|
|
1385
|
+
RET(0,AR(0)); return 1;
|
|
1386
|
+
}
|
|
1387
|
+
if(!strcmp(fn,"insertAt")){
|
|
1388
|
+
t=nums_list_arg(L,base,nargs,0,"list.insertAt");
|
|
1389
|
+
i=checkint(L,base,nargs,1,"list.insertAt");
|
|
1390
|
+
if(i<0) i+=t->alen;
|
|
1391
|
+
if(i<0||i>t->alen) luc_error("list.insertAt: index out of range");
|
|
1392
|
+
list_insert(t,i+1,AR(2)); RET(0,AR(0)); return 1;
|
|
1393
|
+
}
|
|
1394
|
+
if(!strcmp(fn,"removeAt")){
|
|
1395
|
+
t=nums_list_arg(L,base,nargs,0,"list.removeAt");
|
|
1396
|
+
i=checkint(L,base,nargs,1,"list.removeAt");
|
|
1397
|
+
if(i<0) i+=t->alen;
|
|
1398
|
+
if(i<0||i>=t->alen) luc_error("list.removeAt: index out of range");
|
|
1399
|
+
RET(0,list_removeat(t,i+1)); return 1;
|
|
1400
|
+
}
|
|
1401
|
+
if(!strcmp(fn,"repeat")){
|
|
1402
|
+
Value v=AR(0); n=nums_nonnegative(checkint(L,base,nargs,1,"list.repeat"),"list.repeat");
|
|
1403
|
+
if(n>10000000) luc_error("list.repeat: result is too large");
|
|
1404
|
+
out=tab_new(1); for(i=0;i<n;i++) list_push(out,v);
|
|
1405
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1406
|
+
}
|
|
1407
|
+
if(!strcmp(fn,"duplicateGrid")){
|
|
1408
|
+
t=nums_list_arg(L,base,nargs,0,"list.duplicateGrid");
|
|
1409
|
+
int rows=nums_nonnegative(checkint(L,base,nargs,1,"list.duplicateGrid"),"list.duplicateGrid");
|
|
1410
|
+
int cols=nums_nonnegative(checkint(L,base,nargs,2,"list.duplicateGrid"),"list.duplicateGrid");
|
|
1411
|
+
out=tab_new(1);
|
|
1412
|
+
for(i=0;i<rows;i++){
|
|
1413
|
+
Table *row=tab_new(1);
|
|
1414
|
+
for(j=0;j<cols;j++) list_push(row,mkobj(LT_LIST,nums_list_copy(t)));
|
|
1415
|
+
list_push(out,mkobj(LT_LIST,row));
|
|
1416
|
+
}
|
|
1417
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1418
|
+
}
|
|
1419
|
+
if(!strcmp(fn,"resize")){
|
|
1420
|
+
t=nums_list_arg(L,base,nargs,0,"list.resize");
|
|
1421
|
+
n=nums_nonnegative(checkint(L,base,nargs,1,"list.resize"),"list.resize");
|
|
1422
|
+
if(n>10000000) luc_error("list.resize: result is too large");
|
|
1423
|
+
out=nums_list_copy(t);
|
|
1424
|
+
while(out->alen>n) list_removeat(out,out->alen);
|
|
1425
|
+
while(out->alen<n) list_push(out,nargs>=3?AR(2):NIL);
|
|
1426
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1427
|
+
}
|
|
1428
|
+
if(!strcmp(fn,"horizontalSplit")){
|
|
1429
|
+
t=nums_list_arg(L,base,nargs,0,"list.horizontalSplit");
|
|
1430
|
+
n=nums_nonnegative(checkint(L,base,nargs,1,"list.horizontalSplit"),"list.horizontalSplit");
|
|
1431
|
+
if(n==0) luc_error("list.horizontalSplit: number of parts must be > 0");
|
|
1432
|
+
int rows=t->alen,cols=rows&&t->arr[0].t==LT_LIST?AS_TAB(t->arr[0])->alen:0;
|
|
1433
|
+
for(i=0;i<rows;i++) if(t->arr[i].t!=LT_LIST||AS_TAB(t->arr[i])->alen!=cols)
|
|
1434
|
+
luc_error("list.horizontalSplit: matrix must be rectangular");
|
|
1435
|
+
out=tab_new(1);
|
|
1436
|
+
for(i=0;i<n;i++){
|
|
1437
|
+
int from=(int)((int64_t)cols*i/n),to=(int)((int64_t)cols*(i+1)/n);
|
|
1438
|
+
Table *part=tab_new(1);
|
|
1439
|
+
for(j=0;j<rows;j++){
|
|
1440
|
+
Table *row=tab_new(1),*src=AS_TAB(t->arr[j]);
|
|
1441
|
+
for(int c=from;c<to;c++) list_push(row,src->arr[c]);
|
|
1442
|
+
list_push(part,mkobj(LT_LIST,row));
|
|
1443
|
+
}
|
|
1444
|
+
list_push(out,mkobj(LT_LIST,part));
|
|
1445
|
+
}
|
|
1446
|
+
RET(0,mkobj(LT_LIST,out));return 1;
|
|
1447
|
+
}
|
|
1448
|
+
if(!strcmp(fn,"divideList")||!strcmp(fn,"verticalSplit")){
|
|
1449
|
+
t=nums_list_arg(L,base,nargs,0,"list.divideList");
|
|
1450
|
+
n=nums_nonnegative(checkint(L,base,nargs,1,"list.divideList"),"list.divideList");
|
|
1451
|
+
if(n==0) luc_error("list.divideList: number of parts must be > 0");
|
|
1452
|
+
out=tab_new(1);
|
|
1453
|
+
int base_size=t->alen/n, extra=t->alen%n, at=0;
|
|
1454
|
+
for(i=0;i<n;i++){
|
|
1455
|
+
int take=base_size+(i<extra);
|
|
1456
|
+
Table *part=tab_new(1);
|
|
1457
|
+
for(j=0;j<take;j++) list_push(part,t->arr[at++]);
|
|
1458
|
+
list_push(out,mkobj(LT_LIST,part));
|
|
1459
|
+
}
|
|
1460
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1461
|
+
}
|
|
1462
|
+
if(!strcmp(fn,"order")){
|
|
1463
|
+
t=nums_list_arg(L,base,nargs,0,"list.order");
|
|
1464
|
+
nums_sort_table(t,nargs>=2&&truthy(AR(1)));
|
|
1465
|
+
RET(0,AR(0)); return 1;
|
|
1466
|
+
}
|
|
1467
|
+
if(!strcmp(fn,"orderIndex")||!strcmp(fn,"rankIndex")){
|
|
1468
|
+
t=nums_list_arg(L,base,nargs,0,"list.orderIndex");
|
|
1469
|
+
int *idx=(int*)malloc((size_t)(t->alen?t->alen:1)*sizeof(int));
|
|
1470
|
+
if(!idx) luc_error("list.orderIndex: out of memory");
|
|
1471
|
+
for(i=0;i<t->alen;i++) idx[i]=i;
|
|
1472
|
+
for(i=1;i<t->alen;i++){
|
|
1473
|
+
int key=idx[i],k=i-1;
|
|
1474
|
+
while(k>=0&&nums_value_less(t->arr[key],t->arr[idx[k]])){idx[k+1]=idx[k];k--;}
|
|
1475
|
+
idx[k+1]=key;
|
|
1476
|
+
}
|
|
1477
|
+
out=tab_new(1);
|
|
1478
|
+
if(!strcmp(fn,"orderIndex")){
|
|
1479
|
+
for(i=0;i<t->alen;i++) list_push(out,mknum((double)idx[i]));
|
|
1480
|
+
}else{
|
|
1481
|
+
for(i=0;i<t->alen;i++) tab_set(out,mknum((double)idx[i]+1),mknum((double)i));
|
|
1482
|
+
}
|
|
1483
|
+
free(idx); RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1484
|
+
}
|
|
1485
|
+
if(!strcmp(fn,"multiOrder")){
|
|
1486
|
+
t=nums_list_arg(L,base,nargs,0,"list.multiOrder");
|
|
1487
|
+
Table *keys=nums_list_arg(L,base,nargs,1,"list.multiOrder");
|
|
1488
|
+
if(t->alen>1){
|
|
1489
|
+
for(i=1;i<t->alen;i++){
|
|
1490
|
+
Value row=t->arr[i]; j=i-1;
|
|
1491
|
+
while(j>=0){
|
|
1492
|
+
Value prev=t->arr[j]; int less=0;
|
|
1493
|
+
if(row.t!=LT_LIST||prev.t!=LT_LIST) luc_error("list.multiOrder: rows must be lists");
|
|
1494
|
+
Table *ra=AS_TAB(row),*rb=AS_TAB(prev);
|
|
1495
|
+
for(int k=0;k<keys->alen;k++){
|
|
1496
|
+
if(keys->arr[k].t!=LT_NUM) luc_error("list.multiOrder: keys must be integer indices");
|
|
1497
|
+
int col=(int)keys->arr[k].u.n;
|
|
1498
|
+
if(col<0||col>=ra->alen||col>=rb->alen) luc_error("list.multiOrder: key index out of range");
|
|
1499
|
+
if(nums_value_less(ra->arr[col],rb->arr[col])){less=1;break;}
|
|
1500
|
+
if(nums_value_less(rb->arr[col],ra->arr[col])) break;
|
|
1501
|
+
}
|
|
1502
|
+
if(!less) break;
|
|
1503
|
+
t->arr[j+1]=t->arr[j]; j--;
|
|
1504
|
+
}
|
|
1505
|
+
t->arr[j+1]=row;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
RET(0,AR(0)); return 1;
|
|
1509
|
+
}
|
|
1510
|
+
if(!strcmp(fn,"splitByRank")){
|
|
1511
|
+
t=nums_list_arg(L,base,nargs,0,"list.splitByRank");
|
|
1512
|
+
n=checkint(L,base,nargs,1,"list.splitByRank");
|
|
1513
|
+
out=tab_new(1);
|
|
1514
|
+
Table *sorted=nums_list_copy(t); nums_sort_table(sorted,0);
|
|
1515
|
+
if(n<0) n+=sorted->alen;
|
|
1516
|
+
if(n<0||n>sorted->alen) luc_error("list.splitByRank: rank out of range");
|
|
1517
|
+
Table *left=tab_new(1),*right=tab_new(1);
|
|
1518
|
+
for(i=0;i<n;i++) list_push(left,sorted->arr[i]);
|
|
1519
|
+
for(i=n;i<sorted->alen;i++) list_push(right,sorted->arr[i]);
|
|
1520
|
+
list_push(out,mkobj(LT_LIST,left)); list_push(out,mkobj(LT_LIST,right));
|
|
1521
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1522
|
+
}
|
|
1523
|
+
if(!strcmp(fn,"insertIndex")){
|
|
1524
|
+
t=nums_list_arg(L,base,nargs,0,"list.insertIndex");
|
|
1525
|
+
Value value=AR(1); i=0; j=t->alen;
|
|
1526
|
+
while(i<j){ int mid=i+(j-i)/2;
|
|
1527
|
+
if(nums_value_less(t->arr[mid],value)) i=mid+1; else j=mid; }
|
|
1528
|
+
RET(0,mknum((double)i)); return 1;
|
|
1529
|
+
}
|
|
1530
|
+
if(!strcmp(fn,"maxIndex")||!strcmp(fn,"minIndex")){
|
|
1531
|
+
t=nums_list_arg(L,base,nargs,0,"list.index");
|
|
1532
|
+
if(!t->alen){ RET(0,NIL); return 1; }
|
|
1533
|
+
i=0;
|
|
1534
|
+
for(j=1;j<t->alen;j++){
|
|
1535
|
+
if(!strcmp(fn,"maxIndex")?nums_value_less(t->arr[i],t->arr[j]):nums_value_less(t->arr[j],t->arr[i])) i=j;
|
|
1536
|
+
}
|
|
1537
|
+
RET(0,mknum((double)i)); return 1;
|
|
1538
|
+
}
|
|
1539
|
+
if(!strcmp(fn,"select")){
|
|
1540
|
+
t=nums_list_arg(L,base,nargs,0,"list.select");
|
|
1541
|
+
Table *indices=nums_list_arg(L,base,nargs,1,"list.select"); out=tab_new(1);
|
|
1542
|
+
if(indices->alen==t->alen){
|
|
1543
|
+
int mask=1;
|
|
1544
|
+
for(i=0;i<indices->alen;i++) if(indices->arr[i].t!=LT_BOOL){mask=0;break;}
|
|
1545
|
+
if(mask){
|
|
1546
|
+
for(i=0;i<indices->alen;i++) if(indices->arr[i].u.b) list_push(out,t->arr[i]);
|
|
1547
|
+
RET(0,mkobj(LT_LIST,out));return 1;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
for(i=0;i<indices->alen;i++){
|
|
1551
|
+
if(indices->arr[i].t!=LT_NUM) luc_error("list.select: indices must be integers");
|
|
1552
|
+
j=(int)indices->arr[i].u.n; if(j<0) j+=t->alen;
|
|
1553
|
+
if(j<0||j>=t->alen) luc_error("list.select: index out of range");
|
|
1554
|
+
list_push(out,t->arr[j]);
|
|
1555
|
+
}
|
|
1556
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1557
|
+
}
|
|
1558
|
+
if(!strcmp(fn,"findNonzero")||!strcmp(fn,"countNonzero")){
|
|
1559
|
+
t=nums_list_arg(L,base,nargs,0,"list.findNonzero"); out=tab_new(1);
|
|
1560
|
+
for(i=0;i<t->alen;i++){
|
|
1561
|
+
Value v=t->arr[i]; int nonzero=v.t!=LT_NIL&&!(v.t==LT_NUM&&v.u.n==0)&&!(v.t==LT_BOOL&&!v.u.b);
|
|
1562
|
+
if(nonzero) list_push(out,mknum((double)i));
|
|
1563
|
+
}
|
|
1564
|
+
RET(0,!strcmp(fn,"countNonzero")?mknum((double)out->alen):mkobj(LT_LIST,out)); return 1;
|
|
1565
|
+
}
|
|
1566
|
+
if(!strcmp(fn,"distinct")){
|
|
1567
|
+
t=nums_list_arg(L,base,nargs,0,"list.distinct"); out=tab_new(1);
|
|
1568
|
+
for(i=0;i<t->alen;i++){
|
|
1569
|
+
for(j=0;j<out->alen;j++) if(val_rawequal(t->arr[i],out->arr[j])) break;
|
|
1570
|
+
if(j==out->alen) list_push(out,t->arr[i]);
|
|
1571
|
+
}
|
|
1572
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1573
|
+
}
|
|
1574
|
+
if(!strcmp(fn,"filter")){
|
|
1575
|
+
t=nums_list_arg(L,base,nargs,0,"list.filter"); out=tab_new(1);
|
|
1576
|
+
Value pred=AR(1); int scratch=base+nargs+4;
|
|
1577
|
+
for(i=0;i<t->alen;i++){
|
|
1578
|
+
ensure_stack(L,scratch+4); L->stack[scratch]=pred;
|
|
1579
|
+
L->stack[scratch+1]=t->arr[i]; L->stack[scratch+2]=mknum((double)i);
|
|
1580
|
+
vm_call(L,scratch,2,1);
|
|
1581
|
+
if(truthy(L->stack[scratch])) list_push(out,t->arr[i]);
|
|
1582
|
+
}
|
|
1583
|
+
RET(0,mkobj(LT_LIST,out)); return 1;
|
|
1584
|
+
}
|
|
1585
|
+
luc_error("list.%s: not implemented",fn);
|
|
1586
|
+
return 0;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
static void luc_nums_open(Table *math,Table *random){
|
|
1590
|
+
nums_list_api=tab_new(0); nums_search_api=tab_new(0);
|
|
1591
|
+
nums_time_api=tab_new(0); nums_text_api=V.stringlib;
|
|
1592
|
+
nums_matrix_api=tab_new(0); nums_sets_api=tab_new(0); nums_finance_api=tab_new(0);
|
|
1593
|
+
nums_list_register(nums_list_api,"make",f_nums_list_api);
|
|
1594
|
+
nums_list_register(nums_list_api,"toList",f_nums_list_api);
|
|
1595
|
+
nums_list_register(nums_list_api,"rangeList",f_nums_list_api);
|
|
1596
|
+
nums_list_register(nums_list_api,"spread",f_nums_list_api);
|
|
1597
|
+
nums_list_register(nums_list_api,"zeroList",f_nums_list_api);
|
|
1598
|
+
nums_list_register(nums_list_api,"oneList",f_nums_list_api);
|
|
1599
|
+
nums_list_register(nums_list_api,"fillList",f_nums_list_api);
|
|
1600
|
+
nums_list_register(nums_list_api,"blankList",f_nums_list_api);
|
|
1601
|
+
nums_list_register(nums_list_api,"identity",f_nums_list_api);
|
|
1602
|
+
nums_list_register(nums_list_api,"diagonal",f_nums_list_api);
|
|
1603
|
+
nums_list_register(nums_list_api,"fromIterator",f_nums_list_api);
|
|
1604
|
+
nums_list_register(nums_list_api,"gridList",f_nums_list_api);
|
|
1605
|
+
nums_list_register(nums_list_api,"reshape",f_nums_list_api);
|
|
1606
|
+
nums_list_register(nums_list_api,"flatten",f_nums_list_api);
|
|
1607
|
+
nums_list_register(nums_list_api,"flatCopy",f_nums_list_api);
|
|
1608
|
+
nums_list_register(nums_list_api,"transpose",f_nums_list_api);
|
|
1609
|
+
nums_list_register(nums_list_api,"axisSwap",f_nums_list_api);
|
|
1610
|
+
nums_list_register(nums_list_api,"axisMove",f_nums_list_api);
|
|
1611
|
+
nums_list_register(nums_list_api,"join",f_nums_list_api);
|
|
1612
|
+
nums_list_register(nums_list_api,"stack",f_nums_list_api);
|
|
1613
|
+
nums_list_register(nums_list_api,"horizontalJoin",f_nums_list_api);
|
|
1614
|
+
nums_list_register(nums_list_api,"verticalJoin",f_nums_list_api);
|
|
1615
|
+
nums_list_register(nums_list_api,"depthJoin",f_nums_list_api);
|
|
1616
|
+
nums_list_register(nums_list_api,"divideList",f_nums_list_api);
|
|
1617
|
+
nums_list_register(nums_list_api,"horizontalSplit",f_nums_list_api);
|
|
1618
|
+
nums_list_register(nums_list_api,"verticalSplit",f_nums_list_api);
|
|
1619
|
+
nums_list_register(nums_list_api,"resize",f_nums_list_api);
|
|
1620
|
+
nums_list_register(nums_list_api,"append",f_nums_list_api);
|
|
1621
|
+
nums_list_register(nums_list_api,"insertAt",f_nums_list_api);
|
|
1622
|
+
nums_list_register(nums_list_api,"removeAt",f_nums_list_api);
|
|
1623
|
+
nums_list_register(nums_list_api,"repeat",f_nums_list_api);
|
|
1624
|
+
nums_list_register(nums_list_api,"duplicateGrid",f_nums_list_api);
|
|
1625
|
+
nums_list_register(nums_list_api,"readList",f_nums_file_api);
|
|
1626
|
+
nums_list_register(nums_list_api,"writeList",f_nums_file_api);
|
|
1627
|
+
nums_list_register(nums_list_api,"writeBundle",f_nums_file_api);
|
|
1628
|
+
nums_list_register(nums_list_api,"writeCompressed",f_nums_file_api);
|
|
1629
|
+
nums_list_register(nums_list_api,"readText",f_nums_file_api);
|
|
1630
|
+
nums_list_register(nums_list_api,"writeText",f_nums_file_api);
|
|
1631
|
+
nums_list_register(nums_list_api,"readData",f_nums_file_api);
|
|
1632
|
+
nums_list_register(nums_list_api,"readBinary",f_nums_file_api);
|
|
1633
|
+
nums_list_register(nums_list_api,"writeBinary",f_nums_file_api);
|
|
1634
|
+
nums_list_register(nums_list_api,"parseList",f_nums_file_api);
|
|
1635
|
+
nums_list_register(nums_list_api,"listString",f_nums_file_api);
|
|
1636
|
+
nums_list_register(nums_search_api,"order",f_nums_list_api);
|
|
1637
|
+
nums_list_register(nums_search_api,"orderIndex",f_nums_list_api);
|
|
1638
|
+
nums_list_register(nums_search_api,"multiOrder",f_nums_list_api);
|
|
1639
|
+
nums_list_register(nums_search_api,"splitByRank",f_nums_list_api);
|
|
1640
|
+
nums_list_register(nums_search_api,"rankIndex",f_nums_list_api);
|
|
1641
|
+
nums_list_register(nums_search_api,"insertIndex",f_nums_list_api);
|
|
1642
|
+
nums_list_register(nums_search_api,"maxIndex",f_nums_list_api);
|
|
1643
|
+
nums_list_register(nums_search_api,"minIndex",f_nums_list_api);
|
|
1644
|
+
nums_list_register(nums_search_api,"select",f_nums_list_api);
|
|
1645
|
+
nums_list_register(nums_search_api,"findNonzero",f_nums_list_api);
|
|
1646
|
+
nums_list_register(nums_search_api,"countNonzero",f_nums_list_api);
|
|
1647
|
+
nums_list_register(nums_search_api,"distinct",f_nums_list_api);
|
|
1648
|
+
nums_list_register(nums_search_api,"filter",f_nums_list_api);
|
|
1649
|
+
nums_list_register(nums_time_api,"dateTime",f_nums_time_api);
|
|
1650
|
+
nums_list_register(nums_time_api,"timeSpan",f_nums_time_api);
|
|
1651
|
+
nums_list_register(nums_time_api,"isInvalidDate",f_nums_time_api);
|
|
1652
|
+
nums_list_register(nums_time_api,"dateString",f_nums_time_api);
|
|
1653
|
+
nums_list_register(nums_time_api,"workDaysBetween",f_nums_time_api);
|
|
1654
|
+
nums_list_register(nums_time_api,"workDayOffset",f_nums_time_api);
|
|
1655
|
+
nums_list_register(nums_time_api,"isWorkDay",f_nums_time_api);
|
|
1656
|
+
nums_list_register(nums_time_api,"workCalendar",f_nums_time_api);
|
|
1657
|
+
nums_list_register(nums_text_api,"concat",f_nums_text_api);
|
|
1658
|
+
nums_list_register(nums_text_api,"repeatText",f_nums_text_api);
|
|
1659
|
+
nums_list_register(nums_text_api,"capitalize",f_nums_text_api);
|
|
1660
|
+
nums_list_register(nums_text_api,"alignCenter",f_nums_text_api);
|
|
1661
|
+
nums_list_register(nums_text_api,"lowercase",f_nums_text_api);
|
|
1662
|
+
nums_list_register(nums_text_api,"uppercase",f_nums_text_api);
|
|
1663
|
+
nums_list_register(nums_text_api,"titleCase",f_nums_text_api);
|
|
1664
|
+
nums_list_register(nums_text_api,"reverseCase",f_nums_text_api);
|
|
1665
|
+
nums_list_register(nums_text_api,"trimLeft",f_nums_text_api);
|
|
1666
|
+
nums_list_register(nums_text_api,"trimRight",f_nums_text_api);
|
|
1667
|
+
nums_list_register(nums_text_api,"replaceText",f_nums_text_api);
|
|
1668
|
+
nums_list_register(nums_text_api,"findText",f_nums_text_api);
|
|
1669
|
+
nums_list_register(nums_text_api,"countText",f_nums_text_api);
|
|
1670
|
+
nums_list_register(nums_text_api,"startsWith",f_nums_text_api);
|
|
1671
|
+
nums_list_register(nums_text_api,"endsWith",f_nums_text_api);
|
|
1672
|
+
nums_list_register(nums_text_api,"splitText",f_nums_text_api);
|
|
1673
|
+
nums_list_register(nums_text_api,"joinText",f_nums_text_api);
|
|
1674
|
+
nums_list_register(nums_text_api,"isLetters",f_nums_text_api);
|
|
1675
|
+
nums_list_register(nums_text_api,"isDigits",f_nums_text_api);
|
|
1676
|
+
nums_list_register(nums_text_api,"isAlphanumeric",f_nums_text_api);
|
|
1677
|
+
nums_list_register(nums_text_api,"zeroPad",f_nums_text_api);
|
|
1678
|
+
nums_list_register(nums_text_api,"encodeText",f_nums_text_api);
|
|
1679
|
+
nums_list_register(nums_text_api,"decodeText",f_nums_text_api);
|
|
1680
|
+
tab_set(V.globals,cstrv("time"),mkobj(LT_TABLE,nums_time_api));
|
|
1681
|
+
nums_list_register(nums_matrix_api,"dot",f_nums_matrix_api);
|
|
1682
|
+
nums_list_register(nums_matrix_api,"matMul",f_nums_matrix_api);
|
|
1683
|
+
nums_list_register(nums_matrix_api,"cross",f_nums_matrix_api);
|
|
1684
|
+
nums_list_register(nums_matrix_api,"det",f_nums_matrix_api);
|
|
1685
|
+
nums_list_register(nums_matrix_api,"inverse",f_nums_matrix_api);
|
|
1686
|
+
nums_list_register(nums_matrix_api,"transpose",f_nums_list_api);
|
|
1687
|
+
nums_list_register(nums_matrix_api,"trace",f_nums_matrix_api);
|
|
1688
|
+
nums_list_register(nums_matrix_api,"solve",f_nums_matrix_api);
|
|
1689
|
+
nums_list_register(nums_matrix_api,"eigen",f_nums_matrix_api);
|
|
1690
|
+
nums_list_register(nums_matrix_api,"norm",f_nums_matrix_api);
|
|
1691
|
+
nums_list_register(nums_matrix_api,"rank",f_nums_matrix_api);
|
|
1692
|
+
nums_list_register(nums_matrix_api,"qr",f_nums_matrix_api);
|
|
1693
|
+
nums_list_register(nums_matrix_api,"svd",f_nums_matrix_api);
|
|
1694
|
+
nums_list_register(nums_matrix_api,"cholesky",f_nums_matrix_api);
|
|
1695
|
+
nums_list_register(nums_matrix_api,"matrixPower",f_nums_matrix_api);
|
|
1696
|
+
nums_list_register(nums_sets_api,"union",f_nums_sets_api);
|
|
1697
|
+
nums_list_register(nums_sets_api,"intersect",f_nums_sets_api);
|
|
1698
|
+
nums_list_register(nums_sets_api,"difference",f_nums_sets_api);
|
|
1699
|
+
nums_list_register(nums_sets_api,"symmetricDiff",f_nums_sets_api);
|
|
1700
|
+
nums_list_register(nums_sets_api,"isSubset",f_nums_sets_api);
|
|
1701
|
+
nums_list_register(nums_sets_api,"isSuperset",f_nums_sets_api);
|
|
1702
|
+
nums_list_register(nums_sets_api,"isDisjoint",f_nums_sets_api);
|
|
1703
|
+
nums_list_register(nums_sets_api,"setEqual",f_nums_sets_api);
|
|
1704
|
+
{
|
|
1705
|
+
Value unique=tab_get(nums_search_api,cstrv("distinct"));
|
|
1706
|
+
tab_set(nums_sets_api,cstrv("unique"),unique);
|
|
1707
|
+
tab_set(nums_sets_api,cstrv("Unique"),unique);
|
|
1708
|
+
tab_set(nums_sets_api,cstrv("UNIQUE"),unique);
|
|
1709
|
+
}
|
|
1710
|
+
nums_list_register(nums_finance_api,"presentValue",f_nums_finance_api);
|
|
1711
|
+
nums_list_register(nums_finance_api,"futureValue",f_nums_finance_api);
|
|
1712
|
+
nums_list_register(nums_finance_api,"netPresentValue",f_nums_finance_api);
|
|
1713
|
+
nums_list_register(nums_finance_api,"internalRate",f_nums_finance_api);
|
|
1714
|
+
nums_list_register(nums_finance_api,"payment",f_nums_finance_api);
|
|
1715
|
+
nums_list_register(nums_finance_api,"interestRate",f_nums_finance_api);
|
|
1716
|
+
nums_list_register(nums_finance_api,"periodCount",f_nums_finance_api);
|
|
1717
|
+
nums_list_register(nums_finance_api,"principal",f_nums_finance_api);
|
|
1718
|
+
nums_list_register(nums_finance_api,"interest",f_nums_finance_api);
|
|
1719
|
+
nums_list_register(nums_finance_api,"compound",f_nums_finance_api);
|
|
1720
|
+
nums_list_register(nums_finance_api,"discount",f_nums_finance_api);
|
|
1721
|
+
Table *nums=tab_new(0);
|
|
1722
|
+
tab_set(nums,cstrv("math"),mkobj(LT_TABLE,math));
|
|
1723
|
+
tab_set(nums,cstrv("random"),mkobj(LT_TABLE,random));
|
|
1724
|
+
tab_set(nums,cstrv("list"),mkobj(LT_TABLE,nums_list_api));
|
|
1725
|
+
tab_set(nums,cstrv("search"),mkobj(LT_TABLE,nums_search_api));
|
|
1726
|
+
tab_set(nums,cstrv("string"),mkobj(LT_TABLE,nums_text_api));
|
|
1727
|
+
tab_set(nums,cstrv("time"),mkobj(LT_TABLE,nums_time_api));
|
|
1728
|
+
tab_set(nums,cstrv("matrix"),mkobj(LT_TABLE,nums_matrix_api));
|
|
1729
|
+
tab_set(nums,cstrv("sets"),mkobj(LT_TABLE,nums_sets_api));
|
|
1730
|
+
tab_set(nums,cstrv("finance"),mkobj(LT_TABLE,nums_finance_api));
|
|
1731
|
+
tab_set(V.loaded,cstrv("system:nums"),mkobj(LT_TABLE,nums));
|
|
1732
|
+
tab_set(V.globals,cstrv("search"),mkobj(LT_TABLE,nums_search_api));
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
static void luc_nums_bind(Table *nums){
|
|
1736
|
+
Value v;
|
|
1737
|
+
for(int i=0;i<6;i++){
|
|
1738
|
+
const char *name=i==0?"random":i==1?"search":i==2?"time":i==3?"matrix":i==4?"sets":"finance";
|
|
1739
|
+
v=tab_get(nums,cstrv(name));
|
|
1740
|
+
if(v.t==LT_TABLE) tab_set(V.globals,cstrv(name),v);
|
|
1741
|
+
}
|
|
1742
|
+
Table *global=AS_TAB(tab_get(V.globals,cstrv("list")));
|
|
1743
|
+
Value k,val; k=NIL;
|
|
1744
|
+
while(tab_next(nums_list_api,k,&k,&val)) tab_set(global,k,val);
|
|
1745
|
+
tab_set(nums,cstrv("list"),mkobj(LT_TABLE,global));
|
|
1746
|
+
}
|
|
1747
|
+
|