Standardise a Phinary Number
Javascript (ES6) - 446 418 422 420 bytes
Minified:
F=s=>{D=[];z='000000000';N=t=n=i=e=0;s=(z+s.replace(/^([^.]*)$/,'$1.')+z).replace(/~/g,'-').replace(/-?\d/g,s=>((D[n++]=s/1),0));for(;i<n-3;i=j){if(p=D[j=i+1]){if(!e&&p<0){D=D.map(k=>-k);N=~N;p=-p}e=1}d=D[i];x=D[i+2];m=D[i+3];if(p<0){d--;p++;x++;e=j=0}if(p>1){d++;m++;p-=2;e=j=0}if(!d&&p*x==1){d=p;e=j=p=x=0}D[i]=d;D[i+1]=p;D[i+2]=x;D[i+3]=m}return(N?'-':'')+s.replace(/0/g,()=>D[t++]).replace(/^(0(?!\.))+|0+$/g,'')}
Expanded:
F = s => {
D = [];
z = '000000000';
N = t = n = i = e = 0;
s = (z + s.replace( /^([^.]*)$/, '$1.' ) + z).replace( /~/g, '-' ).
replace( /-?\d/g, s => ((D[n++]=s/1),0) );
for( ; i < n-3; i = j ) {
if( p = D[j = i+1] ) {
if( !e && p < 0 ) {
D = D.map( k=>-k );
N = ~N;
p = -p;
}
e = 1;
}
d = D[i];
x = D[i+2];
m = D[i+3];
if( p < 0 ) {
d--;
p++;
x++;
e = j = 0;
}
if( p > 1 ) {
d++;
m++;
p-=2;
e = j = 0;
}
if( !d && p*x == 1 ) {
d = p;
e = j = p = x = 0;
}
D[i] = d;
D[i+1] = p;
D[i+2] = x;
D[i+3] = m;
}
return (N ? '-' : '') + s.replace( /0/g, ()=>D[t++] ).replace( /^(0(?!\.))+|0+$/g, '' );
}
The code produces a function F
that performs the specified conversion.
It's a tough problem to golf. Numerous edge cases creep up that prevent simplification of the code. In particular, dealing with negatives is a pain, both in terms of parsing and in terms of logical handling.
I should note that the code only handles a "reasonable range" of inputs. To extend the domain of the function without bound, the number of zeros in z
can be increased, and the constant bounding the while( c++ < 99 )
loop can be increased. The range currently supported is already overkill for the supplied test cases.
Sample Outputs
F('1') 1.
F('9') 10010.0101
F('1~3.2~1') -0.0101
F('0.~1021') -0.
F('105.~2') 1010.0101
F('~31~5.~1') -100000.1001
The -0.
isn't pretty, but the answer is still correct. I can fix it if necessary.
Haskell, 336 bytes
z=[0,0]
g[a,b]|a*b<0=g[b,a+b]
g x=x<z
k![a,b,c,d]=[b,a+b,d-c+read k,c]
p('.':s)=1:0:2`drop`p s
p('~':k:s)=['-',k]!p s
p(k:s)=[k]!p s
p[]=1:0:z
[1,0]&y='.':z?y
[a,b]&y=[b,a+b]?y
x@[a,b]?y@[c,d]|x==z,y==z=""|g y='-':x?[-c,-d]|g[c-1,d]='0':x&[d,c+d]|g[c,d-1]='1':x&[d,c+d-1]|0<1=[b-a,a]?[d-c,c]
m[a,b,c,d]=[1,0]?[a*d+b*c-a*c,a*c+b*d]
f=m.p
This is the greedy algorithm, but with an exact representation [a,b]
of numbers a + bφ (a, b ∈ ℤ) to avoid floating-point errors. g[a,b]
tests whether a + bφ < 0. Usage example:
*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."