const {test}=require('node:test'); const assert=require('node:assert/strict'); const E=require('./ensemble.js'); test('grid lookup respects coordinates and clamps plot edges',()=>{ const grid={size:2,grid:[-1,1,2,-1]}; assert.equal(E.predict(grid,{x:0,y:0}),-1); assert.equal(E.predict(grid,{x:0,y:1}),2); assert.equal(E.predict(grid,{x:2,y:2}),-1); }); test('SAMME upweights mistakes and restores weights after removal',()=>{ const points=[{label:-1},{label:1},{label:2}]; const models=[{alpha:Math.log(4),predictions:[-1,-1,2]}]; const before=E.weightsFor(points,models); assert.ok(Math.abs(before[1]-2/3)<1e-12); models.push({alpha:1,predictions:[-1,1,2]});models.pop(); assert.deepEqual(E.weightsFor(points,models),before); }); test('three-class voting and forests average probabilities across all classes',()=>{ const classes=[-1,1,2]; const models=[{classes,alpha:1,predictions:[1],tree:{size:1,grid:[1]}},{classes,alpha:2,predictions:[2],tree:{size:1,grid:[2]}}]; assert.equal(E.vote(models,{x:0,y:0}),2);assert.equal(E.trainingVote(models,0),2); const forest=[[.45,.05,.5],[.6,.1,.3]].map(p=>({classes,forest:true,alpha:1,trainingProbabilities:[p],tree:{size:1,probabilities:[p]}})); assert.equal(E.vote(forest,{x:0,y:0}),-1);forest.pop();assert.equal(E.trainingVote(forest,0),2); }); test('training supports a pair of classes that excludes A',()=>{ assert.equal(E.trainingVote([{classes:[1,2],alpha:1,predictions:[2]}],0),2); });