Skip to content

Commit 529b013

Browse files
authored
Merge pull request #94 from JohanWiltink/main
custom NegaBinary numbers, plus necessary bugfixes
2 parents 5f09c6e + 3de99e7 commit 529b013

File tree

3 files changed

+182
-2
lines changed

3 files changed

+182
-2
lines changed

src/lambda-calculus.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class A {
5858
this.left = left;
5959
this.right = right;
6060
}
61-
free() { return union(this.left.free(),this.right.free()); }
61+
free() { return union( this.left.free?.() || [] , this.right.free?.() || [] ); }
6262
toString() {
6363
const left = this.left instanceof L ? `(${this.left})` : this.left ;
6464
const right = this.right instanceof V ? this.right : `(${this.right})` ;
@@ -271,7 +271,7 @@ function parse(code) {
271271
function a(i) {
272272
let q, r = [];
273273
let j = i;
274-
while ( q = paren_d(term)(j) || l(j) || v(j) || n(j) ) {
274+
while ( q = paren_d(term)(j) || l(j) || v(j) || n(j) || negative(n)(j) ) {
275275
[j,q] = q;
276276
r.push(q);
277277
}
@@ -280,6 +280,18 @@ function parse(code) {
280280
else
281281
return null;
282282
}
283+
const negative = number => function(i) {
284+
const j = expect('-')(i);
285+
if ( j ) {
286+
const q = number(j);
287+
if ( q ) {
288+
const [k,r] = q;
289+
return [k,fromInt(-toInt(r))];
290+
} else
291+
error(j,"negative: expected a number literal")
292+
} else
293+
return null;
294+
}
283295
const paren_d = inner => function(i) {
284296
const j = expect('(')(i);
285297
if ( j ) {

tests/negabinary-scott/solution.lc

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#debug
2+
3+
#import combinators.lc
4+
B = \ f g x . f (g x)
5+
C = \ f x y . f y x
6+
K = \ x _ . x
7+
T = \ x f . f x
8+
Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) )
9+
10+
#import church-boolean.lc
11+
True = \ true _false . true
12+
False = \ _true false . false
13+
14+
#import church-ordering.lc
15+
LT = \ lt _eq _gt . lt
16+
EQ = \ _lt eq _gt . eq
17+
GT = \ _lt _eq gt . gt
18+
19+
#import scott-pair.lc
20+
Pair = \ x y f . f x y
21+
fst = \ xy . xy \ x _y . x
22+
snd = \ xy . xy \ _x y . y
23+
bimap = \ fn xy . xy \ x y . Pair (fn x) (fn y)
24+
Y2 = B Y (C (B bimap T))
25+
26+
#import scott-triple.lc
27+
Triple = \ x y z f . f x y z
28+
fst3 = \ xyz . xyz \ x _y _z . x
29+
snd3 = \ xyz . xyz \ _x y _z . y
30+
thd3 = \ xyz . xyz \ _x _y z . z
31+
trimap = \ fn xyz . xyz \ x y z . Triple (fn x) (fn y) (fn z)
32+
Y3 = B Y (C (B trimap T))
33+
34+
#import scott-quad.lc
35+
Quad = \ w x y z f . f w x y z
36+
fst4 = \ wxyz . wxyz \ w _x _y _z . w
37+
snd4 = \ wxyz . wxyz \ _w x _y _z . x
38+
thd4 = \ wxyz . wxyz \ _w _x y _z . y
39+
fth4 = \ wxyz . wxyz \ _w _x _y z . z
40+
quadmap = \ fn wxyz . wxyz \ w x y z . Quad (fn w) (fn x) (fn y) (fn z)
41+
Y4 = B Y (C (B quadmap T))
42+
43+
#export scott-negabinary-number.lc
44+
45+
# NegaBinary = Zero | Bit0 NegaBinary | Bit1 NegaBinary
46+
47+
Bit0 = \ n . \ _end even _odd . even n
48+
Bit1 = \ n . \ _end _even odd . odd n
49+
50+
nega-dbl = \ n . n 0 (K (Bit0 n)) (K (Bit0 n))
51+
52+
Enum = Y2 (Pair (T \ succ pred . \ m . m 1 Bit1 (B nega-dbl pred)) # succ
53+
(T \ succ pred . \ m . m -1 (B Bit1 succ) nega-dbl) # pred
54+
)
55+
succ = fst Enum
56+
pred = snd Enum
57+
58+
Num = Y3 (Triple (T \ add adc adb .
59+
\ m n . m n # add
60+
( \ zm . n m ( \ zn . nega-dbl (add zm zn) ) ( \ zn . Bit1 (add zm zn) ) )
61+
( \ zm . n m ( \ zn . Bit1 (add zm zn) ) ( \ zn . nega-dbl (adb zm zn) ) )
62+
)
63+
(T \ add adc adb .
64+
\ m n . m (succ n) # add-with-carry
65+
( \ zm . n (succ m) ( \ zn . Bit1 (add zm zn) ) ( \ zn . nega-dbl (adb zm zn) ) )
66+
( \ zm . n (succ m) ( \ zn . nega-dbl (adb zm zn) ) ( \ zn . Bit1 (adb zm zn) ) )
67+
)
68+
(T \ add adc adb .
69+
\ m n . m (pred n) # add-with-borrow
70+
( \ zm . n (pred m) ( \ zn . Bit1 (adc zm zn) ) ( \ zn . nega-dbl (add zm zn) ) )
71+
( \ zm . n (pred m) ( \ zn . nega-dbl (add zm zn) ) ( \ zn . Bit1 (add zm zn) ) )
72+
) )
73+
add = fst3 Num
74+
adc = snd3 Num
75+
adb = thd3 Num
76+
77+
negate = \ n . add n (nega-dbl n)
78+
sub = \ m n . add m (negate n)
79+
zero = \ n . n True (K False) (K False)
80+
81+
Ord = Y4 (Quad (T \ lt0 le0 ge0 gt0 . \ n . n False gt0 gt0) # lt0
82+
(T \ lt0 le0 ge0 gt0 . \ n . n True ge0 gt0) # le0
83+
(T \ lt0 le0 ge0 gt0 . \ n . n True le0 le0) # ge0
84+
(T \ lt0 le0 ge0 gt0 . \ n . n False lt0 le0) # gt0
85+
)
86+
lt0 = fst4 Ord
87+
le0 = snd4 Ord
88+
ge0 = thd4 Ord
89+
gt0 = fth4 Ord
90+
91+
compare = \ m n . T (sub m n) \ d . zero d EQ (lt0 d LT GT) # church boolean

tests/negabinary-scott/test.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {readFileSync} from "fs";
2+
import {assert} from "chai";
3+
4+
import * as LC from "../../src/lambda-calculus.js";
5+
6+
const Zero = end => even => odd => end ;
7+
const Bit0 = n => end => even => odd => even(n) ;
8+
const Bit1 = n => end => even => odd => odd (n) ;
9+
const fromInt = n => n ? n&1 ? Bit1(fromInt(-(n>>1))) : Bit0(fromInt(-(n>>1))) : Zero ;
10+
const padded = m => m (false) ( n => n (true) ( () => padded(n) ) (padded) ) (padded) ;
11+
const unsafeToInt = m => m (0) ( n => - 2 * unsafeToInt(n) ) ( n => 1 - 2 * unsafeToInt(n) ) ;
12+
const toInt = n => {
13+
if ( padded(n) )
14+
throw new TypeError(`toInt: padded number ${ unsafeToInt(n) }`);
15+
else
16+
return unsafeToInt(n);
17+
} ;
18+
LC.configure({ purity: "LetRec", numEncoding: { fromInt, toInt }, verbosity: "Concise" });
19+
20+
const solutionText = readFileSync(new URL("./solution.lc", import.meta.url), {encoding: "utf8"});
21+
const solution = LC.compile(solutionText);
22+
const { succ,pred, add,negate,sub, zero, lt0,le0,ge0,gt0,compare } = solution;
23+
const { True,False, LT,EQ,GT } = solution;
24+
25+
const toBoolean = p => p (true) (false) ;
26+
const toOrdering = cmp => cmp ("LT") ("EQ") ("GT") ;
27+
28+
describe("NegaBinaryScott", () => {
29+
it("numbers", () => {
30+
for ( let n=-10; n<=10; n++ )
31+
assert.strictEqual( toInt(fromInt(n)), n, `toInt (fromInt ${ n })` );
32+
});
33+
it("succ", () => {
34+
for ( let n=-10; n<=10; n++ )
35+
assert.strictEqual( toInt(succ(fromInt(n))), n+1, `succ ${ n }` );
36+
// assert.strictEqual( toInt(pred(fromInt(n))), n-1, `pred ${ n }` );
37+
});
38+
it("pred", () => {
39+
for ( let n=-10; n<=10; n++ )
40+
// assert.strictEqual( toInt(succ(fromInt(n))), n+1, `succ ${ n }` ),
41+
assert.strictEqual( toInt(pred(fromInt(n))), n-1, `pred ${ n }` );
42+
});
43+
it("add", () => {
44+
for ( let m=-10; m<=10; m++ )
45+
for ( let n=-10; n<=10; n++ ) {
46+
const actual = toInt(add(fromInt(m))(fromInt(n)));
47+
assert.strictEqual(actual,m+n,`add ${ m } ${ n }`);
48+
}
49+
});
50+
it("negate", () => {
51+
for ( let n=-10; n<=10; n++ )
52+
assert.strictEqual( toInt(negate(fromInt(n))), -n, `negate ${ n }` );
53+
});
54+
it("sub", () => {
55+
for ( let m=-10; m<=10; m++ )
56+
for ( let n=-10; n<=10; n++ ) {
57+
const actual = toInt(sub(fromInt(m))(fromInt(n)));
58+
assert.strictEqual(actual,m-n,`sub ${ m } ${ n }`);
59+
}
60+
});
61+
it("eq, uneq", () => {
62+
for ( let n=-10; n<=10; n++ )
63+
assert.equal(toBoolean(zero(fromInt(n))),n===0,`zero ${ n }`),
64+
assert.equal(toBoolean(lt0(fromInt(n))),n<0,`lt0 ${ n }`),
65+
assert.equal(toBoolean(le0(fromInt(n))),n<=0,`le0 ${ n }`),
66+
assert.equal(toBoolean(ge0(fromInt(n))),n>=0,`ge0 ${ n }`),
67+
assert.equal(toBoolean(gt0(fromInt(n))),n>0,`gt0 ${ n }`);
68+
});
69+
it("compare", () => {
70+
for ( let m=-10; m<=10; m++ )
71+
for ( let n=-10; n<=10; n++ ) {
72+
const actual = toOrdering(compare(fromInt(m))(fromInt(n)));
73+
const expected = m > n ? "GT" : m < n ? "LT" : "EQ" ;
74+
assert.equal(actual,expected,`compare ${ m } ${ n }`);
75+
}
76+
});
77+
});

0 commit comments

Comments
 (0)