\datethis @*Intro. This program is a revision of {\mc ACHAIN2} ({\it not\/} {\mc ACHAIN3}), which you should read first. After I found an unpublished preprint by D. Bleichenbacher and A.~Flammenkamp (1997) on the web, I realized that @^Flammenkamp, Achim@> @^Bleichenbacher, Daniel@> several changes would speed that program up significantly. The main changes here are: (i)~Links are maintained so that it's easy to skip past cases with large~|l[p]|. (ii)~When an odd number $p$ is inserted into the chain at position $j$, we make sure that $p\le\min(b[j-2]+b[j-1],b[j])$. Previously the weaker test $p\le b[j]$ was used. (iii)~Whenever we find a good chain for~$n$, we update the upper bounds for larger numbers, in case this chain implies a better way to compute them than was previously known. (The factor method, used previously, is just a special case of this technique.) One change I intentionally did {\it not\/} make: When trying to make |a[s]| be equal to |p+q| for some previous values |p| and~|q|, Flammenkamp and Bleichenbacher check to see whether appropriate |p| and~|q| are already present; if so, they accept |a[s]| and move on. Very plausible. But they don't implement a strong equivalence test with canonical chains, as I do; and I have not been able to verify that their ``move on'' heuristic is justifiable together with the strong cutoffs in my canonical approach, because of subtle ambiguities that arise in special cases. @d nmax 10000000 /* should be less than $2^{24}$ on a 32-bit machine */ @c #include #include #include unsigned char l[nmax+2]; int a[128],b[128]; unsigned int undo[128*128]; int ptr; /* this many items of the |undo| stack are in use */ struct { int lbp,ubp,lbq,ubq,r,ptrp,ptrq; } stack[128]; int tail[128],outdeg[128],outsum[128],limit[128]; FILE *infile, *outfile; int down[nmax]; /* a navigation aid discussed below */ int link[nmax]; /* stack links when propagating upper bounds */ int top; /* top element of the stack (or 1 when empty) */ int main(int argc, char* argv[]) { register int i,j,n,p,q,r,s,ubp,ubq=0,lbp,lbq,ptrp,ptrq; int lb,ub,timer=0; @; a[0]=b[0]=1, a[1]=b[1]=2; /* an addition chain always begins like this */ @; for (n=1;n; @; if (n%1000==0) { j=clock(); printf("%d..%d done in %.5g minutes\n", n-999,n,(double)(j-timer)/(60*CLOCKS_PER_SEC)); timer=j; } done: @; @; } } @ @= if (argc!=3) { fprintf(stderr,"Usage: %s infile outfile\n",argv[0]); exit(-1); } infile=fopen(argv[1],"r"); if (!infile) { fprintf(stderr,"I couldn't open `%s' for reading!\n",argv[1]); exit(-2); } outfile=fopen(argv[2],"w"); if (!outfile) { fprintf(stderr,"I couldn't open `%s' for writing!\n",argv[2]); exit(-3); } @ @= fprintf(outfile,"%c",l[n]+' '); fflush(outfile); /* make sure the result is viewable immediately */ @ At this point I compute the known lower bound $\lfloor\lg n\rfloor+\lceil\lg\min(\nu n,16)\rceil$. (With a change file, I could make the program set |l[n]=lb| and |goto done| if the input value is |' '| or more. This change would amount to believing that the input file has true values, thereby essentially restarting a computation that was only partly finished. Hmm, wait: Actually I should also use the factor method to update upper bounds, before going to |done|, if |l[n]| has changed.) @= for (q=n,i=-1,j=0;q;q>>=1,i++) j+=q&1; /* now $i=\lfloor\lg n\rfloor$ and $j=\nu n$ */ if (j>16) j=16; for (j--;j;j>>=1) i++; lb=fgetc(infile)-' '; /* |fgetc| will return a negative value after EOF */ if (lb= for (n=2,down[1]=1;n>1]+(n&1), l[n]=127; for (i=1,p=0;i*i=r+down[i]+down[j]) l[(1< } } for (n=1;n; @ Whenever we learn a better upper bound, we might as well broadcast all of its consequences, using the factor method. (The total number of times this happens for a particular number |n| is at most $\lg n$, and the time to propagate is proportional to |nmax/n|, so the total time for all these updates is at most $O(\log n)^2$ times |nmax|.) A linked stack is used to handle these updates. An element |p| is on the stack if and only if |link[p]!=0|, if and only |l[p]| has decreased since the last time we checked all of its multiples. @d upbound(p,x) if (l[p]>x) { l[p]=x; if (link[p]==0) link[p]=top,top=p; } @= top=1; /* start with empty stack */ for (i=4;i1) { p=top, top=link[p], link[p]=0; upbound(p+1,l[p]+1); upbound(p+2,l[p]+2); for (i=2;i*p @^Bleichenbacher, Daniel@> At the beginning of this step, |top=1|. We've allocated space for |l[nmax]| and |l[nmax+1]| in order to avoid making a special test here. @= for (j=1;j*n1) { p=top, top=link[p], link[p]=0; upbound(p+1,l[p]+1); upbound(p+2,l[p]+2); for (i=2;i*p= ub=l[n], l[n]=lb; while (lb=2;i--) { if ((a[i]<<1)>1; if (b[i]>=b[i+1]) b[i]=b[i+1]-1; } @; l[n]=++lb; } backtrackdone: @ One of the key operations we need is to increase |p| to the smallest element $p'>p$ that has $l[p']p$ that has $l[p']= { p++; if (l[p]==s) p=(down[p]>p? down[p]: nmax); } @ @=s|, increase |p| to the next element with |l[p]= do { if (down[p]>p) p=down[p]; else { p=nmax;@+break; } }@+while (l[p]>=s); @ I can't help exclaiming that this little algorithm is quite pretty. @= if (l[n]l[n];p=q) q=down[p], down[p]=n; down[n]=p; } @ We maintain a stack of subproblems, and a stack for undoing, as in {\mc ACHAIN2} and its predecessors. @= ptr=0; /* clear the |undo| stack */ for (r=s=lb;s>2;s--) { if (outdeg[s]==1) limit[s]=a[s]-tail[outsum[s]];@+ else limit[s]=a[s]-1; /* the max feasible |p| */ if (limit[s]>b[s-1]) limit[s]=b[s-1]; @; while (p<=limit[s]) { @; ptrp=ptr; for (; ubp>=lbp; ubp--) { @; if (p==q) goto happiness; if (ubq>=ubp) ubq=ubp-1; ptrq=ptr; for (; ubq>=lbq; ubq--) { @; happiness: @; goto onward; /* now |a[s]| is covered; try to cover |a[s-1]| */ backup: s++; if (s>lb) goto impossible; @; if (p==q) goto failp; failq:@+ while (ptr>ptrq) @; } /* end loop on |ubq| */ failp:@+ while (ptr>ptrp) @; } /* end loop on |ubp| */ failpq: @; } /* end loop on |p| */ goto backup; onward: continue; } /* end loop on |s| */ @; goto backtrackdone; impossible:@; @ At this point we have |a[k]=b[k]| for all $r\le k\le|lb|$. @= if (a[s]&1) { /* necessarily |p!=q| */ unequal:@+if (outdeg[s-1]==0) q=a[s]/3;@+else q=a[s]>>1; if (q>b[s-2]) q=b[s-2]; p=a[s]-q; if (l[p]>=s) { @=s|,...@>; q=a[s]-p; } }@+else { p=q=a[s]>>1; if (l[p]>=s) goto unequal; /* a rare case like |l[191]=l[382]| */ } if (p>limit[s]) goto backup; for (;r>2&&a[r-1]==b[r-1];r--); if (p>b[r-1]) { /* now |ra[r]) r++; /* this step keeps |rb[r-2]) { if (a[r]<=a[s]-b[r-2]) p=a[r],q=b[s]-p; else q=b[r-2],p=a[s]-q; } @ @= if (p==q) { if (outdeg[s-1]==0) q=(a[s]/3)+1; /* will be decreased momentarily */ if (q>b[s-2]) q=b[s-2];@+ else q--; p=a[s]-q; if (l[p]>=s) { @=s|,...@>; q=a[s]-p; } }@+else { @; q=a[s]-p; } if (q>2) { if (a[s-1]==b[s-1]) { /* maybe |p| has to be present already */ doublecheck:@+while (pb[r-1]) { while (p>a[r]) r++; p=a[r],q=a[s]-p; /* possibly |r=s| now */ }@+else if (q>b[r-2]) { if (a[r]<=a[s]-b[r-2]) p=a[r],q=b[s]-p; else q=b[r-2],p=a[s]-q; } } if (ubq>=s) ubq=s-1; while (q>=a[ubq+1]) ubq++; while (qb[ubq]) { q=b[ubq],p=a[s]-q; if (a[s-1]==b[s-1]) goto doublecheck; } } @ @= tail[s]=q, stack[s].r=r; outdeg[ubp]++, outsum[ubp]+=s; outdeg[ubq]++, outsum[ubq]+=s; stack[s].lbp=lbp,stack[s].ubp=ubp; stack[s].lbq=lbq,stack[s].ubq=ubq; stack[s].ptrp=ptrp,stack[s].ptrq=ptrq; @ @= ptrq=stack[s].ptrq,ptrp=stack[s].ptrp; lbq=stack[s].lbq,ubq=stack[s].ubq; lbp=stack[s].lbp,ubp=stack[s].ubp; outdeg[ubq]--, outsum[ubq]-=s; outdeg[ubp]--, outsum[ubp]-=s; q=tail[s], p=a[s]-q, r=stack[s].r; @ After the test in this step is passed, we'll have |ubp>ubq| and |lbp>lbq|. @= if (l[p]>=s) goto failpq; lbp=l[p]; while (b[lbp]b[lbp-2]+b[lbp-1]) { if (++lbp>=s) goto failpq; } if (a[lbp]>p) goto failpq; for (ubp=lbp;a[ubp+1]<=p;ubp++); if (ubp==s-1) lbp=ubp; if (p==q) lbq=lbp,ubq=ubp; else { lbq=l[q]; if (lbq>=ubp) goto failpq; while (b[lbq]b[lbq-2]+b[lbq-1]) lbq++; if (lbq>=ubp) goto failpq; if (a[lbq]>q) goto failpq; if (lbp<=lbq) lbp=lbq+1; while ((q<<(lbp-lbq))ubp) goto failpq; } for (ubq=lbq;a[ubq+1]<=q && (q<<(ubp-ubq-1))>=p;ubq++); } @ The undoing mechanism is very simple: When changing |a[j]|, we put |(j<<24)+x| on the |undo| stack, where |x| was the former value. Similarly, when changing |b[j]|, we stack the value |(1<<31)+(j<<24)+x|. @d newa(j,y) undo[ptr++]=(j<<24)+a[j], a[j]=y @d newb(j,y) undo[ptr++]=(1<<31)+(j<<24)+b[j], b[j]=y @= { i=undo[--ptr]; if (i>=0) a[i>>24]=i&0xffffff; else b[(i&0x3fffffff)>>24]=i&0xffffff; } @ At this point we know that $a[ubp]\le p\le b[ubp]$. @= if (a[ubp]!=p) { newa(ubp,p); for (j=ubp-1;(a[j]<<1)>1; if (i>b[j]) goto failp; newa(j,i); } for (j=ubp+1;a[j]<=a[j-1];j++) { i=a[j-1]+1; if (i>b[j]) goto failp; newa(j,i); } } if (b[ubp]!=p) { newb(ubp,p); for (j=ubp-1;b[j]>=b[j+1];j--) { i=b[j+1]-1; if (ib[j-1]<<1;j++) { i=b[j-1]<<1; if (i; @ If, say, we've just set |a[8]=b[8]=132|, special considerations apply, because the only addition chains of length~8 for 132 are $$\eqalign{ &1,2,4,8,16,32,64,128,132;\cr &1,2,4,8,16,32,64,68,132;\cr &1,2,4,8,16,32,64,66,132;\cr &1,2,4,8,16,32,34,66,132;\cr &1,2,4,8,16,32,33,66,132;\cr &1,2,4,8,16,17,33,66,132.\cr}$$ The values of |a[4]| and |b[4]| must therefore be 16; and then, of course, we also must have |a[3]=b[3]=8|, etc. Similar reasoning applies whenever we set $a[j]=b[j]=2^j+2^k$ for $k\le j-4$. Such cases may seem extremely special. But my hunch is that they are important, because efficient chains need such values. When we try to prove that no efficient chain exists, we want to show that such values can't be present. Numbers with small |l[p]| are harder to rule out, so it should be helpful to penalize them. @= i=p-(1<<(ubp-1)); if (i && ((i&(i-1))==0) && (i<<4)>=1,j--); if (b[j]<(1<= if (a[ubq]!=q) { if (a[ubq]>q) goto failq; newa(ubq,q); for (j=ubq-1;(a[j]<<1)>1; if (i>b[j]) goto failq; newa(j,i); } for (j=ubq+1;a[j]<=a[j-1];j++) { i=a[j-1]+1; if (i>b[j]) goto failq; newa(j,i); } } if (b[ubq]!=q) { if (b[ubq]=b[j+1];j--) { i=b[j+1]-1; if (ib[j-1]<<1;j++) { i=b[j-1]<<1; if (i; @ @= i=q-(1<<(ubq-1)); if (i && ((i&(i-1))==0) && (i<<4)>=1,j--); if (b[j]<(1<