@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/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
+