From f4f4efd1b51b8e5b0c55785dc359f0b0e4526e04 Mon Sep 17 00:00:00 2001 From: Henry Rich Date: Tue, 7 Jan 2025 16:21:45 -0500 Subject: [PATCH] fa() stop checking for invalid arg; symbis() don't zap for public assignment; jtxdefn save a test --- jsrc/cx.c | 8 ++++---- jsrc/dstop.c | 2 +- jsrc/j.h | 2 +- jsrc/ja.h | 6 +++--- jsrc/je.h | 1 + jsrc/p.c | 2 +- jsrc/s.c | 21 +++++++++++++-------- jsrc/vcat.c | 2 +- jsrc/vrand.c | 2 +- test/g320ip.ijs | 12 +++++++++++- test/gtdot5.ijs | 8 ++++---- 11 files changed, 41 insertions(+), 25 deletions(-) diff --git a/jsrc/cx.c b/jsrc/cx.c index 88f77222e..f45490195 100644 --- a/jsrc/cx.c +++ b/jsrc/cx.c @@ -65,7 +65,7 @@ // popstmt is used to call tpop if the tpop stack is not empty; asgstmt is issued after the parse, where a variable can be invalidated to prevent it from being saved over calls // we call only when the stack has values on it. We do our best during assignment to retrace the stack rather than zap it. If the stack has any values, we must call to make // sure that all assignments-in-place have had the usercount fully restored -#define parseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); if((UI)jt->tnextpushp!=(UI)old){popstmt} S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \ +#define parseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); popstmt S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \ SETTRACK \ if(likely(!(attnval+(NPGpysfmtdl&128+16))))z=parsea(queue,m); \ else {if(jt->sitop&&jt->sitop->dclnk&&jt->sitop->dclnk->dctype==DCCALL)jt->sitop->dclnk->dcix=~ic; z=parsex(queue,m,CWSOURCE(cwsent,CNSTOREDCW,ic),(NPGpysfmtdl&128+16)?jt->sitop->dclnk:0); if(!(jt->uflags.trace&TRACEDB))NPGpysfmtdl&=~2;} \ @@ -125,7 +125,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k; // an incumbent value, we remove it. We also zap the value we install, just as in any normal assignment L *asym=&SYMORIGIN[cv->indexsym]; // pointer symbol-table entry, index then item ASSERT(!(asym->flag&LREADONLY),EVRO) // it had better not be readonly now - fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it + if(unlikely(QCWORD(asym->fval)!=0))fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it A xx; GAT0(xx,INT,1,0); IAV0(xx)[0]=-1; AFLAGINIT(xx,AFRO) // -1 is the iteration number if there are no iterations; mark value RO to prevent xxx_index =: xxx_index + 1 from changing inplace ACINITUNPUSH(xx); asym->fval=SETNAMED(MAKEFVAL(xx,ATYPETOVALTYPE(INT))); // raise usecount, as local name; install as value of xyz_index rifv(t); // it would be work to handle virtual t, because you can't just ra() a virtual, as virtuals are freed only from the tpop stack. So we wimp out & realize. note we can free from a boxed array now @@ -138,7 +138,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k; // We store the block in 2 places: cv and symp.val. We ra() once for each place // If there is an incumbent value, discard it asym=&SYMORIGIN[cv->itemsym]; A val=QCWORD(asym->fval); // stored reference address; incumbent value there - fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error + if(unlikely(val!=0))fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error // Calculate the item size and save it I isz; I r=AR(t)-((UI)AR(t)>0); PROD(isz,r,AS(t)+1); I tt=AT(t); cv->itemsiz=isz<t); // decr the for/select value, protected at beginning. NOP if it is 0 + if(likely(cv->t!=0))fa(cv->t); // decr the for/select value, protected at beginning. NOP if it is 0 cv=cv->bchn; // go back to previous stack level R cv; } diff --git a/jsrc/dstop.c b/jsrc/dstop.c index 2e2e09708..feaf6401f 100644 --- a/jsrc/dstop.c +++ b/jsrc/dstop.c @@ -78,7 +78,7 @@ F1(jtdbstops){ RZ(w=vs(w)); if(AN(w)){RZ(ras(w));}else w=0; // protect w if it is nonempty; if empty, convert to null WRITELOCK(JT(jt,dblock)) A stops=JT(jt,dbstops); JT(jt,dbstops)=w; WRITEUNLOCK(JT(jt,dblock)) // swap addresses under lock - fa(stops); // undo the ra() done when value was stored - null is ok + if(stops!=0)fa(stops); // undo the ra() done when value was stored R mtm; } diff --git a/jsrc/j.h b/jsrc/j.h index 943266b38..e4c4e8e84 100644 --- a/jsrc/j.h +++ b/jsrc/j.h @@ -1024,12 +1024,12 @@ struct jtimespec jmtfclk(void); //'fast clock'; maybe less inaccurate; intended #define CCOMMON(x,pref,err) ({A res=(x); pref if(unlikely((AT(res)&PYX)!=0)){if(unlikely((res=jtpyxval(jt,res))==0))err;} res; }) // extract & resolve contents; execute err if error in resolution x may have side effects // since most uses of contents check the type first, it would be good to have the type loaded at any finish. But the compiler doesn't do that. #define READLOCK(lock) {S prev; if(unlikely(((prev=__atomic_fetch_add(&lock,1,__ATOMIC_ACQ_REL))&(S)-WLOCKBIT)!=0))readlock(&lock,prev);} +#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits #if WLOCKBIT==0x8000 #define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_or(&lock,(S)WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);} #else #define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_add(&lock,WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);} #endif -#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits #define WRITEUNLOCK(lock) __atomic_fetch_and(&lock,WLOCKBIT-1, __ATOMIC_ACQ_REL); // clear all the write bits #else #define CCOMMON(x,pref,err) (x) diff --git a/jsrc/ja.h b/jsrc/ja.h index 5569b1c26..53e2b43a0 100644 --- a/jsrc/ja.h +++ b/jsrc/ja.h @@ -358,11 +358,11 @@ // faifowed does the free only if the block is marked FAOWED in stkf. These may have been stacked by local names, if the stack was later protected #define faifowed(x,Zc,tt,stkf) {if(unlikely(tt<0) || ((((Zc>>(ACPERMANENTX-(STKFAOWEDX+1)))&(4*STKFAOWED-1))<((I)stkf&STKFAOWED)) && unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2)))jtfamf(jt,x,tt);} // call if sparse or ending; never touch a PERM // FAOWED becomes ..0f0; perm becomes .0p00 where perhaps p00 has +-1 added; 0f0>p00 means 'FAOWED & not PERMANENT' -#define fajt(jt,x) {if(likely((x)!=0))faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})} +#define fajt(jt,x) {/*if(likely((x)!=0))obsolete*/faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})} #define fa(x) fajt(jt,(x)) // when the block will usually NOT be deleted #define falikely(x) fa(x) // when the block will usually be deleted (not used yet) -#define fatype(x,tt) {if((x)==0)SEGFAULT; if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // scaf when type is known (i. e. NAME) +#define fatype(x,tt) {if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // when type is known (i. e. NAME) #define fanamedacv(x) {I Zc=AC(x); if(likely(!ACISPERM(Zc)))if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,AT(x));} // block is known to be ACV, recursive, AC>0, and almost always AC>1 // when x is known to be valid and usecount has gone to 0 #define fanano0(x) faaction(jt,(x),;) @@ -923,7 +923,7 @@ extern void jfree4gmp(void*,size_t); #define rapos(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0 #define raposlocal(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0 #define raposacv(x) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL);}} // ACV is guaranteed recursive -#define raposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){ACADD(x,1); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse +#define raposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse #define raposlocalqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse #define rarecur(x) {I c=AC(x); if(likely(!ACISPERM(c))){if(c<0)AC(x)=(I)((UI)c+(ACINPLACE+ACUC1));else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); I tt=AT(x); if(unlikely(ISSPARSE(tt)))x=jtra((x),(tt),x);}} // ra but recursibles know to be recursive // NOTE that every() produces blocks with usecount 0x8..2 (if a recursive block has pristine contents whose usecount is 2); if we ZAP that it must go to 2 diff --git a/jsrc/je.h b/jsrc/je.h index 9631776ea..2a02a2f2f 100644 --- a/jsrc/je.h +++ b/jsrc/je.h @@ -681,6 +681,7 @@ extern void jtcopyTT(J, void *, void *, I, I,I); #endif extern A cw57rep(J,A); extern I infererrtok(J); +extern I johnson(I); extern A jtac1(J,AF); extern A jtac2(J,AF); extern B jtadd2(J,F,F,C*); diff --git a/jsrc/p.c b/jsrc/p.c index 7913a70f3..6a7335b05 100644 --- a/jsrc/p.c +++ b/jsrc/p.c @@ -1060,7 +1060,7 @@ rejectfrag:; // by another thread while we are using the value. The case is very rare but we test for it, so we have to make it work. // If final assignment was local this can't happen, and we do the fa jt->parserstackframe = oframe; // pop the parser frame-stack before tpushna, which may fail - if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} else tpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush + if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} else tpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush }else{ // If there was an error during execution or name-stacking, exit with failure. Error has already been signaled. Remove zombiesym. Repurpose pt0ecam failparsestack: // here we encountered an error during stacking. The error was processed using an old stack, so its spacing is wrong. // we set the error word# for the failing word and then resignal the error to get the spacing right and call eformat to annotate it diff --git a/jsrc/s.c b/jsrc/s.c index c7166764f..a9521f04f 100644 --- a/jsrc/s.c +++ b/jsrc/s.c @@ -691,7 +691,7 @@ A jtprobequiet(J jt,A a){A g; // assign symbol: assign name a in symbol table g to the value w (but g is ignored if a is a locative) // Result is 0 if error, otherwise low 2 bits are x1 = final assignment, 1x = local assignment, others garbage -// flags set in jt: bit 0=this is a final assignment; +// flags set in jt: bit 0=this is a final assignment; bit 1 always 0 I jtsymbis(J jt,A a,A w,A g){F2PREFIP; ARGCHK2(a,w); I anmf=NAV(a)->flag; // fetch flags for the name @@ -739,7 +739,7 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; if((AR(g)&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms) I4 symx=NAV(a)->symx; // fetch the symbol slot assigned to this name (0 if none) e=likely((SGNIF(AR(g),ARLCLONEDX)|(symx-1))>=0)?SYMORIGIN+(I)symx:probeislocal(a,g); // local symbol given and we are using the original table: use the symbol. Otherwise, look up and reserve 1 symbol - g=0; // indicate we have no lock to clear + g=(A)((I)jtinplace|-JTASGNWASLOCAL); // indicate local assignment (we have no lock to clear), remember final assignment valtype|=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse }else{ // global table SYMRESERVE(1) // before we go into lock, make sure we have a symbol to assign to @@ -747,12 +747,14 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; valtype|=QCNAMED|QCRAREQD; // must flag local/global type in symbol e=probeis(a, g); // get the symbol address to use, old or new. This returns holding a lock on the locale // if we are writing to a non-local table, update the table's Bloom filter. + g=(A)((I)g+((I)jtinplace&JTFINALASGN)); // flags in g: copy final-assignment flag, keep glocal-table flag 0 indicating free needed BLOOMSET(bloombase,chainno); // g is under lock. This modifies the shared memory every time - might be better to write only when chain is empty // A couple of debugging flags are set during assignment. We don't bother for local names if(unlikely(JT(jt,stch)!=0))e->flag|=LCH; // update 'changed' flag if enabled - needed only for globals e->sn=jt->currslistx; // Save the script in which this name was defined - meaningful only for globals } - // ****** if g is a global table, we have a write lock on the locale, which we must release in any error paths. g=0 otherwise ******* + // ****** if g is a global table, bit2=0 and we have a write lock on the locale, which we must release in any error paths. The low 2 bits + // of g are exit flags: bit0=final assignment, bit 1=local assignment. If local assignment, g=-2 (not final) or -1 (final) ******* A x=e->fval; // if x is 0, this name has not been assigned yet; if nonzero, x points to the incumbent value // If we are assigning the same data block that's already there, don't bother with changing use counts or anything else (assignment-in-place) @@ -775,9 +777,11 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; // Virtual values were realized earlier, and usecounts guaranteed recursive // If the value is abandoned inplaceable, we can just zap it and set its usecount to 1 // SPARSE nouns must never be inplaceable, because their components are not - if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value +// obsolete if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value, in local table + if(((I)g+SGNTO0(AC(w)))==0){ // if final assignment of abandoned value, in local table (g=-1 and inplace bit set) // We can zap abandoned nouns. But only if they are final assignment: something like nm:_ [ nm=. 4+4 would free the active block if we zapped, when the FAOWED was applied - // also, if the name is global it may vanish, along with the value, at any time, so we + // the idea is that the assignment will protect the value; this is true for local assignment but not for global, because another thread may free the name immediately. + // If we could be sure that the result of the sentence would never be inspected we could return a value that might be freed, but without such a guarantee we can zap only local assignment. AFLAGORLOCAL(w,AFKNOWNNAMED); // indicate the value is in a name. We do this to allow virtual extension. // very often a final assignment will assign the abandoned result of the last computation. These values accumulate on the tstack and have to // be popped off every now & then in jtxdefn, since there is nothing else to pop them. We detect this important case, in which the zaploc of the @@ -802,15 +806,16 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; } }else x=0; // repurpose x to be the value needing fa // x here is the value that needs to be freed - if(g!=0)WRITEUNLOCK(g->lock)else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return + if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock); +// obsolete else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return // ************* we have released the write lock // If this is a reassignment, we need to decrement the use count in the old value, since that value is no longer used. Do so after the new value is raised, // in case the new value was being protected by the old (ex: n =. >n). // It is the responsibility of parse to keep the usecount of a named value raised until it has come out of execution SYMVALFA2(x); // if the old value needs to be traversed in detail, do it now outside of lock (subroutine call) - R (I)jtinplace; // good return, with bit 0 set if final assignment, bit 1 if local + R (I)g; // good return, with bit 0 set if final assignment, bit 1 if local exitlock: // error exit - if(g!=0)WRITEUNLOCK(g->lock) + if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock) R 0; } /* a: name; w: value; g: symbol table */ diff --git a/jsrc/vcat.c b/jsrc/vcat.c index 4f3ce4de1..d7a10d45e 100644 --- a/jsrc/vcat.c +++ b/jsrc/vcat.c @@ -357,7 +357,7 @@ A jtapip(J jt, A a, A w){F2PREFIP;A h; I lgk=bplg(at); I lgatomsini=MAX(LGSZI-lgk,0); // lg of size of atom of a; lg of number of atoms in an I (0 if <1) // Because the test for inplaceability is rather lengthy, start with a quick check of the atom counts. If adding the atoms in w to those in a // would push a over a power-of-2 boundary, skip the rest of the testing. We detect this by absence of carry out of the high bit (inside EXTENDINPLACE) - if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0) { + if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0){ // collect some values into a flags register #define FGLGK 0x7 #define FGVIRTREQDX 3 // if virtual extension required diff --git a/jsrc/vrand.c b/jsrc/vrand.c index 3feb9ef01..73c0505be 100644 --- a/jsrc/vrand.c +++ b/jsrc/vrand.c @@ -570,7 +570,7 @@ F1(jtrngseeds){I k,r; if(r){ // w is not an atom. the RNG had better be Mersenne Twister. Initialize using w, and save the w list ASSERT(1==r&&MTI==jt->rngdata->rng,EVRANK); - RZ(ras(w)); fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers + RZ(ras(w)); if(jt->rngdata->rngseed!=0)fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers mt_init_by_array(AV(w),AN(w)); }else switch(jt->rngdata->rng){ // atomic w. We can use that for any generator. Choose the current one. diff --git a/test/g320ip.ijs b/test/g320ip.ijs index be4a77c63..525459a4c 100644 --- a/test/g320ip.ijs +++ b/test/g320ip.ijs @@ -602,9 +602,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@, ''b''' f =: 3 : 0 9!:53 (1) a =: 10000#'c' -assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@(({.''b'') ,~ ]) a' +for. i. 4 do. 0 end. NB. ensure tpop +assert. IGNOREIFFVI 3000 > err =: 7!:2 'a =: {.@(({.''b'') ,~ ]) a' a =: 10000#'c' +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@, ''b''' +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@, ''b''' 1 ) @@ -625,9 +628,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@:, ''b''' f =: 3 : 0 9!:53 (1) a =: 10000#'c' +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@:(({.''b'') ,~ ]) a' a =: 10000#'c' +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@:, ''b''' +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@:, ''b''' 1 ) @@ -644,8 +650,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a' NB. realize any vir f =: 3 : 0 9!:53 (1) a =: i. 1000 +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a' a =: i. 1000 +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ unsafename) a' 1 ) @@ -661,8 +669,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a' f =: 3 : 0 9!:53 (1) a =: i. 1000 +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a' a =: i. 1000 +for. i. 4 do. 0 end. NB. ensure tpop assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ unsafename) a' 1 ) diff --git a/test/gtdot5.ijs b/test/gtdot5.ijs index 6a957ecac..dd8b189c9 100644 --- a/test/gtdot5.ijs +++ b/test/gtdot5.ijs @@ -41,7 +41,7 @@ EMPTY t1=: 3 : 0 ALL=: 0$0 -pyx=. (p1 t.'')"0 i. 300 +pyx=. (p1 t.'')"0 i. 100 1:&>pyx echo #~.ALL echo #/.~ ALL @@ -50,7 +50,7 @@ EMPTY t2=: 3 : 0 ALL=: 0 0$0 -pyx=. (p2 t.'')"0 i. 300 +pyx=. (p2 t.'')"0 i. 100 1:&>pyx echo #~.ALL echo #/.~ ALL @@ -74,13 +74,13 @@ end. ) t3=: 3 : 0 -pyx=. (p3 t.'')"0 i. 300 +pyx=. (p3 t.'')"0 i. 100 echo ;pyx EMPTY ) t4=: 3 : 0 -pyx=. (p4 t.'')"0 i. 300 +pyx=. (p4 t.'')"0 i. 100 echo ;pyx EMPTY )