diff --git a/example/test.js b/example/test.js index 79db78d..b97fd50 100644 --- a/example/test.js +++ b/example/test.js @@ -4,9 +4,7 @@ chaiConfig.truncateThreshold = 0; import * as LC from "@codewars/lambda-calculus"; import { solution } from "./files.js"; // /workspace/files.js -LC.config.purity = "Let"; -LC.config.numEncoding = "Church"; -const toInt = LC.toIntWith(LC.config); +LC.configure({ purity: "Let", numEncoding: "Church" }); const { counter } = LC.compile(solution()); const T = t => _ => t; @@ -14,8 +12,8 @@ const F = _ => f => f; describe("counter", () => { it("fixed tests", () => { - assert.strictEqual(toInt(counter(T)(T)(T)(F)), 3); - assert.strictEqual(toInt(counter(T)(F)), 1); - assert.strictEqual(toInt(counter(T)(T)(T)(T)(T)(T)(T)(F)), 7); + assert.equal( counter(T)(T)(T)(F), 3); + assert.equal( counter(T)(F), 1); + assert.equal( counter(T)(T)(T)(T)(T)(T)(T)(F), 7); }); }); diff --git a/src/lambda-calculus.js b/src/lambda-calculus.js index 2d45d75..bc6976a 100644 --- a/src/lambda-calculus.js +++ b/src/lambda-calculus.js @@ -16,11 +16,13 @@ Kacarott - https://github.com/Kacarott */ // Default options -export const config = { verbosity: "Calm" // Calm | Concise | Loquacious | Verbose - , purity: "Let" // Let | LetRec | PureLC - , numEncoding: "Church" // None | Church | Scott | BinaryScott +const config = { verbosity: "Calm" // Calm | Concise | Loquacious | Verbose + , purity: "PureLC" // Let | LetRec | PureLC + , numEncoding: "None" // None | Church | Scott | BinaryScott }; +export function configure(cfg={}) { return Object.assign(config,cfg); } + function union(left, right) { const r = new Set(left); for ( const name of right ) r.add(name); @@ -98,238 +100,217 @@ primitives.setThunk( "trace", new Tuple( Primitive( function(v) { console.info(S const Y = new L("f",new A(new L("x",new A(new V("f"),new A(new V("x"),new V("x")))),new L("x",new A(new V("f"),new A(new V("x"),new V("x")))))); -export function fromInt(n) { return fromIntWith()(n); } - -export function fromIntWith(cfg={}) { - const {numEncoding,verbosity} = Object.assign( {}, config, cfg ); - return function fromInt(n) { - if ( numEncoding === "Church" ) - if ( n >= 0 ) - return new L("s", new L("z", Array(n).fill().reduce( s => new A(new V("s"), s), new V("z")) )); - else { - if ( verbosity >= "Concise" ) console.error(`fromInt.Church: negative number ${ n }`); - throw new RangeError; - } - else if ( numEncoding === "Scott" ) // data Int = Zero | Succ Int - if ( n >= 0 ) - return new Array(n).fill().reduce( v => new L('_', new L('f', new A(new V('f'), v))), new L('z', new L('_', new V('z'))) ); - else { - if ( verbosity >= "Concise" ) console.error(`fromInt.Scott: negative number ${ n }`); - throw new RangeError; - } - else if ( numEncoding === "BinaryScott" ) // data Int = End | Even Int | Odd Int // LittleEndian, padding ( trailing ) 0 bits are out of spec - behaviour is undefined - if ( n >= 0 ) { - const zero = new L('z', new L('_', new L('_', new V('z')))); - return n ? n.toString(2).split("").reduce( (z,bit) => new L('_', new L('f', new L('t', new A(new V( bit==='0' ? 'f' : 't' ), z)))), zero ) : zero ; - } else { - if ( verbosity >= "Concise" ) console.error(`fromInt.BinaryScott: negative number ${ n }`); - throw new RangeError; - } - else if ( numEncoding === "None" ) { - if ( verbosity >= "Concise" ) console.error(`fromInt.None: number ${ n }`); - throw new EvalError; - } else - return numEncoding.fromInt(n); // Custom encoding - } ; +export function fromInt(n) { + if ( config.numEncoding === "Church" ) + if ( n >= 0 ) + return new L("s", new L("z", Array(n).fill().reduce( s => new A(new V("s"), s), new V("z")) )); + else { + if ( config.verbosity >= "Concise" ) console.error(`fromInt.Church: negative number ${ n }`); + throw new RangeError; + } + else if ( config.numEncoding === "Scott" ) // data Int = Zero | Succ Int + if ( n >= 0 ) + return new Array(n).fill().reduce( v => new L('_', new L('f', new A(new V('f'), v))), new L('z', new L('_', new V('z'))) ); + else { + if ( config.verbosity >= "Concise" ) console.error(`fromInt.Scott: negative number ${ n }`); + throw new RangeError; + } + else if ( config.numEncoding === "BinaryScott" ) // data Int = End | Even Int | Odd Int // LittleEndian, padding ( trailing ) 0 bits are out of spec - behaviour is undefined + if ( n >= 0 ) { + const zero = new L('z', new L('_', new L('_', new V('z')))); + return n ? n.toString(2).split("").reduce( (z,bit) => new L('_', new L('f', new L('t', new A(new V( bit==='0' ? 'f' : 't' ), z)))), zero ) : zero ; + } else { + if ( config.verbosity >= "Concise" ) console.error(`fromInt.BinaryScott: negative number ${ n }`); + throw new RangeError; + } + else if ( config.numEncoding === "None" ) { + if ( config.verbosity >= "Concise" ) console.error(`fromInt.None: number ${ n }`); + throw new EvalError; + } else + return config.numEncoding.fromInt(n); // Custom encoding } -export function toInt(term) { return toIntWith()(term); } - -export function toIntWith(cfg={}) { - const {numEncoding,verbosity} = Object.assign( {}, config, cfg ); - return function toInt(term) { - try { - if ( numEncoding === "Church" ) { - return term ( x => x+1 ) ( Primitive(0) ); - } else if ( numEncoding === "Scott" ) { - let result = 0, evaluating = true; - while ( evaluating ) - term ( () => evaluating = false ) ( n => () => { term = n; result++ } ) (); - return result; - } else if ( numEncoding === "BinaryScott" ) { - let result = 0, bit = 1, evaluating = true; - while ( evaluating ) - term ( () => evaluating = false ) ( n => () => { term = n; bit *= 2 } ) ( n => () => { term = n; result += bit; bit *= 2 } ) (); - return result; - } else if ( numEncoding === "None" ) - return term; - else - return numEncoding.toInt(term); // Custom encoding - } catch (e) { - if ( verbosity >= "Concise" ) console.error(`toInt: ${ term } is not a number in numEncoding ${ numEncoding }`); - throw e; - } - } ; +export function toInt(term) { + try { + if ( config.numEncoding === "Church" ) { + return term ( x => x+1 ) ( Primitive(0) ); + } else if ( config.numEncoding === "Scott" ) { + let result = 0, evaluating = true; + while ( evaluating ) + term ( () => evaluating = false ) ( n => () => { term = n; result++ } ) (); + return result; + } else if ( config.numEncoding === "BinaryScott" ) { + let result = 0, bit = 1, evaluating = true; + while ( evaluating ) + term ( () => evaluating = false ) ( n => () => { term = n; bit *= 2 } ) ( n => () => { term = n; result += bit; bit *= 2 } ) (); + return result; + } else if ( config.numEncoding === "None" ) + return term; + else + return numEncoding.toInt(term); // Custom encoding + } catch (e) { + if ( config.verbosity >= "Concise" ) console.error(`toInt: ${ term } is not a number in numEncoding ${ numEncoding }`); + throw e; + } } // parse :: String -> Env { String => Term } -function parse(code) { return parseWith()(code); } - -function parseWith(cfg={}) { - const {numEncoding,purity,verbosity} = Object.assign( {}, config, cfg ); - const fromInt = fromIntWith({numEncoding,purity,verbosity}); - return function parse(code) { - function parseTerm(env,code) { - function wrap(name,term) { - const FV = term.free(); FV.delete("()"); - if ( purity === "Let" ) - return Array.from(FV).reduce( (tm,nm) => { - if ( env.has(nm) ) { - if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); - tm.env.set( nm, env.get(nm) ); - return tm; - } else { - if ( verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); - if ( nm === name ) - throw new ReferenceError(`undefined free variable ${ nm }: direct recursive calls are not supported in Let mode`); - else - throw new ReferenceError(`undefined free variable ${ nm }`); - } - } , new Tuple( term, new Env ) ); - else if ( purity==="LetRec" ) - return Array.from(FV).reduce( (tm,nm) => { +function parse(code) { + function parseTerm(env,code) { + function wrap(name,term) { + const FV = term.free(); FV.delete("()"); + if ( config.purity === "Let" ) + return Array.from(FV).reduce( (tm,nm) => { + if ( env.has(nm) ) { + if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); + tm.env.set( nm, env.get(nm) ); + return tm; + } else { + if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); if ( nm === name ) - return tm; - else if ( env.has(nm) ) { - if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); - tm.env.set( nm, env.get(nm) ); - return tm; - } else { - if ( verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); + throw new ReferenceError(`undefined free variable ${ nm }: direct recursive calls are not supported in Let mode`); + else throw new ReferenceError(`undefined free variable ${ nm }`); - } - } , new Tuple( FV.has(name) ? new A(Y,new L(name,term)) : term , new Env ) ); - else if ( purity==="PureLC" ) - if ( FV.size ) { - if ( verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); - throw new EvalError(`unresolvable free variable(s) ${ Array.from(FV) }: all expressions must be closed in PureLC mode`); - } else - return new Tuple( term, new Env ); - else - throw new RangeError(`config.purity: unknown setting "${ purity }"`); - } - const letters = /[a-z]/i; - const digits = /\d/; - const identifierChars = /[-'\w]/; - const whitespace = /\s/; - function error(i,msg) { - console.error(code); - console.error(' '.repeat(i) + '^'); - console.error(msg + " at position " + i); - throw new SyntaxError(msg); - } - function sp(i) { while ( whitespace.test( code[i] || "" ) ) i++; return i; } - const expect = c => function(i) { return code[i]===c ? sp(i+1) : 0 ; } ; - function name(i) { - if ( code[i]==='_' ) { - while ( identifierChars.test( code[i] || "" ) ) i++; - return [sp(i),"_"]; - } else if ( letters.test( code[i] || "" ) ) { - let j = i+1; - while ( identifierChars.test( code[j] || "" ) ) j++; - return [sp(j),code.slice(i,j)]; - } else - return null; - } - function n(i) { - if ( digits.test(code[i]) ) { - let j = i+1; - while ( digits.test(code[j]) ) j++; - return [sp(j),fromInt(Number(code.slice(i,j)))]; - } else - return null; - } - function v(i) { - const r = name(i); - if ( r ) { - const [j,name] = r; - return [j,new V(name)]; - } else - return null; - } - function l(i) { - const j = expect('\\')(i); - if ( j ) { - let arg, args = []; - let k = j; - while ( arg = name(k) ) { - [k,arg] = arg; - args.push(arg); } - if ( args.length ) { - const l = expect('.')(k) || error(k,"lambda: expected '.'") ; - const [m,body] = term(l) || error(l,"lambda: expected a lambda calculus term") ; - return [ m, args.reduceRight( (body,arg) => new L(arg,body) , body ) ]; - } else - error(k,"lambda: expected at least one named argument"); - } else - return null; - } - function a(i) { - let q, r = []; - let j = i; - while ( q = paren_d(term)(j) || l(j) || v(j) || n(j) ) { - [j,q] = q; - r.push(q); - } - if ( r.length ) - return [ j, r.reduce( (z,term) => new A(z,term) ) ]; - else - return null; - } - const paren_d = inner => function(i) { - const j = expect('(')(i); - if ( j ) { - const q = inner(j); - if ( q ) { - const [k,r] = q; - const l = expect(')')(k) || error(k,"paren_d: expected ')'") ; - return [l,r]; + } , new Tuple( term, new Env ) ); + else if ( config.purity==="LetRec" ) + return Array.from(FV).reduce( (tm,nm) => { + if ( nm === name ) + return tm; + else if ( env.has(nm) ) { + if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); + tm.env.set( nm, env.get(nm) ); + return tm; } else { - const k = expect(')')(j) || error(j,"paren_d: expected ')'") ; - const undefinedTerm = new V("()"); - undefinedTerm.defName = name(0)[1]; - return [k, undefinedTerm]; + if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); + throw new ReferenceError(`undefined free variable ${ nm }`); } + } , new Tuple( FV.has(name) ? new A(Y,new L(name,term)) : term , new Env ) ); + else if ( config.purity==="PureLC" ) + if ( FV.size ) { + if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); + throw new EvalError(`unresolvable free variable(s) ${ Array.from(FV) }: all expressions must be closed in PureLC mode`); + } else + return new Tuple( term, new Env ); + else + throw new RangeError(`config.purity: unknown setting "${ purity }"`); + } + const letters = /[a-z]/i; + const digits = /\d/; + const identifierChars = /[-'\w]/; + const whitespace = /\s/; + function error(i,msg) { + console.error(code); + console.error(' '.repeat(i) + '^'); + console.error(msg + " at position " + i); + throw new SyntaxError(msg); + } + function sp(i) { while ( whitespace.test( code[i] || "" ) ) i++; return i; } + const expect = c => function(i) { return code[i]===c ? sp(i+1) : 0 ; } ; + function name(i) { + if ( code[i]==='_' ) { + while ( identifierChars.test( code[i] || "" ) ) i++; + return [sp(i),"_"]; + } else if ( letters.test( code[i] || "" ) ) { + let j = i+1; + while ( identifierChars.test( code[j] || "" ) ) j++; + return [sp(j),code.slice(i,j)]; + } else + return null; + } + function n(i) { + if ( digits.test(code[i]) ) { + let j = i+1; + while ( digits.test(code[j]) ) j++; + return [sp(j),fromInt(Number(code.slice(i,j)))]; + } else + return null; + } + function v(i) { + const r = name(i); + if ( r ) { + const [j,name] = r; + return [j,new V(name)]; + } else + return null; + } + function l(i) { + const j = expect('\\')(i); + if ( j ) { + let arg, args = []; + let k = j; + while ( arg = name(k) ) { + [k,arg] = arg; + args.push(arg); + } + if ( args.length ) { + const l = expect('.')(k) || error(k,"lambda: expected '.'") ; + const [m,body] = term(l) || error(l,"lambda: expected a lambda calculus term") ; + return [ m, args.reduceRight( (body,arg) => new L(arg,body) , body ) ]; } else - return null; - } ; - const term = a; - function defn(i) { - const [j,nm] = name(i) || error(i,"defn: expected a name") ; - const k = expect('=')(j) || error(j,"defn: expected '='") ; - const [l,tm] = term(k) || error(k,"defn: expected a lambda calculus term") ; - return [l,[nm,tm]]; + error(k,"lambda: expected at least one named argument"); + } else + return null; + } + function a(i) { + let q, r = []; + let j = i; + while ( q = paren_d(term)(j) || l(j) || v(j) || n(j) ) { + [j,q] = q; + r.push(q); } - const [i,r] = defn(0); - if ( i===code.length ) { - const [name,term] = r; - if ( config.verbosity >= "Loquacious" ) - console.debug(`compiled ${ name }${ config.verbosity >= "Verbose" ? ` = ${ term }` : "" }`); - return env.setThunk( name, wrap(name,term)); + if ( r.length ) + return [ j, r.reduce( (z,term) => new A(z,term) ) ]; + else + return null; + } + const paren_d = inner => function(i) { + const j = expect('(')(i); + if ( j ) { + const q = inner(j); + if ( q ) { + const [k,r] = q; + const l = expect(')')(k) || error(k,"paren_d: expected ')'") ; + return [l,r]; + } else { + const k = expect(')')(j) || error(j,"paren_d: expected ')'") ; + const undefinedTerm = new V("()"); + undefinedTerm.defName = name(0)[1]; + return [k, undefinedTerm]; + } } else - error(i,"defn: incomplete parse"); + return null; + } ; + const term = a; + function defn(i) { + const [j,nm] = name(i) || error(i,"defn: expected a name") ; + const k = expect('=')(j) || error(j,"defn: expected '='") ; + const [l,tm] = term(k) || error(k,"defn: expected a lambda calculus term") ; + return [l,[nm,tm]]; } - return code.replace( /#.*$/gm, "" ) // ignore comments - .replace( /\n(?=\s)/g, "" ) // continue lines - .split( '\n' ) // split lines - .filter( term => /\S/.test(term) ) // skip empty lines - .reduce(parseTerm, new Env(primitives)); // parse lines + const [i,r] = defn(0); + if ( i===code.length ) { + const [name,term] = r; + if ( config.verbosity >= "Loquacious" ) + console.debug(`compiled ${ name }${ config.verbosity >= "Verbose" ? ` = ${ term }` : "" }`); + return env.setThunk( name, wrap(name,term)); + } else + error(i,"defn: incomplete parse"); } + return code.replace( /#.*$/gm, "" ) // ignore comments + .replace( /\n(?=\s)/g, "" ) // continue lines + .split( '\n' ) // split lines + .filter( term => /\S/.test(term) ) // skip empty lines + .reduce(parseTerm, new Env(primitives)); // parse lines } -export function compile(code) { return compileWith()(code); } - -export function compileWith(cfg={}) { - const {numEncoding,purity,verbosity} = Object.assign( {}, config, cfg ); - return function compile(code) { - if (typeof code !== "string" || !code) throw new TypeError("missing code"); - const env = parseWith({numEncoding,purity,verbosity})(code); - const r = {}; - for ( const [name] of env ) - Object.defineProperty( r, name, { get() { return evalLC(new Tuple(new V(name), env)); }, enumerable: true } ); - return r; - } ; +export function compile(code) { + if ( typeof code !== "string" || ! code ) throw new TypeError("missing code"); + const env = parse(code); + const r = {}; + for ( const [name] of env ) + Object.defineProperty( r, name, { get() { return evalLC(new Tuple(new V(name), env)); }, enumerable: true } ); + return r; } // Top level call, term :: Tuple @@ -341,7 +322,7 @@ function evalLC(term) { function result(arg) { let argEnv; if ( arg?.term && arg?.env ) ({ term: arg, env: argEnv } = arg); // if callback is passed another callback, or a term - const termVal = new Tuple( typeof arg !== 'number' ? arg : fromInt(arg) , new Env(argEnv) ); + const termVal = new Tuple( typeof arg === 'number' ? fromInt(arg) : arg , new Env(argEnv) ); return runEval( new Tuple(term.body, new Env(env).setThunk(term.name, termVal)), stack ); } return Object.assign( result, {term,env} ); diff --git a/tests/basics-binary-scott/test.js b/tests/basics-binary-scott/test.js index a265a82..48a31da 100644 --- a/tests/basics-binary-scott/test.js +++ b/tests/basics-binary-scott/test.js @@ -2,13 +2,11 @@ import {readFileSync} from "fs"; import {assert} from "chai"; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "BinaryScott"; +LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const solution = LC.compile(solutionText); -const fromInt = LC.fromIntWith(LC.config); -const toInt = LC.toIntWith(LC.config); +const {fromInt,toInt} = LC; const {False,True,not,and,or,xor,implies} = solution; const {LT,EQ,GT,compare,lt,le,eq,ge,gt} = solution; @@ -27,220 +25,196 @@ const refGCD = m => n => n ? refGCD(n)(m%n) : m ; describe("Binary Scott tests",function(){ this.timeout(0); it("enumeration",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); const one = succ(zero) const two = succ(one) const three = succ(two) const four = succ(three) const five = succ(four) - assert.strictEqual( toString(zero), "$" ); - assert.strictEqual( toString(one), "1$" ); - assert.strictEqual( toString(two), "01$" ); - assert.strictEqual( toString(three), "11$" ); - assert.strictEqual( toString(four), "001$" ); - assert.strictEqual( toString(five), "101$" ); - assert.strictEqual( toString(five), "101$" ); - assert.strictEqual( toString(pred(five)), "001$" ); - assert.strictEqual( toString(unpad(pred(pred(five)))), "11$" ); - assert.strictEqual( toString(unpad(pred(pred(pred(five))))), "01$" ); - assert.strictEqual( toString(unpad(pred(pred(pred(pred(five)))))), "1$" ); - assert.strictEqual( toString(unpad(pred(pred(pred(pred(pred(five))))))), "$" ); + assert.equal( toString(zero), "$" ); + assert.equal( toString(one), "1$" ); + assert.equal( toString(two), "01$" ); + assert.equal( toString(three), "11$" ); + assert.equal( toString(four), "001$" ); + assert.equal( toString(five), "101$" ); + assert.equal( toString(five), "101$" ); + assert.equal( toString(pred(five)), "001$" ); + assert.equal( toString(unpad(pred(pred(five)))), "11$" ); + assert.equal( toString(unpad(pred(pred(pred(five))))), "01$" ); + assert.equal( toString(unpad(pred(pred(pred(pred(five)))))), "1$" ); + assert.equal( toString(unpad(pred(pred(pred(pred(pred(five))))))), "$" ); }); it("successor",()=>{ - let n = zero; + let n = 0; for ( let i=1; i<=100; i++ ) { n = succ (n); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ i } <- ${ toString(n) }`); - assert.strictEqual( toInt(n), i ); + assert.equal( n, i ); } }); it("predecessor",()=>{ - let n = fromInt(100); + let n = 100; for ( let i=100; i--; ) { n = pred (n); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ i } <- ${ toString(n) }`); - assert.strictEqual( toInt(n), i ); + assert.equal( n, i ); } }); it("predecessor robustness",()=>{ - if ( LC.config.verbosity >= "Loquacious" ) console.log(`pred 01$ -> 1$`); - assert.strictEqual( toString( pred ( fromInt(2) ) ), "1$" ); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`pred $ -> $`); - assert.strictEqual( toString( pred ( end => even => odd => end ) ), "$" ); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`pred 0$ -> $`); - assert.strictEqual( toString( pred ( end => even => odd => even ( - end => even => odd => end ) ) ), "$" ); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`pred 00$ -> $`); - assert.strictEqual( toString( pred ( end => even => odd => even ( - end => even => odd => even ( - end => even => odd => end ) ) ) ), "$" ); + assert.equal( toString( pred ( 2 ) ), "1$" ); + assert.equal( toString( pred ( end => even => odd => end ) ), "$" ); + assert.equal( toString( pred ( end => even => odd => even ( + end => even => odd => end ) ) ), "$" ); + assert.equal( toString( pred ( end => even => odd => even ( + end => even => odd => even ( + end => even => odd => end ) ) ) ), "$" ); }); it("ordering",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`compare ${ m } ${ n }`); - assert.strictEqual( compare (fromInt(m)) (fromInt(n)) ("-1") ("0") ("1"), String(Number(m>n) - Number(mn) - Number(m{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`compare ${ m } ${ n }`); - assert.strictEqual( lt (fromInt(m)) (fromInt(n)) (false)(true), m < n ); - assert.strictEqual( le (fromInt(m)) (fromInt(n)) (false)(true), m <= n ); - assert.strictEqual( eq (fromInt(m)) (fromInt(n)) (false)(true), m == n ); - assert.strictEqual( ge (fromInt(m)) (fromInt(n)) (false)(true), m >= n ); - assert.strictEqual( gt (fromInt(m)) (fromInt(n)) (false)(true), m > n ); - assert.strictEqual( eq (fromInt(m)) (fromInt(m)) (false)(true), true ); + assert.equal( lt (fromInt(m)) (fromInt(n)) (false)(true), m < n ); + assert.equal( le (fromInt(m)) (fromInt(n)) (false)(true), m <= n ); + assert.equal( eq (fromInt(m)) (fromInt(n)) (false)(true), m == n ); + assert.equal( ge (fromInt(m)) (fromInt(n)) (false)(true), m >= n ); + assert.equal( gt (fromInt(m)) (fromInt(n)) (false)(true), m > n ); + assert.equal( eq (fromInt(m)) (fromInt(m)) (false)(true), true ); } }); it("addition",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } + ${ n } = ${ m+n }`); - assert.strictEqual( toInt( plus (fromInt(m)) (fromInt(n)) ), m + n ); + assert.equal( plus (m) (n), m + n ); } }); it("multiplication",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } * ${ n } = ${ m*n }`); - assert.strictEqual( toInt( times (fromInt(m)) (fromInt(n)) ), m * n ); + assert.equal( times (m) (n), m * n ); } }); it("subtraction",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`subtract ${ m } ${ n }`); - assert.strictEqual( toInt( minus (fromInt(m)) (fromInt(n)) ), Math.max( 0, m - n ) ); - assert.strictEqual( toInt( minus (fromInt(n)) (fromInt(m)) ), Math.max( 0, n - m ) ); + assert.equal( minus (m) (n), Math.max( 0, m - n ) ); + assert.equal( minus (n) (m), Math.max( 0, n - m ) ); } }); it("division",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`division ${ m } ${ n }`); - assert.deepEqual( toPair( divMod (fromInt(m)) (fromInt(n||1)) ).map(toInt), [ m/(n||1)|0, m%(n||1) ] ); - assert.deepEqual( toPair( divMod (fromInt(n)) (fromInt(m||1)) ).map(toInt), [ n/(m||1)|0, n%(m||1) ] ); + assert.deepEqual( toPair( divMod (m) (n||1) ).map(toInt), [ m/(n||1)|0, m%(n||1) ] ); + assert.deepEqual( toPair( divMod (n) (m||1) ).map(toInt), [ n/(m||1)|0, n%(m||1) ] ); } }); it("exponentiation",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i), n = rnd(i%10); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } ** ${ n } = ${ m**n }`); - assert.strictEqual( toInt( pow (fromInt(m)) (fromInt(n)) ), m ** n ); + assert.equal( pow (m) (n), m ** n ); } }); it("greatest common divisor",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i), n = rnd(i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`gcd ${ m } ${ n } = ${ refGCD(m)(n) }`); - assert.strictEqual( toInt( gcd (fromInt(m)) (fromInt(n)) ), refGCD(m)(n) ); + assert.equal( gcd (m) (n), refGCD(m)(n) ); } }); it("least common multiple",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i), n = rnd(i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`lcm ${ m } ${ n } = ${ m/(refGCD(m)(n)||1)*n }`); - assert.strictEqual( toInt( lcm (fromInt(m)) (fromInt(n)) ), m / (refGCD(m)(n)||1) * n ); + assert.equal( lcm (m) (n), m / (refGCD(m)(n)||1) * n ); } }); it("minimum",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`min ${ m } ${ n } = ${ Math.min(m,n) }`); - assert.strictEqual( toInt( min (fromInt(m)) (fromInt(n)) ), Math.min(m,n) ); + assert.equal( min (m) (n), Math.min(m,n) ); } }); it("maximum",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`max ${ m } + ${ n } = ${ Math.max(m,n) }`); - assert.strictEqual( toInt( max (fromInt(m)) (fromInt(n)) ), Math.max(m,n) ); + assert.equal( max (m) (n), Math.max(m,n) ); } }); it("shifting bits",()=>{ for ( let i=1; i<=100; i++ ) { const n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`shift ${ n }`); - assert.strictEqual( toInt( shiftL (fromInt(n)) ), n >> 1 ); - assert.strictEqual( toInt( shiftR0 (fromInt(n)) ), n << 1 ); - assert.strictEqual( toInt( shiftR1 (fromInt(n)) ), n << 1 | 1 ); + assert.equal( shiftL (n), n >> 1 ); + assert.equal( shiftR0 (n), n << 1 ); + assert.equal( shiftR1 (n), n << 1 | 1 ); } }); it("zero padding",()=>{ for ( let i=1; i<=100; i++ ) { const n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`isPadded ${ n }`); - assert.strictEqual( isPadded (fromInt(n)) (false)(true), false ); - assert.strictEqual( isPadded (pad(fromInt(n))) (false)(true), true ); - assert.strictEqual( isPadded (pad(pad(fromInt(n)))) (false)(true), true ); - assert.strictEqual( isPadded (pad(pad(pad(fromInt(n))))) (false)(true), true ); + assert.equal( isPadded (n) (false)(true), false ); + assert.equal( isPadded (pad(n)) (false)(true), true ); + assert.equal( isPadded (pad(pad(n))) (false)(true), true ); + assert.equal( isPadded (pad(pad(pad(n)))) (false)(true), true ); } }); it("bitwise and",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } & ${ n } = ${ m&n }`); - assert.strictEqual( toInt( bitAnd (fromInt(m)) (fromInt(n)) ), m & n ); + assert.equal( bitAnd (m) (n), m & n ); } }); it("bitwise or",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } | ${ n } = ${ m|n }`); - assert.strictEqual( toInt( bitOr (fromInt(m)) (fromInt(n)) ), m | n ); + assert.equal( bitOr (m) (n), m | n ); } }); it("bitwise exclusive or",()=>{ for ( let i=1; i<=100; i++ ) { const m = rnd(i*i), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`${ m } ^ ${ n } = ${ m^n }`); - assert.strictEqual( toInt( bitXor (fromInt(m)) (fromInt(n)) ), m ^ n ); + assert.equal( bitXor (m) (n), m ^ n ); } }); it("testing bits",()=>{ for ( let i=1; i<=100; i++ ) { const j = rnd(i%32), n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`testBit ${ j } ${ n } = ${ Boolean( n & 1<{ for ( let i=1; i<=100; i++ ) { const j = rnd(i%32); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`bit ${ j } = ${ 1<{ const refPopCount = n => n && 1 + refPopCount(n & n-1) ; for ( let i=1; i<=100; i++ ) { const n = rnd(i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`popCount ${ n } = ${ refPopCount(n) }`); - assert.strictEqual( toInt( popCount (fromInt(n)) ), refPopCount(n) ); // JS restricted to 32-bit + assert.equal( popCount (n), refPopCount(n) ); // JS restricted to 32-bit } }); it("logical not",()=>{ - assert.strictEqual( not(False) (false)(true), true ); - assert.strictEqual( not(True) (false)(true), false ); + assert.equal( not(False) (false)(true), true ); + assert.equal( not(True) (false)(true), false ); }); it("logical and",()=>{ - assert.strictEqual( and(False)(False) (false)(true), false ); - assert.strictEqual( and(False)(True) (false)(true), false ); - assert.strictEqual( and(True) (False) (false)(true), false ); - assert.strictEqual( and(True) (True) (false)(true), true ); + assert.equal( and(False)(False) (false)(true), false ); + assert.equal( and(False)(True) (false)(true), false ); + assert.equal( and(True) (False) (false)(true), false ); + assert.equal( and(True) (True) (false)(true), true ); }); it("logical or",()=>{ - assert.strictEqual( or(False)(False) (false)(true), false ); - assert.strictEqual( or(False)(True) (false)(true), true ); - assert.strictEqual( or(True) (False) (false)(true), true ); - assert.strictEqual( or(True) (True) (false)(true), true ); + assert.equal( or(False)(False) (false)(true), false ); + assert.equal( or(False)(True) (false)(true), true ); + assert.equal( or(True) (False) (false)(true), true ); + assert.equal( or(True) (True) (false)(true), true ); }); it("logical exclusive or",()=>{ - assert.strictEqual( xor(False)(False) (false)(true), false ); - assert.strictEqual( xor(False)(True) (false)(true), true ); - assert.strictEqual( xor(True) (False) (false)(true), true ); - assert.strictEqual( xor(True) (True) (false)(true), false ); + assert.equal( xor(False)(False) (false)(true), false ); + assert.equal( xor(False)(True) (false)(true), true ); + assert.equal( xor(True) (False) (false)(true), true ); + assert.equal( xor(True) (True) (false)(true), false ); }); it("logical implies",()=>{ assert.strictEqual( implies(False)(False) (false)(true), true ); @@ -251,9 +225,8 @@ describe("Binary Scott tests",function(){ it("parity",()=>{ for ( let i=1; i<=100; i++ ) { const n = rnd(i*i*i); - if ( LC.config.verbosity >= "Loquacious" ) console.log(`parity ${ n }`); - assert.strictEqual( odd (fromInt(n)) (false)(true), Boolean(n&1) ); - assert.strictEqual( even (fromInt(n)) (false)(true), ! (n&1) ); + assert.equal( odd (fromInt(n)) (false)(true), Boolean(n&1) ); + assert.equal( even (fromInt(n)) (false)(true), ! (n&1) ); } }); }); diff --git a/tests/basics-church/test.js b/tests/basics-church/test.js index 59745d4..4207276 100644 --- a/tests/basics-church/test.js +++ b/tests/basics-church/test.js @@ -2,13 +2,11 @@ import {readFileSync} from "fs"; import {assert} from "chai"; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Church"; +LC.configure({ purity: "LetRec", numEncoding: "Church" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const solution = LC.compile(solutionText); -const fromInt = LC.fromIntWith(LC.config); -const toInt = LC.toIntWith(LC.config); +const { fromInt, toInt } = LC; const {B,C,I,KI,M,S,T,V,W,Y,Z} = solution; const {True,False,not,and,or,xor,implies} = solution; @@ -16,28 +14,27 @@ const {lt,le,eq,ge,gt} = solution; const {zero,succ,pred,isZero} = solution; const {plus,times,pow,minus} = solution; -const toPair = xy => [ fst(xy), snd(xy) ] ; - const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; describe("Church tests",function(){ this.timeout(0); it("fixed tests",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "Church" }); const one = succ(zero); const two = succ(one); const three = succ(two); const four = succ(three); const five = succ(four); - assert.strictEqual( toInt(zero), 0 ); - assert.strictEqual( toInt(one), 1 ); - assert.strictEqual( toInt(two), 2 ); - assert.strictEqual( toInt(three), 3 ); - assert.strictEqual( toInt(four), 4 ); - assert.strictEqual( toInt(five), 5 ); + assert.equal( zero, 0 ); + assert.equal( one, 1 ); + assert.equal( two, 2 ); + assert.equal( three, 3 ); + assert.equal( four, 4 ); + assert.equal( five, 5 ); const n = 1e3; - assert.strictEqual( toInt(I(fromInt(n))), n ); - assert.strictEqual( toInt(times(fromInt(1e2))(fromInt(1e1))), 1e3 ); - assert.strictEqual( toInt(pow(fromInt(10))(fromInt(3))), 1e3 ); - assert.strictEqual( toInt(pred(pow(fromInt(10))(fromInt(3)))), 1e3-1 ); + assert.equal( I(fromInt(n)), n ); + assert.equal( times(1e2)(1e1), 1e3 ); + assert.equal( pow(10)(3), 1e3 ); + assert.equal( pred(pow(10)(3)), 1e3-1 ); }); }); diff --git a/tests/counter/test.js b/tests/counter/test.js index 70fcdca..4f03983 100644 --- a/tests/counter/test.js +++ b/tests/counter/test.js @@ -3,20 +3,19 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "Let"; -LC.config.numEncoding = "Church"; +LC.configure({ purity: "Let", numEncoding: "Church" }); + const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {counter} = LC.compile(solutionText); -const toInt = LC.toIntWith(LC.config); - const T = t => f => t ; const F = t => f => f ; describe("counter", () => { it("fixed tests", function() { - assert.deepEqual( toInt( counter(T)(T)(T)(F) ), 3 ); - assert.deepEqual( toInt( counter(T)(F) ), 1 ); - assert.deepEqual( toInt( counter(T)(T)(T)(T)(T)(T)(T)(F) ), 7 ); + LC.configure({ purity: "Let", numEncoding: "Church" }); + assert.equal( counter(T)(T)(T)(F), 3 ); + assert.equal( counter(T)(F), 1 ); + assert.equal( counter(T)(T)(T)(T)(T)(T)(T)(F), 7 ); }); }); diff --git a/tests/delta-generators/test.js b/tests/delta-generators/test.js index d50a089..f59d66d 100644 --- a/tests/delta-generators/test.js +++ b/tests/delta-generators/test.js @@ -2,8 +2,7 @@ import {readFileSync} from "fs"; import {assert} from "chai"; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Scott"; +LC.configure({ purity: "LetRec", numEncoding: "Scott" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {delta} = LC.compile(solutionText); @@ -18,8 +17,7 @@ succ = \ n _ f . f n incr = \ n a . a (\ x _ . x) (\ b . b n (incr (succ n))) inf = incr 0`); -const fromInt = LC.fromIntWith(LC.config); -const toInt = LC.toIntWith(LC.config); +const {toInt} = LC; const Fst = fst => snd => fst ; const Snd = fst => snd => snd ; @@ -37,7 +35,8 @@ function toArr(a, n) { // lists use double pair encoding, not Scott! describe("delta-generators", () => { it("fixed tests", function() { - assert.deepEqual( toArr( delta (fromInt(2)) (fin), 2 ), [1, 1] ); - assert.deepEqual( toArr( delta (fromInt(1)) (inf), 10 ), [1,1,1,1,1,1,1,1,1,1] ); + LC.configure({ purity: "LetRec", numEncoding: "Scott" }); + assert.deepEqual( toArr( delta (2) (fin), 2 ), [1, 1] ); + assert.deepEqual( toArr( delta (1) (inf), 10 ), [1,1,1,1,1,1,1,1,1,1] ); }); }); diff --git a/tests/hello-world/test.js b/tests/hello-world/test.js index 90e28db..ee9a35c 100644 --- a/tests/hello-world/test.js +++ b/tests/hello-world/test.js @@ -3,14 +3,11 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "Let"; -LC.config.numEncoding = "Church"; +LC.configure({ purity: "Let", numEncoding: "Church" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {hello} = LC.compile(solutionText); -const toInt = LC.toIntWith(LC.config); - const Fst = fst => snd => fst ; const Snd = fst => snd => snd ; @@ -19,10 +16,11 @@ const head = xs => xs (Snd) (Fst) ; const tail = xs => xs (Snd) (Snd) ; // double pair encoding for list -const toString = xs => isNil (xs) ? "" : String.fromCharCode(toInt(head(xs))) + toString(tail(xs)) ; +const toString = xs => isNil (xs) ? "" : String.fromCharCode(LC.toInt(head(xs))) + toString(tail(xs)) ; describe("hello-world", () => { it("fixed test", function() { + LC.configure({ purity: "Let", numEncoding: "Church" }); assert.equal( toString(hello), "Hello, world!" ); }); }); diff --git a/tests/is-prime-scott/test.js b/tests/is-prime-scott/test.js index 421f49d..e49c6ef 100644 --- a/tests/is-prime-scott/test.js +++ b/tests/is-prime-scott/test.js @@ -3,35 +3,34 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Scott"; +LC.configure({ purity: "LetRec", numEncoding: "Scott" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {isPrime} = LC.compile(solutionText); -const fromInt = LC.fromIntWith(LC.config); -describe("is-prime-scott", () => { - it("fixed tests", function() { - this.timeout(12e3); - assert.equal( isPrime(fromInt( 0)) (true)(false), false ); - assert.equal( isPrime(fromInt( 1)) (true)(false), false ); - assert.equal( isPrime(fromInt( 2)) (true)(false), true ); - assert.equal( isPrime(fromInt( 3)) (true)(false), true ); - assert.equal( isPrime(fromInt( 4)) (true)(false), false ); - assert.equal( isPrime(fromInt( 5)) (true)(false), true ); - assert.equal( isPrime(fromInt( 6)) (true)(false), false ); - assert.equal( isPrime(fromInt( 7)) (true)(false), true ); - assert.equal( isPrime(fromInt( 8)) (true)(false), false ); - assert.equal( isPrime(fromInt( 9)) (true)(false), false ); - assert.equal( isPrime(fromInt(10)) (true)(false), false ); - assert.equal( isPrime(fromInt(11)) (true)(false), true ); - assert.equal( isPrime(fromInt(12)) (true)(false), false ); - assert.equal( isPrime(fromInt(13)) (true)(false), true ); - assert.equal( isPrime(fromInt(14)) (true)(false), false ); - assert.equal( isPrime(fromInt(15)) (true)(false), false ); - assert.equal( isPrime(fromInt(16)) (true)(false), false ); - assert.equal( isPrime(fromInt(17)) (true)(false), true ); - assert.equal( isPrime(fromInt(18)) (true)(false), false ); - assert.equal( isPrime(fromInt(19)) (true)(false), true ); +describe("is-prime-scott",function(){ + this.timeout(12e3); + it("fixed tests",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "Scott" }); + assert.equal( isPrime( 0) (true)(false), false ); + assert.equal( isPrime( 1) (true)(false), false ); + assert.equal( isPrime( 2) (true)(false), true ); + assert.equal( isPrime( 3) (true)(false), true ); + assert.equal( isPrime( 4) (true)(false), false ); + assert.equal( isPrime( 5) (true)(false), true ); + assert.equal( isPrime( 6) (true)(false), false ); + assert.equal( isPrime( 7) (true)(false), true ); + assert.equal( isPrime( 8) (true)(false), false ); + assert.equal( isPrime( 9) (true)(false), false ); + assert.equal( isPrime(10) (true)(false), false ); + assert.equal( isPrime(11) (true)(false), true ); + assert.equal( isPrime(12) (true)(false), false ); + assert.equal( isPrime(13) (true)(false), true ); + assert.equal( isPrime(14) (true)(false), false ); + assert.equal( isPrime(15) (true)(false), false ); + assert.equal( isPrime(16) (true)(false), false ); + assert.equal( isPrime(17) (true)(false), true ); + assert.equal( isPrime(18) (true)(false), false ); + assert.equal( isPrime(19) (true)(false), true ); }); }); diff --git a/tests/is-prime/test.js b/tests/is-prime/test.js index a9f22a0..6d50038 100644 --- a/tests/is-prime/test.js +++ b/tests/is-prime/test.js @@ -3,35 +3,34 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Church"; +LC.configure({ purity: "LetRec", numEncoding: "Church" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {isPrime} = LC.compile(solutionText); -const fromInt = LC.fromIntWith(LC.config); -describe("is-prime", () => { - it("fixed tests", function() { - this.timeout(12e3); - assert.equal( isPrime(fromInt(0)) (true)(false), false ); - assert.equal( isPrime(fromInt(1)) (true)(false), false ); - assert.equal( isPrime(fromInt(2)) (true)(false), true ); - assert.equal( isPrime(fromInt(3)) (true)(false), true ); - assert.equal( isPrime(fromInt(4)) (true)(false), false ); - assert.equal( isPrime(fromInt(5)) (true)(false), true ); - assert.equal( isPrime(fromInt(6)) (true)(false), false ); - assert.equal( isPrime(fromInt(7)) (true)(false), true ); - assert.equal( isPrime(fromInt(8)) (true)(false), false ); - assert.equal( isPrime(fromInt(9)) (true)(false), false ); - assert.equal( isPrime(fromInt(10)) (true)(false), false ); - assert.equal( isPrime(fromInt(11)) (true)(false), true ); - assert.equal( isPrime(fromInt(12)) (true)(false), false ); - assert.equal( isPrime(fromInt(13)) (true)(false), true ); - assert.equal( isPrime(fromInt(14)) (true)(false), false ); - assert.equal( isPrime(fromInt(15)) (true)(false), false ); - assert.equal( isPrime(fromInt(16)) (true)(false), false ); - assert.equal( isPrime(fromInt(17)) (true)(false), true ); - assert.equal( isPrime(fromInt(18)) (true)(false), false ); - assert.equal( isPrime(fromInt(19)) (true)(false), true ); +describe("is-prime",function(){ + this.timeout(12e3); + it("fixed tests",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "Church" }); + assert.equal( isPrime(0) (true)(false), false ); + assert.equal( isPrime(1) (true)(false), false ); + assert.equal( isPrime(2) (true)(false), true ); + assert.equal( isPrime(3) (true)(false), true ); + assert.equal( isPrime(4) (true)(false), false ); + assert.equal( isPrime(5) (true)(false), true ); + assert.equal( isPrime(6) (true)(false), false ); + assert.equal( isPrime(7) (true)(false), true ); + assert.equal( isPrime(8) (true)(false), false ); + assert.equal( isPrime(9) (true)(false), false ); + assert.equal( isPrime(10) (true)(false), false ); + assert.equal( isPrime(11) (true)(false), true ); + assert.equal( isPrime(12) (true)(false), false ); + assert.equal( isPrime(13) (true)(false), true ); + assert.equal( isPrime(14) (true)(false), false ); + assert.equal( isPrime(15) (true)(false), false ); + assert.equal( isPrime(16) (true)(false), false ); + assert.equal( isPrime(17) (true)(false), true ); + assert.equal( isPrime(18) (true)(false), false ); + assert.equal( isPrime(19) (true)(false), true ); }); }); diff --git a/tests/multiply/test.js b/tests/multiply/test.js index 3b87a24..38b86e1 100644 --- a/tests/multiply/test.js +++ b/tests/multiply/test.js @@ -4,27 +4,24 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Church"; -LC.config.verbosity = "Concise"; +LC.configure({ purity: "LetRec", numEncoding: "Church", verbosity: "Concise" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {multiply} = LC.compile(solutionText); -const fromInt = LC.fromIntWith(LC.config); -const toInt = LC.toIntWith(LC.config); describe("Multiply",()=>{ - it("example tests",function(){ - assert.equal( toInt(multiply(fromInt(7))(fromInt(7))), 49 ); - assert.equal( toInt(multiply(fromInt(11))(fromInt(11))), 121 ); - }); + it("example tests",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "Church", verbosity: "Concise" }); + assert.equal( multiply(7)(7), 49 ); + assert.equal( multiply(11)(11), 121 ); + }); - it("random tests",function(){ - const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; - for ( let i=1; i<=100; i++ ) { - const m = rnd(i), n = rnd(i); - assert.equal( toInt(multiply(fromInt(m))(fromInt(n))), m*n ); - } - }); + it("random tests",()=>{ + const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; + for ( let i=1; i<=100; i++ ) { + const m = rnd(i), n = rnd(i); + assert.equal( multiply(m)(n), m*n ); + } + }); }); diff --git a/tests/prime-sieve/test.js b/tests/prime-sieve/test.js index 283e419..9aad32a 100644 --- a/tests/prime-sieve/test.js +++ b/tests/prime-sieve/test.js @@ -3,30 +3,29 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "BinaryScott"; +LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const {primes} = LC.compile(solutionText); -const toInt = LC.toIntWith(LC.config); const head = xs => xs ( x => xs => x ) ; const tail = xs => xs ( x => xs => xs ) ; const take = n => xs => n ? [ head(xs), ...take(n-1)(tail(xs)) ] : [] ; describe("prime-sieve", () => { it("fixed tests: primes", function() { + LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); this.timeout(12e3); - assert.equal( toInt(head(primes)), 2 ); - assert.equal( toInt(head(tail(primes))), 3 ); - assert.equal( toInt(head(tail(tail(primes)))), 5 ); - assert.equal( toInt(head(tail(tail(tail(primes))))), 7 ); - assert.deepEqual( take(100)(primes).map(toInt), [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71 - ,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149 - ,151,157,163,167,173,179,181,191,193,197,199,211,223,227 - ,229,233,239,241,251,257,263,269,271,277,281,283,293,307 - ,311,313,317,331,337,347,349,353,359,367,373,379,383,389 - ,397,401,409,419,421,431,433,439,443,449,457,461,463,467 - ,479,487,491,499,503,509,521,523,541 - ] ); + assert.equal( head(primes), 2 ); + assert.equal( head(tail(primes)), 3 ); + assert.equal( head(tail(tail(primes))), 5 ); + assert.equal( head(tail(tail(tail(primes)))), 7 ); + assert.deepEqual( take(100)(primes).map(LC.toInt), [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71 + ,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149 + ,151,157,163,167,173,179,181,191,193,197,199,211,223,227 + ,229,233,239,241,251,257,263,269,271,277,281,283,293,307 + ,311,313,317,331,337,347,349,353,359,367,373,379,383,389 + ,397,401,409,419,421,431,433,439,443,449,457,461,463,467 + ,479,487,491,499,503,509,521,523,541 + ] ); }); }); diff --git a/tests/scott-lists/test.js b/tests/scott-lists/test.js index 2dc5328..fc9a2ef 100644 --- a/tests/scott-lists/test.js +++ b/tests/scott-lists/test.js @@ -3,9 +3,7 @@ import {assert, config as chaiConfig} from "chai"; chaiConfig.truncateThreshold = 0; import * as LC from "../../src/lambda-calculus.js"; -LC.config.purity = "LetRec"; -LC.config.numEncoding = "Scott"; -LC.config.verbosity = "Concise"; +LC.configure({ purity: "LetRec", numEncoding: "Scott", verbosity: "Concise" }); const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); const solution = LC.compile(solutionText); @@ -27,8 +25,7 @@ const {"group-by":groupBy,"nub-by":nubBy,"delete-by":deleteBy,"delete-firsts-by" const {init,last,tails,inits,slice,transpose} = solution; const {zero,succ,pred,add,"is-zero":isZero,Pair,None,Some} = solution; -const fromInt = LC.fromIntWith(LC.config); -const toInt = LC.toIntWith(LC.config); +const { fromInt, toInt } = LC; const fromArray = xs => xs.reduceRight( (z,x) => cons(x)(z) , nil ) ; const toArray = foldl ( z => x => [...z,x] ) ([]) ; const fromPair = ([fst,snd]) => Pair(fst)(snd) ; @@ -43,41 +40,42 @@ const rndNonEmptyArray = size => Array.from( { length: rnd(size) || 1 }, () => r describe("Scott Lists",function(){ it("nil,cons,singleton",()=>{ + LC.configure({ purity: "LetRec", numEncoding: "Scott", verbosity: "Concise" }); assert.deepEqual( toArray( nil ), [] ); for ( let i=1; i<=10; i++ ) { const x = rnd(i), xs = rndArray(i); - assert.deepEqual( toArray( cons (fromInt(x)) (fromArray(xs.map(fromInt))) ).map(toInt), [x,...xs], `after ${ i } tests` ); - assert.deepEqual( toArray( singleton (fromInt(x)) ).map(toInt), [x], `after ${ i } tests` ); + assert.deepEqual( toArray( cons (x) (fromArray(xs)) ).map(toInt), [x,...xs], `after ${ i } tests` ); + assert.deepEqual( toArray( singleton (x) ).map(toInt), [x], `after ${ i } tests` ); } }); it("foldr,foldl,scanr,scanl",()=>{ for ( let i=1; i<=10; i++ ) { const xs = rndArray(i); - assert.deepEqual( toInt( foldr (add) (zero) (fromArray(xs.map(fromInt))) ), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); - assert.deepEqual( toInt( foldl (add) (zero) (fromArray(xs.map(fromInt))) ), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); - assert.deepEqual( toArray( scanr (add) (zero) (fromArray(xs.map(fromInt))) ).map(toInt), xs.reduceRight( (z,x) => [ z[0]+x, ...z ], [0] ), `after ${ i } tests` ); - assert.deepEqual( toArray( scanl (add) (zero) (fromArray(xs.map(fromInt))) ).map(toInt), xs.reduce( (z,x) => [ ...z, z[z.length-1]+x ] , [0] ), `after ${ i } tests` ); + assert.equal( foldr (add) (zero) (fromArray(xs)), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); + assert.equal( foldl (add) (zero) (fromArray(xs)), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); + assert.deepEqual( toArray( scanr (add) (zero) (fromArray(xs)) ).map(toInt), xs.reduceRight( (z,x) => [ z[0]+x, ...z ], [0] ), `after ${ i } tests` ); + assert.deepEqual( toArray( scanl (add) (zero) (fromArray(xs)) ).map(toInt), xs.reduce( (z,x) => [ ...z, z[z.length-1]+x ] , [0] ), `after ${ i } tests` ); } }); it("take,drop",()=>{ for ( let i=1; i<=10; i++ ) { const n = rnd(i), xs = rndArray(i); - assert.deepEqual( toArray( take (fromInt(n)) (fromArray(xs.map(fromInt))) ).map(toInt), xs.slice(0,n), `after ${ i } tests` ); - assert.deepEqual( toArray( drop (fromInt(n)) (fromArray(xs.map(fromInt))) ).map(toInt), xs.slice(n), `after ${ i } tests` ); + assert.deepEqual( toArray( take (n) (fromArray(xs)) ).map(toInt), xs.slice(0,n), `after ${ i } tests` ); + assert.deepEqual( toArray( drop (n) (fromArray(xs)) ).map(toInt), xs.slice(n), `after ${ i } tests` ); } }); it("append,concat,snoc",()=>{ for ( let i=1; i<=10; i++ ) { const x = rnd(i), xs = rndArray(i), ys = rndArray(i); - assert.deepEqual( toArray( append (fromArray(xs.map(fromInt))) (fromArray(ys.map(fromInt))) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); - assert.deepEqual( toArray( concat (fromArray([ fromArray(xs.map(fromInt)), fromArray(ys.map(fromInt)) ])) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); - assert.deepEqual( toArray( snoc (fromArray(xs.map(fromInt))) (fromInt(x)) ).map(toInt), [...xs,x], `after ${ i } tests` ); + assert.deepEqual( toArray( append (fromArray(xs)) (fromArray(ys)) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); + assert.deepEqual( toArray( concat (fromArray([ fromArray(xs), fromArray(ys) ])) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); + assert.deepEqual( toArray( snoc (fromArray(xs)) (x) ).map(toInt), [...xs,x], `after ${ i } tests` ); } }); it("uncons",()=>{ for ( let i=1; i<=10; i++ ) { const xs = rndArray(i); - assert.deepEqual( toNullable ( xy => toPair(xy).map( (x,i) => i ? toArray(x).map(toInt) : toInt(x) ) ) ( uncons (fromArray(xs.map(fromInt))) ), + assert.deepEqual( toNullable ( xy => toPair(xy).map( (x,i) => i ? toArray(x).map(toInt) : toInt(x) ) ) ( uncons (fromArray(xs)) ), xs.length ? [ xs[0], xs.slice(1) ] : null , `after ${ i } tests` ); } @@ -86,7 +84,7 @@ describe("Scott Lists",function(){ const N = 10; for ( let i=1; i<=10; i++ ) { const x = rnd(i), y = rnd(i); - const actual = toArray( take (N) (iterate (add (fromInt(y))) (fromInt(x))) ).map(toInt); + const actual = toArray( take (N) (iterate (add (y)) (x)) ).map(toInt); const expected = Array.from( { length: N }, (_,i) => x + i * y ); assert.deepEqual( actual, expected, `after ${ i } tests` ); } @@ -95,7 +93,7 @@ describe("Scott Lists",function(){ const N = 10; for ( let i=1; i<=10; i++ ) { const x = rnd(i); - const actual = toArray( take (N) (repeat (fromInt(x))) ).map(toInt); + const actual = toArray( take (N) (repeat (x)) ).map(toInt); const expected = Array.from( { length: N }, () => x ); assert.deepEqual( actual, expected, `after ${ i } tests` ); } @@ -112,7 +110,7 @@ describe("Scott Lists",function(){ it("replicate",()=>{ for ( let i=1; i<=10; i++ ) { const n = rnd(i), x = rnd(i); - const actual = toArray( replicate (fromInt(n)) (fromInt(x)) ).map(toInt); + const actual = toArray( replicate (n) (x) ).map(toInt); const expected = Array.from( { length: n }, () => x ); assert.deepEqual( actual, expected, `after ${ i } tests` ); } @@ -120,7 +118,7 @@ describe("Scott Lists",function(){ it("unfold",()=>{ for ( let i=1; i<=10; i++ ) { const x = rnd(i); - const actual = toArray( unfold ( x => (isZero (x)) (Some (Pair (x) (pred (x)))) (None) ) (fromInt(x)) ).map(toInt); + const actual = toArray( unfold ( x => (isZero (x)) (Some (Pair (x) (pred (x)))) (None) ) (x) ).map(toInt); const expected = Array.from( { length: x }, (_,i) => x-i ); assert.deepEqual( actual, expected, `after ${ i } tests` ); }