@wafertools/wafermap 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/AGENTS.md +13 -14
  2. package/CHANGELOG.md +235 -0
  3. package/README.md +5 -5
  4. package/dist/packages/canvas-adapter/deprecated.d.ts +10 -0
  5. package/dist/packages/canvas-adapter/deprecated.js +1 -0
  6. package/dist/packages/canvas-adapter/dieList.d.ts +1 -3
  7. package/dist/packages/canvas-adapter/dieList.js +7 -7
  8. package/dist/packages/canvas-adapter/index.d.ts +1 -3
  9. package/dist/packages/canvas-adapter/index.js +1 -1
  10. package/dist/packages/canvas-adapter/insightsTab.d.ts +0 -1
  11. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  12. package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
  13. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +1 -40
  14. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  15. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +74 -104
  16. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  17. package/dist/packages/canvas-adapter/summaryPanel.d.ts +11 -8
  18. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  19. package/dist/packages/canvas-adapter/toCanvas.d.ts +20 -12
  20. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  21. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  22. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  23. package/dist/packages/canvas-adapter/userGuideHtml.js +12 -8
  24. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  25. package/dist/packages/canvas-adapter/version.js +1 -1
  26. package/dist/packages/canvas-adapter/warnings.js +1 -1
  27. package/dist/packages/core/deprecated.d.ts +64 -0
  28. package/dist/packages/core/deprecated.js +1 -0
  29. package/dist/packages/core/index.d.ts +1 -3
  30. package/dist/packages/core/index.js +1 -1
  31. package/dist/packages/core/passBins.d.ts +33 -0
  32. package/dist/packages/core/passBins.js +1 -0
  33. package/dist/packages/core/waferLabel.d.ts +25 -0
  34. package/dist/packages/core/waferLabel.js +1 -0
  35. package/dist/packages/renderer/binColors.d.ts +67 -13
  36. package/dist/packages/renderer/binColors.js +1 -1
  37. package/dist/packages/renderer/buildView.d.ts +8 -9
  38. package/dist/packages/renderer/buildView.js +1 -1
  39. package/dist/packages/renderer/buildWaferMap.d.ts +47 -13
  40. package/dist/packages/renderer/buildWaferMap.js +1 -1
  41. package/dist/packages/renderer/colorMap.d.ts +4 -3
  42. package/dist/packages/renderer/colorSchemes.d.ts +8 -6
  43. package/dist/packages/renderer/deprecate.d.ts +24 -0
  44. package/dist/packages/renderer/deprecate.js +1 -0
  45. package/dist/packages/renderer/deprecated.d.ts +45 -0
  46. package/dist/packages/renderer/deprecated.js +1 -0
  47. package/dist/packages/renderer/index.d.ts +2 -3
  48. package/dist/packages/renderer/index.js +1 -1
  49. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  50. package/dist/packages/stats/binPareto.d.ts +6 -1
  51. package/dist/packages/stats/clusterDetection.d.ts +0 -2
  52. package/dist/packages/stats/deprecated.d.ts +87 -0
  53. package/dist/packages/stats/deprecated.js +1 -0
  54. package/dist/packages/stats/index.d.ts +3 -14
  55. package/dist/packages/stats/index.js +1 -1
  56. package/dist/packages/stats/regions.d.ts +7 -2
  57. package/dist/packages/stats/regions.js +1 -1
  58. package/dist/packages/stats/renderSummaryReport.d.ts +3 -0
  59. package/dist/packages/stats/renderSummaryReport.js +34 -34
  60. package/dist/packages/stats/types.d.ts +0 -43
  61. package/dist/packages/stats/yield.d.ts +12 -6
  62. package/dist/packages/stats/yield.js +1 -1
  63. package/package.json +1 -1
@@ -1 +1 @@
1
- import{createWafer as j}from"../core/wafer.js";import{isYieldEligibleDie as ce,getDieKey as V,hasPosition as E}from"../core/dies.js";import{applyOrientation as q,transformDies as le}from"../core/transforms.js";import{affineRotation as ue,affineMirror as he,affineCompose as pe,affinePoint as me}from"../core/transforms.js";import{inferWaferFromXY as ge}from"../core/inference/wafer.js";import{resolveGridPitch as ye}from"../core/inference/pitch.js";import{assignGridIndices as we}from"../core/inference/grid.js";import{generateReticleGrid as xe}from"../core/reticle.js";import{buildView as H}from"./buildView.js";import{modeOf as De}from"../core/utils.js";import{aggregateValues as U,aggregateBinCounts as be}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function Me(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Oe(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Re(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let u=1/0,c=-1/0,a=1/0,l=-1/0;for(const s of t)s.x<u&&(u=s.x),s.x>c&&(c=s.x),s.y<a&&(a=s.y),s.y>l&&(l=s.y);return{offsetX:Math.round((c+u)/2),offsetY:Math.round((l+a)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function Se(t,n,e,u,c,a){if(a)return{colMidX:(Math.round(a.x)-n)*u,colMidY:(Math.round(a.y)-e)*c,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let l=1/0,s=-1/0,h=1/0,p=-1/0;for(const i of t){const f=Math.round(i.x)-n,r=Math.round(i.y)-e;f<l&&(l=f),f>s&&(s=f),r<h&&(h=r),r>p&&(p=r)}return{colMidX:(l+s)/2*u,colMidY:(h+p)/2*c,anchored:!1}}function Ee(t,n,e){let u=1/0,c=-1/0,a=1/0,l=-1/0,s=0,h=0;for(const f of t){const r=Math.round(f.x)-n,d=Math.round(f.y)-e;s+=r,h+=d,r<u&&(u=r),r>c&&(c=r),d<a&&(a=d),d>l&&(l=d)}const p=t.length,i=(f,r,d)=>{const m=(d-r)/2;return m<=0?0:Math.abs(f/p-(r+d)/2)/m};return i(s,u,c)>.11||i(h,a,l)>.11}function ve(t,n){let e=t?.xAxisDirection==="left",u=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(u=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:u}}function ke(t,n){const{method:e,targetBin:u}=t,c=t.results.map(a=>a.filter(E));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const a=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),l=new Set;for(const i of c)for(const f of i)if(f.testValues)for(const r of Object.keys(f.testValues)){const d=Number(r);a.has(d)||l.add(d)}if(l.size===0)return U(c,e);const s=[...l],h=s.map(i=>U(c,e,i)),p=new Map;for(let i=0;i<s.length;i++){const f=s[i];for(const r of h[i]){const d=V(r);p.has(d)||p.set(d,{template:r,testValues:{}});const m=p.get(d),g=r.testValues?.[0];g!==void 0&&(m.testValues[f]=g)}}return[...p.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(u===void 0)return[];const a=be(c,u,"hard");if(e==="countBin")return a;const l=c.length;return a.map(s=>({...s,testValues:{0:l>0?(s.testValues?.[0]??0)/l*100:0}}))}if(e==="mode"){const a=new Map;for(const s of c)for(const h of s){const p=V(h);a.has(p)||a.set(p,[]),a.get(p).push(h)}const l=[];for(const[s,h]of a){const p=s.split(","),i=Number(p[0]),f=Number(p[1]),r=h.map(m=>m.hbin).filter(m=>m!==void 0),d=De(r);d!==null&&l.push({x:i,y:f,hbin:d})}return l}return[]}function J(t){const n=t.filter(E),e=n.length,u=n.filter(a=>a.edgeExcluded).length,c=n.filter(a=>dieHasTestData(a)||a.hbin!==void 0||a.sbin!==void 0).length;return{filledDies:c,totalDies:e,edgeExcludedDies:u,unpositionedDies:t.length-n.length,ratio:e>0?c/e:0}}function Q(t,n,e="exclude"){const u=new Set(n),a=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,l=t.filter(d=>d.partial).length;let s=0,h=0,p=!1;for(const d of t){if(!ce(d))continue;const m=d.hbin??d.sbin;m!==void 0&&(p=!0,u.has(m)?s++:h++)}const i=s+h,f=p&&i>0?s/i*100:null;let r=null;if(e==="denominator-only"){const d=i+a;r=p&&d>0?s/d*100:null}return{passDies:s,failDies:h,edgeExcludedDies:a,partialDies:l,totalDies:i,yieldPercent:f,yieldPercentGross:r}}function Z(t,n,e,u,c,a=0,l=0,s=0,h=0,p=0,i=!1,f=!1){if(!t)return[];const r=t.anchorDie??{x:0,y:0},d=xe(n,{width:t.width,height:t.height,diePitchX:u,diePitchY:c,anchorDie:{x:r.x-a,y:r.y-l},gridOrigin:{x:-s,y:-h}}),m=pe(he(i,f,n.center.x,n.center.y),ue(p,n.center.x,n.center.y));return d.filter(g=>{const v=g.width/2,D=g.height/2,b=[[g.x-v,g.y-D],[g.x+v,g.y-D],[g.x+v,g.y+D],[g.x-v,g.y+D]].map(([w,x])=>me(m,w,x)),k=Math.min(...b.map(w=>w.x)),A=Math.max(...b.map(w=>w.x)),F=Math.min(...b.map(w=>w.y)),P=Math.max(...b.map(w=>w.y));return e.some(w=>w.physX>=k&&w.physX<A&&w.physY>=F&&w.physY<P)})}function Ae(t,n,e){const c=Math.max(0,n.radius-e)**2;return t.map(a=>{const l=a.physX-n.center.x,s=a.physY-n.center.y;return l*l+s*s>c?{...a,edgeExcluded:!0}:a})}function Ce(t,n,e){const u=t.filter(E),c=t.filter(i=>!E(i)),a=new Map;for(const i of u){let f=a.get(i.x);f||(f=new Map,a.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const l=new Set(e);function s(i,f){const r=i.hbin,d=f.hbin;if(r===void 0||d===void 0)return!1;const m=l.has(r),g=l.has(d);return n==="best"?g!==m?g:d<r:g!==m?m:d>r}const h=new Map;for(const i of u){const f=V(i),r=h.get(f);n==="first"&&r||(n==="best"||n==="worst")&&r&&!s(r,i)||h.set(f,i)}return[...Array.from(h.values()).map(i=>{const r=a.get(i.x)?.get(i.y)??1;return r>1?{...i,retestCount:r}:i}),...c]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const u=t.testPass?.[n];if(u!==void 0)return u;if(e!==void 0&&!isParametricTest(e)){const c=t.testValues?.[n];if(c===0||c===1)return c===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function L(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ee(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function te(t){return(t.warnings??[]).map(n=>{const e=Ie(n);return{code:e,message:n,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:t.wafer.confidence}})}function Ie(t){return t.includes(ne)?"geometry-conflict":t.includes(ie)?"non-standard-diameter":t.includes(se)?"diameter-exceeds-die-extent":"partial-coverage"}const ne="do not fit inside the supplied",ie="Inferred wafer diameter",se="The probed dies reach only",Fe=.75,Pe=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const _e=.02;function oe(t,n){return n.reduce((e,u)=>Math.abs(u-t)<Math.abs(e-t)?u:e)}function Ye(t,n){if(!n.length)return!0;const e=oe(t,n);return Math.abs(e-t)/e<=_e}function $e(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=Me(t),{debug:u,...c}=n??{},a=e.lotStackOpts?ke(e.lotStackOpts,e.testDefs):e.results;for(let o=0;o<Math.min(5,a.length);o++){const y=a[o];if(typeof y.x=="string"||typeof y.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const o of a)if(o.x==null!=(o.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(o.x)}, y=${String(o.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const l=Ce(a,e.retestPolicy,e.passBins),s={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}};if(e.explicitDies){let o=e.explicitDies;const y=l.filter(E),R=l.filter(T=>!E(T));if(y.length>0){const T=new Map(y.map(S=>[V(S),S]));o=o.map(S=>{const G=T.get(V(S));return G?L(S,G):S})}const $=e.waferOpts?.diameter??300,I=j({diameter:$,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});I.orientation!==0&&(o=q(o,I));const z=Z(e.reticleOpts,I,o,1,1,0,0,0,0,I.orientation),de=c.showReticle??e.reticleOpts!==void 0,B=H(I,o,{...c,reticles:z,showReticle:de,plotMode:ee(l,c),testDefs:e.testDefs,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),fe=R.map((T,S)=>L({id:`unpositioned_${S}`,width:o[0]?.width??1,height:o[0]?.height??1},T)),W=[...o,...fe];return{wafer:I,dies:W,view:B,reticleConfig:e.reticleOpts,units:"mm",inference:s,warnings:te(s),plotMode:B.plotMode,metadata:B.metadata,isLotStack:!1,dataCoverage:J(W),yield:Q(W,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const h=l.filter(E),p=l.filter(o=>!E(o)),i=h.map(o=>({x:o.x,y:o.y})),f=ye(i,e.dieOpts,e.waferOpts?.diameter);s.diePitch={confidence:f.confidence,units:f.units};const{pitchX:r,pitchY:d}=f,m=f.units,g=Oe(h,e.dieOpts),v=we(i);s.grid={confidence:v.confidence};const{offsetX:D,offsetY:b}=Re(i,g,v),{flipX:k,flipY:A}=ve(e.dieOpts,g),{colMidX:F,colMidY:P,anchored:w}=Se(i,D,b,r,d,e.waferOpts?.center);let x=e.waferOpts?.diameter;const _=i.map(o=>({x:(Math.round(o.x)-D)*r-F,y:(Math.round(o.y)-b)*d-P})),C=_.length>0?Math.max(..._.map(({x:o,y})=>Math.hypot(Math.abs(o)+r/2,Math.abs(y)+d/2))):0;if(x===void 0)if(i.length>0)if(m==="mm"){const o=ge(_,{minRadius:C});x=o.diameter,s.wafer={confidence:o.confidence,method:o.method};const y=$e(e.standardDiameters);if(!Ye(x,y)){const R=oe(x,y);(s.warnings??=[]).push(`${ie} ${x.toFixed(1)} mm is not a standard wafer size (nearest is ${R} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`)}}else x=C*2,s.wafer={confidence:f.confidence*.8,method:"extent"};else x=m==="mm"?300:30,s.wafer={confidence:0,method:"default"};if(!w&&i.length>0&&Ee(i,D,b)&&(s.wafer.method="inferred-partial",(s.warnings??=[]).push("Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly.")),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&C>0&&i.length>=Pe){const o=C/(x/2);o<Fe&&(s.warnings??=[]).push(`${se} ${(o*100).toFixed(0)}% of the ${x} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`)}if(e.waferOpts?.diameter!==void 0){const o=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(o&&C>x/2+1e-9){const y=_.filter(({x:R,y:$})=>Math.hypot(Math.abs(R)+r/2,Math.abs($)+d/2)>x/2+1e-9).length;(s.warnings??=[]).push(`${y} of ${_.length} probed die positions ${ne} ${x} mm wafer at the supplied die pitch of ${r} \xD7 ${d} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(C*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`)}}const O=j({diameter:x,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let M=h.map(o=>{const y=Math.round(o.x)-D,R=Math.round(o.y)-b,$={id:`${y}_${R}`,x:y,y:R,physX:y*r-F,physY:R*d-P,width:r,height:d,insideWafer:!0,partial:!1};return L($,o)});(D!==0||b!==0)&&(M=M.map(o=>({...o,x:o.x+D,y:o.y+b,id:`${o.x+D}_${o.y+b}`}))),M=q(M,O),(k||A)&&(M=le(M,{flipX:k,flipY:A},O.center));const X=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(M=Ae(M,O,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>O.radius&&X.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${O.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const K=Z(e.reticleOpts,O,M,r,d,D,b,F,P,O.orientation,k,A),re=c.showReticle??e.reticleOpts!==void 0,Y=H(O,M,{...c,reticles:K,showReticle:re,plotMode:ee(l,c),testDefs:e.testDefs,dataAxisFlip:{x:k,y:A},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),ae=p.map((o,y)=>L({id:`unpositioned_${y}`,width:r,height:d},o)),N=[...M,...ae];return{wafer:O,dies:N,view:Y,reticleConfig:e.reticleOpts,units:m,inference:s,warnings:[...te(s),...X],plotMode:Y.plotMode,metadata:Y.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:J(N),yield:Q(N,e.passBins,e.edgeDieYieldMode),reticles:K,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:Y.aggrMethod,lotSize:Y.lotSize}}
1
+ import{createWafer as q}from"../core/wafer.js";import{isYieldEligibleDie as fe,getDieKey as B,hasPosition as v}from"../core/dies.js";import{applyOrientation as H,transformDies as ce}from"../core/transforms.js";import{affineRotation as le,affineMirror as ue,affineCompose as he,affinePoint as me}from"../core/transforms.js";import{inferWaferFromXY as ge}from"../core/inference/wafer.js";import{resolveGridPitch as pe}from"../core/inference/pitch.js";import{assignGridIndices as ye}from"../core/inference/grid.js";import{generateReticleGrid as xe}from"../core/reticle.js";import{buildView as U}from"./buildView.js";import{modeOf as we}from"../core/utils.js";import{aggregateValues as J,aggregateBinCounts as be}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function De(t){const n=(l,o)=>({ringCount:l,ringCountWarning:{code:"analysis-option-corrected",severity:"warning",message:`ringCount=${String(t)} ${o} (using ${l}).`}});if(t===void 0)return{ringCount:4,ringCountWarning:void 0};if(typeof t!="number"||!Number.isFinite(t))return n(4,"is not a finite number");const e=Math.max(1,Math.round(t));return e===t?{ringCount:t,ringCountWarning:void 0}:n(e,"is not a whole number of at least 1")}function Q(t){const n=(u,i)=>u!==null&&typeof u=="object"&&u[i]!==void 0,e=Array.isArray(t)?{}:t,l=[],o=(u,i,f)=>{u&&l.push(`\`${i}\` (now \`${f}\`)`)};o(n(e,"data"),"data","results"),o(n(e,"die"),"die","dieConfig"),o(n(e,"stack"),"stack","lotStack"),o(n(e.dieConfig,"origin"),"dieConfig.origin","dieConfig.coordinateOrigin"),o(n(e.waferConfig,"flat"),"waferConfig.flat","waferConfig.notch"),o(n(e.reticleConfig,"anchor"),"reticleConfig.anchor","reticleConfig.anchorDie"),o(n(e.lotStack,"aggr"),"lotStack.aggr","lotStack.method"),o(Array.isArray(e.testDefs)&&e.testDefs.some(u=>n(u,"index")&&!n(u,"testNumber")),"TestDef.index","TestDef.testNumber");const r=n(e.lotStack,"results")?e.lotStack.results:[],h=(Array.isArray(t)?[t]:[e.results,...Array.isArray(r)?r:[]]).filter(u=>Array.isArray(u));if(o(h.some(u=>u.slice(0,100).some(i=>n(i,"values"))),"values","testValues"),!l.length)return;const s=l.length>1,m=`buildWaferMap ignored ${l.join(", ")}: ${s?"these names were":"that name was"} removed in an earlier release, so what ${s?"they":"it"} described is not on this map. Rename and rebuild.`;return console.warn(`[wafermap] ${m}`),{code:"input-field-removed",message:m,severity:"error"}}function Me(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],ringCount:4,ringCountWarning:void 0,removedFieldWarning:Q(t),standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],...De(t.ringCount),removedFieldWarning:Q(t),standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Ce(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Oe(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let l=1/0,o=-1/0,r=1/0,h=-1/0;for(const s of t)s.x<l&&(l=s.x),s.x>o&&(o=s.x),s.y<r&&(r=s.y),s.y>h&&(h=s.y);return{offsetX:Math.round((o+l)/2),offsetY:Math.round((h+r)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function Se(t,n,e,l,o,r){if(r)return{colMidX:(Math.round(r.x)-n)*l,colMidY:(Math.round(r.y)-e)*o,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let h=1/0,s=-1/0,m=1/0,u=-1/0;for(const i of t){const f=Math.round(i.x)-n,c=Math.round(i.y)-e;f<h&&(h=f),f>s&&(s=f),c<m&&(m=c),c>u&&(u=c)}return{colMidX:(h+s)/2*l,colMidY:(m+u)/2*o,anchored:!1}}function ve(t,n,e){let l=1/0,o=-1/0,r=1/0,h=-1/0,s=0,m=0;for(const f of t){const c=Math.round(f.x)-n,d=Math.round(f.y)-e;s+=c,m+=d,c<l&&(l=c),c>o&&(o=c),d<r&&(r=d),d>h&&(h=d)}const u=t.length,i=(f,c,d)=>{const g=(d-c)/2;return g<=0?0:Math.abs(f/u-(c+d)/2)/g};return i(s,l,o)>.11||i(m,r,h)>.11}function Re(t,n){let e=t?.xAxisDirection==="left",l=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(l=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:l}}function ke(t,n){const{method:e,targetBin:l}=t,o=t.results.map(r=>r.filter(v));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const r=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),h=new Set;for(const i of o)for(const f of i)if(f.testValues)for(const c of Object.keys(f.testValues)){const d=Number(c);r.has(d)||h.add(d)}if(h.size===0)return J(o,e);const s=[...h],m=s.map(i=>J(o,e,i)),u=new Map;for(let i=0;i<s.length;i++){const f=s[i];for(const c of m[i]){const d=B(c);u.has(d)||u.set(d,{template:c,testValues:{}});const g=u.get(d),p=c.testValues?.[0];p!==void 0&&(g.testValues[f]=p)}}return[...u.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(l===void 0)return[];const r=be(o,l,"hard");if(e==="countBin")return r;const h=o.length;return r.map(s=>({...s,testValues:{0:h>0?(s.testValues?.[0]??0)/h*100:0}}))}if(e==="mode"){const r=new Map;for(const s of o)for(const m of s){const u=B(m);r.has(u)||r.set(u,[]),r.get(u).push(m)}const h=[];for(const[s,m]of r){const u=s.split(","),i=Number(u[0]),f=Number(u[1]),c=m.map(g=>g.hbin).filter(g=>g!==void 0),d=we(c);d!==null&&h.push({x:i,y:f,hbin:d})}return h}return[]}function Z(t){const n=t.filter(v),e=n.length,l=n.filter(r=>r.edgeExcluded).length,o=n.filter(r=>dieHasTestData(r)||r.hbin!==void 0||r.sbin!==void 0).length;return{filledDies:o,totalDies:e,edgeExcludedDies:l,unpositionedDies:t.length-n.length,ratio:e>0?o/e:0}}function ee(t,n,e="exclude"){const l=new Set(n),r=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,h=t.filter(d=>d.partial).length;let s=0,m=0,u=!1;for(const d of t){if(!fe(d))continue;const g=d.hbin??d.sbin;g!==void 0&&(u=!0,l.has(g)?s++:m++)}const i=s+m,f=u&&i>0?s/i*100:null;let c=null;if(e==="denominator-only"){const d=i+r;c=u&&d>0?s/d*100:null}return{passDies:s,failDies:m,edgeExcludedDies:r,partialDies:h,totalDies:i,yieldPercent:f,yieldPercentGross:c}}function te(t,n,e,l,o,r=0,h=0,s=0,m=0,u=0,i=!1,f=!1){if(!t)return[];const c=t.anchorDie??{x:0,y:0},d=xe(n,{width:t.width,height:t.height,diePitchX:l,diePitchY:o,anchorDie:{x:c.x-r,y:c.y-h},gridOrigin:{x:-s,y:-m}}),g=he(ue(i,f,n.center.x,n.center.y),le(u,n.center.x,n.center.y));return d.filter(p=>{const R=p.width/2,k=p.height/2,w=[[p.x-R,p.y-k],[p.x+R,p.y-k],[p.x+R,p.y+k],[p.x-R,p.y+k]].map(([y,N])=>me(g,y,N)),M=Math.min(...w.map(y=>y.x)),A=Math.max(...w.map(y=>y.x)),E=Math.min(...w.map(y=>y.y)),$=Math.max(...w.map(y=>y.y));return e.some(y=>y.physX>=M&&y.physX<A&&y.physY>=E&&y.physY<$)})}function Ae(t,n,e){const o=Math.max(0,n.radius-e)**2;return t.map(r=>{const h=r.physX-n.center.x,s=r.physY-n.center.y;return h*h+s*s>o?{...r,edgeExcluded:!0}:r})}function Ee(t,n,e){const l=t.filter(v),o=t.filter(i=>!v(i)),r=new Map;for(const i of l){let f=r.get(i.x);f||(f=new Map,r.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const h=new Set(e);function s(i,f){const c=i.hbin,d=f.hbin;if(c===void 0||d===void 0)return!1;const g=h.has(c),p=h.has(d);return n==="best"?p!==g?p:d<c:p!==g?g:d>c}const m=new Map;for(const i of l){const f=B(i),c=m.get(f);n==="first"&&c||(n==="best"||n==="worst")&&c&&!s(c,i)||m.set(f,i)}return[...Array.from(m.values()).map(i=>{const c=r.get(i.x)?.get(i.y)??1;return c>1?{...i,retestCount:c}:i}),...o]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const l=t.testPass?.[n];if(l!==void 0)return l;if(e!==void 0&&!isParametricTest(e)){const o=t.testValues?.[n];if(o===0||o===1)return o===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function Y(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ne(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function ie(t,n){return t.map(({code:e,message:l})=>({code:e,message:l,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:n.wafer.confidence}))}const Fe="do not fit inside the supplied",We="Inferred wafer diameter",$e="The probed dies reach only",Ie=.75,Pe=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const Te=.02;function se(t,n){return n.reduce((e,l)=>Math.abs(l-t)<Math.abs(e-t)?l:e)}function _e(t,n){if(!n.length)return!0;const e=se(t,n);return Math.abs(e-t)/e<=Te}function Be(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=Me(t),{debug:l,...o}=n??{},r=e.lotStackOpts?ke(e.lotStackOpts,e.testDefs):e.results;for(let a=0;a<Math.min(5,r.length);a++){const x=r[a];if(typeof x.x=="string"||typeof x.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const a of r)if(a.x==null!=(a.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(a.x)}, y=${String(a.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const h=Ee(r,e.retestPolicy,e.passBins),s={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}},m=[];if(e.explicitDies){let a=e.explicitDies;const x=h.filter(v),O=h.filter(_=>!v(_));if(x.length>0){const _=new Map(x.map(S=>[B(S),S]));a=a.map(S=>{const G=_.get(B(S));return G?Y(S,G):S})}const T=e.waferOpts?.diameter??300,W=q({diameter:T,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});W.orientation!==0&&(a=H(a,W));const z=te(e.reticleOpts,W,a,1,1,0,0,0,0,W.orientation),ae=o.showReticle??e.reticleOpts!==void 0,L=U(W,a,{...o,reticles:z,showReticle:ae,plotMode:ne(h,o),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),de=O.map((_,S)=>Y({id:`unpositioned_${S}`,width:a[0]?.width??1,height:a[0]?.height??1},_)),X=[...a,...de];return{wafer:W,dies:X,view:L,reticleConfig:e.reticleOpts,units:"mm",inference:s,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...ie(m,s),...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:L.plotMode,metadata:L.metadata,isLotStack:!1,dataCoverage:Z(X),passBins:e.passBins,ringCount:e.ringCount,yield:ee(X,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const u=h.filter(v),i=h.filter(a=>!v(a)),f=u.map(a=>({x:a.x,y:a.y})),c=pe(f,e.dieOpts,e.waferOpts?.diameter);s.diePitch={confidence:c.confidence,units:c.units};const{pitchX:d,pitchY:g}=c,p=c.units,R=Ce(u,e.dieOpts),k=ye(f);s.grid={confidence:k.confidence};const{offsetX:w,offsetY:M}=Oe(f,R,k),{flipX:A,flipY:E}=Re(e.dieOpts,R),{colMidX:$,colMidY:y,anchored:N}=Se(f,w,M,d,g,e.waferOpts?.center);let b=e.waferOpts?.diameter;const I=f.map(a=>({x:(Math.round(a.x)-w)*d-$,y:(Math.round(a.y)-M)*g-y})),F=I.length>0?Math.max(...I.map(({x:a,y:x})=>Math.hypot(Math.abs(a)+d/2,Math.abs(x)+g/2))):0;if(b===void 0)if(f.length>0)if(p==="mm"){const a=ge(I,{minRadius:F});b=a.diameter,s.wafer={confidence:a.confidence,method:a.method};const x=Be(e.standardDiameters);if(!_e(b,x)){const O=se(b,x);m.push({code:"non-standard-diameter",message:`${We} ${b.toFixed(1)} mm is not a standard wafer size (nearest is ${O} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`})}}else b=F*2,s.wafer={confidence:c.confidence*.8,method:"extent"};else b=p==="mm"?300:30,s.wafer={confidence:0,method:"default"};if(!N&&f.length>0&&ve(f,w,M)&&(s.wafer.method="inferred-partial",m.push({code:"partial-coverage",message:"Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly."})),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&F>0&&f.length>=Pe){const a=F/(b/2);a<Ie&&m.push({code:"diameter-exceeds-die-extent",message:`${$e} ${(a*100).toFixed(0)}% of the ${b} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`})}if(e.waferOpts?.diameter!==void 0){const a=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(a&&F>b/2+1e-9){const x=I.filter(({x:O,y:T})=>Math.hypot(Math.abs(O)+d/2,Math.abs(T)+g/2)>b/2+1e-9).length;m.push({code:"geometry-conflict",message:`${x} of ${I.length} probed die positions ${Fe} ${b} mm wafer at the supplied die pitch of ${d} \xD7 ${g} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(F*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`})}}const C=q({diameter:b,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let D=u.map(a=>{const x=Math.round(a.x)-w,O=Math.round(a.y)-M,T={id:`${x}_${O}`,x,y:O,physX:x*d-$,physY:O*g-y,width:d,height:g,insideWafer:!0,partial:!1};return Y(T,a)});(w!==0||M!==0)&&(D=D.map(a=>({...a,x:a.x+w,y:a.y+M,id:`${a.x+w}_${a.y+M}`}))),D=H(D,C),(A||E)&&(D=ce(D,{flipX:A,flipY:E},C.center));const K=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(D=Ae(D,C,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>C.radius&&K.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${C.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const j=te(e.reticleOpts,C,D,d,g,w,M,$,y,C.orientation,A,E),re=o.showReticle??e.reticleOpts!==void 0,P=U(C,D,{...o,reticles:j,showReticle:re,plotMode:ne(h,o),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,dataAxisFlip:{x:A,y:E},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),oe=i.map((a,x)=>Y({id:`unpositioned_${x}`,width:d,height:g},a)),V=[...D,...oe];return{wafer:C,dies:V,view:P,reticleConfig:e.reticleOpts,units:p,inference:s,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...ie(m,s),...K,...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:P.plotMode,metadata:P.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:Z(V),passBins:e.passBins,ringCount:e.ringCount,yield:ee(V,e.passBins,e.edgeDieYieldMode),reticles:j,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:P.aggrMethod,lotSize:P.lotSize}}
@@ -17,9 +17,10 @@ export declare const NO_DATA_FILL = "#d6d9dd";
17
17
  * Indices 1–63 are perceptually spread colours generated via golden-angle
18
18
  * HSL stepping.
19
19
  *
20
- * No longer used for bins: bin colours are rank-assigned from a registered bin
21
- * palette by `resolveBinColors` (binColors.ts). Hashing a bin number into this
22
- * list is what made two different bins share a colour.
20
+ * No longer used for bins: bin colours come from a registered bin palette,
21
+ * indexed by bin number and pass/fail, in `resolveBinColors` (binColors.ts).
22
+ * Hashing a bin number into this list is what made two different bins share a
23
+ * colour.
23
24
  */
24
25
  export declare const BIN_PALETTE: readonly string[];
25
26
  /** Wang hash — maps any integer to a well-distributed unsigned 32-bit value. Shared with colorSchemes. */
@@ -40,15 +40,17 @@ export interface BinColorScheme {
40
40
  /** Human-readable display name. */
41
41
  label: string;
42
42
  /**
43
- * Colours for passing bins, handed out most-populous bin first. Convention is
44
- * greens: an engineer reads green as pass before reading the legend.
43
+ * Colours for passing bins, indexed by bin number: pass bin 1 takes the first,
44
+ * pass bin 2 the second, wrapping round. Convention is greens: an engineer
45
+ * reads green as pass before reading the legend.
45
46
  */
46
47
  pass: readonly string[];
47
48
  /**
48
- * Colours for failing bins, handed out most-populous bin first, so put the
49
- * most distinct colours at the front. Must not resemble the pass colours —
50
- * a fail bin that reads as a pass is the most expensive misreading a wafer
51
- * map can produce.
49
+ * Colours for failing bins, indexed by bin number: fail bin 2 takes the
50
+ * first, bin 3 the second, wrapping round. Put the most distinct colours at
51
+ * the front — the low bin numbers most programs use land there. Must not
52
+ * resemble the pass colours — a fail bin that reads as a pass is the most
53
+ * expensive misreading a wafer map can produce.
52
54
  */
53
55
  fail: readonly string[];
54
56
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The release that deletes every export deprecated through this helper. Named in
3
+ * each notice and each `@deprecated` tag, and held to by tests/deprecations.test.mjs,
4
+ * which fails once the changelog or package.json reaches it with them still here.
5
+ */
6
+ export declare const DEPRECATED_REMOVAL_VERSION = "0.31.0";
7
+ /**
8
+ * @internal Every name deprecated through this module, and whether it is a function
9
+ * (which logs a notice) or a value (which cannot). tests/deprecations.test.mjs pins
10
+ * it to the announced list, so a deprecation cannot be added or dropped silently.
11
+ */
12
+ export declare const DEPRECATED_EXPORTS: Map<string, "function" | "value">;
13
+ /**
14
+ * @internal Wrap `fn` so its first call logs one notice naming `name` and `advice`.
15
+ * The wrapper has exactly `fn`'s type — generics and overloads included — so a
16
+ * deprecated export's signature does not change until it is removed.
17
+ */
18
+ export declare function deprecated<F extends (...args: never[]) => unknown>(fn: F, name: string, advice: string): F;
19
+ /**
20
+ * @internal Register a deprecated constant. A value cannot log on use, so its
21
+ * `@deprecated` tag and the changelog are the only announcement.
22
+ */
23
+ export declare function deprecatedValue<T>(value: T, name: string): T;
24
+ //# sourceMappingURL=deprecate.d.ts.map
@@ -0,0 +1 @@
1
+ export const DEPRECATED_REMOVAL_VERSION="0.31.0",DEPRECATED_EXPORTS=new Map;const n=new Set;export function deprecated(t,e,r){DEPRECATED_EXPORTS.set(e,"function");const o=t;return((...c)=>(n.has(e)||(n.add(e),console.warn(`[wafermap] ${e} is deprecated and will be removed in ${DEPRECATED_REMOVAL_VERSION}. ${r}`)),o(...c)))}export function deprecatedValue(t,e){return DEPRECATED_EXPORTS.set(e,"value"),t}
@@ -0,0 +1,45 @@
1
+ import { valueToViridis as valueToViridisImpl, valueToGreyscale as valueToGreyscaleImpl } from './colorMap.js';
2
+ import { getValueColorScheme as getValueColorSchemeImpl } from './colorSchemes.js';
3
+ /**
4
+ * @deprecated Removed in 0.31.0. Use `resolveValueColorFn('default')`, which returns the same
5
+ * colours through the read-path that also applies `reverseValueScheme`.
6
+ */
7
+ export declare const valueToViridis: typeof valueToViridisImpl;
8
+ /**
9
+ * @deprecated Removed in 0.31.0. Use `resolveValueColorFn('greyscale')`, which returns the same
10
+ * colours through the read-path that also applies `reverseValueScheme`.
11
+ */
12
+ export declare const valueToGreyscale: typeof valueToGreyscaleImpl;
13
+ /**
14
+ * @deprecated Removed in 0.31.0. Colour by value with `resolveValueColorFn(name, reversed)`, and list
15
+ * names and labels with `listValueColorSchemes()`. `forValue` ignores
16
+ * `reverseValueScheme`, so a surface built on it can show a reading in a
17
+ * different colour from the map.
18
+ */
19
+ export declare const getValueColorScheme: typeof getValueColorSchemeImpl;
20
+ import { buildView as buildViewImpl, buildHoverText as buildHoverTextImpl, buildMapTitle as buildMapTitleImpl } from './buildView.js';
21
+ import { resolveBinColors as resolveBinColorsImpl } from './binColors.js';
22
+ import { getBinColorScheme as getBinColorSchemeImpl } from './colorSchemes.js';
23
+ import { contrastTextColor as contrastTextColorImpl } from './colorMap.js';
24
+ import { dieHasTestData as dieHasTestDataImpl, isParametricTest as isParametricTestImpl, getDieTestValue as getDieTestValueImpl } from './buildWaferMap.js';
25
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
26
+ export declare const buildView: typeof buildViewImpl;
27
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
28
+ export declare const buildHoverText: typeof buildHoverTextImpl;
29
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
30
+ export declare const buildMapTitle: typeof buildMapTitleImpl;
31
+ /** @deprecated Removed in 0.31.0. The maps resolve bin colours themselves: set a colour with `BinDef.color`, or register a palette with `registerBinColorScheme`. */
32
+ export declare const resolveBinColors: typeof resolveBinColorsImpl;
33
+ /** @deprecated Removed in 0.31.0. The maps resolve bin colours themselves: set a colour with `BinDef.color`, or register a palette with `registerBinColorScheme`. */
34
+ export declare const getBinColorScheme: typeof getBinColorSchemeImpl;
35
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
36
+ export declare const contrastTextColor: typeof contrastTextColorImpl;
37
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
38
+ export declare const dieHasTestData: typeof dieHasTestDataImpl;
39
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
40
+ export declare const isParametricTest: typeof isParametricTestImpl;
41
+ /** @deprecated Removed in 0.31.0. Read die.testValues[testNumber]. */
42
+ export declare const getDieTestValue: typeof getDieTestValueImpl;
43
+ /** @deprecated Removed in 0.31.0. Pass the sizes you want as buildWaferMap's `standardDiameters`; the default ladder is [100, 125, 150, 200, 300]. */
44
+ export declare const STANDARD_WAFER_DIAMETERS_MM: readonly number[];
45
+ //# sourceMappingURL=deprecated.d.ts.map
@@ -0,0 +1 @@
1
+ import{valueToViridis as a,valueToGreyscale as s}from"./colorMap.js";import{getValueColorScheme as i}from"./colorSchemes.js";import{deprecated as e}from"./deprecate.js";export const valueToViridis=e(a,"valueToViridis","Use resolveValueColorFn('default'), which returns the same colours."),valueToGreyscale=e(s,"valueToGreyscale","Use resolveValueColorFn('greyscale'), which returns the same colours."),getValueColorScheme=e(i,"getValueColorScheme","Colour by value with resolveValueColorFn(name, reversed) \u2014 forValue ignores reverseValueScheme \u2014 and list names and labels with listValueColorSchemes().");import{deprecatedValue as n}from"./deprecate.js";import{buildView as p,buildHoverText as c,buildMapTitle as m}from"./buildView.js";import{resolveBinColors as u}from"./binColors.js";import{getBinColorScheme as d}from"./colorSchemes.js";import{contrastTextColor as T}from"./colorMap.js";import{dieHasTestData as h,isParametricTest as I,getDieTestValue as V,STANDARD_WAFER_DIAMETERS_MM as C}from"./buildWaferMap.js";const o="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",t="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",l="The maps resolve bin colours themselves: set a colour with BinDef.color, or register a palette with registerBinColorScheme.",r="It is an internal helper that was exported by accident; buildWaferMap, analyzeWaferMap and the renderers already apply it.",D="Read die.testValues[testNumber].";export const buildView=e(p,"buildView",`${t} ${o}`),buildHoverText=e(c,"buildHoverText",`${t} ${o}`),buildMapTitle=e(m,"buildMapTitle",`${t} ${o}`),resolveBinColors=e(u,"resolveBinColors",`${l} ${o}`),getBinColorScheme=e(d,"getBinColorScheme",`${l} ${o}`),contrastTextColor=e(T,"contrastTextColor",`${r} ${o}`),dieHasTestData=e(h,"dieHasTestData",`${r} ${o}`),isParametricTest=e(I,"isParametricTest",`${r} ${o}`),getDieTestValue=e(V,"getDieTestValue",`${D} ${o}`),STANDARD_WAFER_DIAMETERS_MM=n(C,"STANDARD_WAFER_DIAMETERS_MM");
@@ -1,9 +1,8 @@
1
- export { buildView, getDieKey, buildHoverText, buildMapTitle, } from './buildView.js';
1
+ export { getDieKey } from './buildView.js';
2
2
  export type { View, MapTitleParts, PlotMode, ViewRect, ViewText, ViewHoverPoint, ViewOverlay, ViewOptions, } from './buildView.js';
3
3
  export * from './buildWaferMap.js';
4
4
  export type { WaferMetadata, DieMetadata } from '../core/metadata.js';
5
- export { valueToViridis, valueToGreyscale, contrastTextColor, } from './colorMap.js';
6
5
  export * from './colorSchemes.js';
7
- export { resolveBinColors } from './binColors.js';
8
6
  export type { BinColors, BinColorOptions } from './binColors.js';
7
+ export { valueToViridis, valueToGreyscale, getValueColorScheme, buildView, buildHoverText, buildMapTitle, resolveBinColors, getBinColorScheme, contrastTextColor, dieHasTestData, isParametricTest, getDieTestValue, STANDARD_WAFER_DIAMETERS_MM, } from './deprecated.js';
9
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export{buildView,getDieKey,buildHoverText,buildMapTitle}from"./buildView.js";export*from"./buildWaferMap.js";export{valueToViridis,valueToGreyscale,contrastTextColor}from"./colorMap.js";export*from"./colorSchemes.js";export{resolveBinColors}from"./binColors.js";
1
+ export{getDieKey}from"./buildView.js";export*from"./buildWaferMap.js";export*from"./colorSchemes.js";export{valueToViridis,valueToGreyscale,getValueColorScheme,buildView,buildHoverText,buildMapTitle,resolveBinColors,getBinColorScheme,contrastTextColor,dieHasTestData,isParametricTest,getDieTestValue,STANDARD_WAFER_DIAMETERS_MM}from"./deprecated.js";
@@ -1 +1 @@
1
- import{isYieldEligibleDie as ee,getDieKey as x,isPositionedDie as de}from"../core/dies.js";import{buildWaferMap as Re,getTestPassStatus as me,isParametricTest as j}from"../renderer/buildWaferMap.js";import{buildQuadrantRegions as Be,buildReticlePositionRegions as Ce,buildRingRegions as Fe,buildSectorRegions as Ne,buildTestSiteRegions as Pe,sectorCompassNames as ge,areQuadrantsAdjacent as Ve,parseRegionKey as O}from"./regions.js";import{buildClusterFindings as Le}from"./clusterDetection.js";import{classifyPattern as Ee}from"./patternClassification.js";import{normalCdf as he}from"./math.js";import{quantile as te}from"./math.js";import{mean as X,clamp01 as be}from"../core/utils.js";const Ae={significanceLevel:{min:1e-6,max:1},minimumEffectSize:{min:0,max:1},minimumRelativeEffect:{min:0,max:Number.MAX_SAFE_INTEGER},ringCount:{min:1,max:Number.MAX_SAFE_INTEGER}},pe=[4,8,16,32];function Ke(i){const a=Object.fromEntries(Object.entries(i).filter(([,f])=>f!==void 0)),s={..._,...a},o=[],e=[];for(const f of["significanceLevel","minimumEffectSize","minimumRelativeEffect","ringCount"]){const{min:l,max:r}=Ae[f],n=s[f];if(typeof n!="number"||!Number.isFinite(n)){e.push(`${f}=${String(n)} is not a finite number (using ${_[f]})`),s[f]=_[f];continue}const c=Math.min(r,Math.max(l,n));c!==n&&(e.push(`${f}=${n} is outside ${l}\u2013${r} (using ${c})`),s[f]=c)}if(!Number.isInteger(s.ringCount)){const f=Math.max(1,Math.round(s.ringCount));e.push(`ringCount=${s.ringCount} is not a whole number (using ${f})`),s.ringCount=f}if(pe.includes(s.sectorCount)||(e.push(`sectorCount=${s.sectorCount} is not one of ${pe.join(", ")} (using ${_.sectorCount})`),s.sectorCount=_.sectorCount),e.length){const f=`analyzeWaferMap: ${e.join("; ")}. The analysis ran with the corrected values.`;console.warn(`[wafermap] ${f}`),o.push({code:"analysis-option-corrected",message:f,severity:"warning"})}return{resolved:s,warnings:o}}const _={ringCount:4,passBins:[1],significanceLevel:.05,minimumEffectSize:.2,minimumRelativeEffect:1,minimumSampleSize:5,includePartial:!1,includeEdgeExcluded:!1,enableYieldAnalysis:!0,enableHardBinAnalysis:!0,enableSoftBinAnalysis:!0,enableTestValueAnalysis:!1,computePerTestStats:!1,enableReticlePositionAnalysis:!0,enableClusterAnalysis:!0,enableAngularAnalysis:!0,sectorCount:8,enablePatternClassification:!0,minimumClusterSize:5};function xe(i,a){const s={...i};return s.minimumClusterSize=Math.max(5,Math.round(a*.003)),s}function Te(i){return"wafer"in i&&"dies"in i&&"view"in i?i:Re(i)}function je(i,a){return ee(i,a)?i.hbin!==void 0||i.sbin!==void 0||i.testValues!==void 0&&Object.keys(i.testValues).length>0:!1}function Oe(i,a,s){if(!i||a)return;const o=(s??[]).filter(e=>j(e)&&(e.limitLow!==void 0||e.limitHigh!==void 0));if(o.length!==0)return e=>{for(const f of o){const l=f.testNumber,r=e.testValues?.[l];if(r!==void 0&&(f.limitLow!==void 0&&r<f.limitLow||f.limitHigh!==void 0&&r>f.limitHigh))return!0}return!1}}function He(i,a,s){const o=new Set,e=new Set,f=new Set,l=new Map,r=new Map;for(const n of i){if(n.testValues)for(const c of Object.keys(n.testValues))o.add(Number(c));if(n.testPass)for(const c of Object.keys(n.testPass))o.add(Number(c));n.hbin!==void 0&&e.add(n.hbin),n.sbin!==void 0&&f.add(n.sbin),ee(n)&&(n.hbin!==void 0&&l.set(n.hbin,(l.get(n.hbin)??0)+1),n.sbin!==void 0&&r.set(n.sbin,(r.get(n.sbin)??0)+1))}return{totalDies:i.length,analyzedDies:a,excludedDies:i.length-a,yieldPercent:s,testsConsidered:[...o].sort((n,c)=>n-c),hardBinsConsidered:[...e].sort((n,c)=>n-c),softBinsConsidered:[...f].sort((n,c)=>n-c),...l.size?{hardBinCounts:Object.fromEntries(l)}:{},...r.size?{softBinCounts:Object.fromEntries(r)}:{}}}function qe(i,a){if(!a?.length)return;const s=a.filter(e=>j(e)&&(e.limitLow!==void 0||e.limitHigh!==void 0));if(!s.length)return;const o=[];for(const e of s){const f=e.testNumber;let l=0,r=0,n=0,c=0;for(const t of i){if(t.partial||t.edgeExcluded)continue;const h=t.testValues?.[f];h!==void 0&&(c++,e.limitLow!==void 0&&h<e.limitLow?r++:e.limitHigh!==void 0&&h>e.limitHigh?n++:l++)}o.push({testNumber:f,label:e.name,passDies:l,failLowDies:r,failHighDies:n,totalDies:c,yieldPercent:c>0?l/c*100:null})}return o.length?o:void 0}export function computeFunctionalYield(i,a){if(!a?.length)return;const s=a.filter(e=>!j(e));if(!s.length)return;const o=[];for(const e of s){const f=e.testNumber;let l=0,r=0;for(const c of i){if(c.partial||c.edgeExcluded)continue;const t=me(c,f,e);t!==void 0&&(t?l++:r++)}const n=l+r;o.push({testNumber:f,label:e.name,passDies:l,failDies:r,totalDies:n,passRatePercent:n>0?l/n*100:null})}return o.length?o:void 0}function Ie(i,a,s,o){const e=[];for(const f of a){const l=[];for(const h of i){if(h.partial||h.edgeExcluded)continue;const m=h.testValues?.[f];m!==void 0&&l.push(m)}if(l.length<o)continue;const r=X(l),n=Math.sqrt(le(l,r)),c=l.slice().sort((h,m)=>h-m),t=s?.find(h=>h.testNumber===f)?.name??String(f);e.push({testNumber:f,label:t,count:l.length,min:c[0],max:c[c.length-1],mean:r,stddev:n,median:te(c,.5),q1:te(c,.25),q3:te(c,.75)})}return e.length?e:void 0}function H(i,a,s,o){const e=(i+s)/(a+o),f=e*(1-e)*(1/a+1/o);if(!Number.isFinite(f)||f<=0)return 1;const l=(i/a-s/o)/Math.sqrt(f);return be(2*(1-he(Math.abs(l))))}function ne(i){const a=new Map;for(const s of i){const o=`${s.variable.kind}:${s.comparison.family}`,e=a.get(o)??[];e.push(s),a.set(o,e)}for(const s of a.values()){const o=[...s].filter(l=>l.stats.pValue!==void 0).sort((l,r)=>(l.stats.pValue??1)-(r.stats.pValue??1)),e=o.length;let f=1;for(let l=e-1;l>=0;l--){const r=o[l],n=r.stats.pValue??1,c=Math.min(1,Math.min(f,n*e/(l+1)));r.stats.adjustedPValue=c,f=c}}return i}function ie(i,a,s){const o=Math.abs(a),e=s!==void 0?Math.abs(s):0;return i<=.01&&(o>=.3||e>=2.5)?"unusual":i<=.05&&(o>=.2||e>=1.5)?"notable":"info"}function se(i,a){return i<=.01&&Math.abs(a)>=.5?"unusual":i<=.05&&Math.abs(a)>=.15?"notable":"info"}function J(i){return i==="reticle-position"?"other reticle positions":i==="test-site"?"other test sites":"the rest of the map"}function q(i){return i==="reticle-position"?"Other reticle positions":i==="test-site"?"Other test sites":"Rest of map"}function ye(i,a,s){const o=(Math.abs(a)*100).toFixed(1);return`${i} yield is ${o} percentage points ${a>0?"higher":"lower"} than ${J(s)}`}function oe(i,a){return a==="quadrant"&&!i.startsWith("Quadrant")?`quadrant ${i}`:i}function Se(i,a,s,o){const e=oe(i,o),f=(Math.abs(s)*100).toFixed(1);return`${e} has ${a} occurrence ${f} percentage points ${s>0?"higher":"lower"} than ${J(o)}`}function _e(i,a,s,o){const e=oe(i,o),f=(Math.abs(s)*100).toFixed(1);return`${e} has ${a} pass rate ${f} percentage points ${s>0?"higher":"lower"} than ${J(o)}`}function we(i,a,s,o,e,f){const l=oe(i,e),r=J(e),n=s>0?"higher":"lower";if(o!==void 0&&Number.isFinite(o)){const t=(Math.abs(o)*100).toFixed(1);return`${l} mean ${a} is ${t}% ${n} than ${r}`}const c=f?` ${f}`:"";return`${l} mean ${a} is ${Math.abs(s).toPrecision(3)}${c} ${n} than ${r}`}function $e(i,a,s){const o=a?.find(e=>e.bin===i);return o?.name?`${s} ${i} (${o.name})`:`${s} ${i}`}function Ye(i,a){const s=a?.find(o=>o.testNumber===i);return{label:s?.name??`Test ${i}`,unit:s?.unit}}function ae(i,a){const s=new Map(i.map(e=>[x(e),e])),o=new Map;for(const e of a){const f=[];for(const l of e.dieKeys){const r=s.get(l);r&&f.push(r)}o.set(e.key,f)}return o}function re(i,a){return ne(i),i.filter(s=>{const o=s.stats.adjustedPValue??s.stats.pValue??1,e=Math.abs(s.effect.absoluteDelta??0),f=Math.abs(s.effect.relativeDelta??0);return o<=a.significanceLevel&&(e>=a.minimumEffectSize||f>=a.minimumRelativeEffect)}).map(s=>({...s,severity:ie(s.stats.adjustedPValue??s.stats.pValue??1,s.effect.absoluteDelta??0,s.effect.relativeDelta)}))}function Y(i,a,s,o){const e=new Set(s),f=ae(i,a),l=new Map,r=new Map;for(const[c,t]of f){let h=0,m=0;for(const d of t)d.hbin!==void 0&&(m++,e.has(d.hbin)&&h++);l.set(c,h),r.set(c,m)}const n=[];for(const c of a){const t=r.get(c.key)??0,h=l.get(c.key)??0;let m=0,d=0;for(const[y,b]of r)y!==c.key&&(m+=b,d+=l.get(y)??0);if(t<o.minimumSampleSize||m<o.minimumSampleSize)continue;const g=h/t,p=d/m,S=g-p,D=H(h,t,d,m);n.push({id:`yield:${c.key}`,level:"wafer",severity:"info",variable:{kind:"yield",label:"Yield"},comparison:{family:c.family,left:c.label,right:q(c.family)},effect:{direction:S===0?"different":S>0?"higher":"lower",absoluteDelta:S,relativeDelta:p===0?void 0:S/p,effectSize:S},stats:{method:"two-proportion-z",pValue:D,sampleSizeLeft:t,sampleSizeRight:m},summary:ye(c.label,S,c.family),highlight:{kind:"region",regionFamily:c.family,regionKeys:[c.key],dieKeys:[...c.dieKeys]}})}return re(n,o)}function V(i,a,s,o,e,f){const l=d=>s==="soft"?d.sbin:d.hbin,r=[...new Set(i.map(l).filter(d=>d!==void 0))].sort((d,g)=>d-g),n=ae(i,a),c=new Map,t=new Map;for(const[d,g]of n){const p=new Map;for(const S of g){const D=l(S);D!==void 0&&p.set(D,(p.get(D)??0)+1)}c.set(d,p),t.set(d,g.length)}const h=[],m=e==="hardBin"?"HBin":"SBin";for(const d of a){const g=t.get(d.key)??0,p=c.get(d.key);let S=0;const D=new Map;for(const[y,b]of c)if(y!==d.key){S+=t.get(y)??0;for(const[$,k]of b)D.set($,(D.get($)??0)+k)}if(!(g<f.minimumSampleSize||S<f.minimumSampleSize))for(const y of r){const b=p.get(y)??0,$=D.get(y)??0,k=b/g,w=$/S,v=k-w,R=H(b,g,$,S),u=$e(y,o,m);h.push({id:`${e}:${y}:${d.key}`,level:"wafer",severity:"info",variable:{kind:e,bin:y,label:u},comparison:{family:d.family,left:d.label,right:q(d.family)},effect:{direction:v===0?"different":v>0?"higher":"lower",absoluteDelta:v,relativeDelta:w===0?void 0:v/w,effectSize:v},stats:{method:"two-proportion-z",pValue:R,sampleSizeLeft:g,sampleSizeRight:S},summary:Se(d.label,u,v,d.family),highlight:{kind:"bin",bin:y,regionKeys:[d.key],dieKeys:[...d.dieKeys]}})}}return re(h,f)}function W(i,a,s,o){const e=(s??[]).filter(t=>!j(t));if(!e.length)return[];const f=e.map(t=>t.testNumber),l=ae(i,a),r=new Map,n=new Map;for(const[t,h]of l){const m=new Uint32Array(e.length),d=new Uint32Array(e.length);for(const g of h)for(let p=0;p<e.length;p++){const S=me(g,f[p],e[p]);S!==void 0&&(d[p]++,S&&m[p]++)}r.set(t,m),n.set(t,d)}const c=[];for(const t of a){const h=r.get(t.key),m=n.get(t.key);for(let d=0;d<e.length;d++){let g=0,p=0;for(const[v,R]of n)v!==t.key&&(p+=R[d],g+=r.get(v)[d]);const S=m[d],D=h[d];if(S<o.minimumSampleSize||p<o.minimumSampleSize)continue;const y=D/S,b=g/p,$=y-b,k=H(D,S,g,p),w=f[d];c.push({id:`functional:${w}:${t.key}`,level:"wafer",severity:"info",variable:{kind:"functionalTest",index:w,label:`${e[d].name} pass rate`},comparison:{family:t.family,left:t.label,right:q(t.family)},effect:{direction:$===0?"different":$>0?"higher":"lower",absoluteDelta:$,relativeDelta:b===0?void 0:$/b,effectSize:$},stats:{method:"two-proportion-z",pValue:k,sampleSizeLeft:S,sampleSizeRight:p},summary:_e(t.label,e[d].name,$,t.family),highlight:{kind:"region",regionFamily:t.family,regionKeys:[t.key],dieKeys:[...t.dieKeys]}})}}return re(c,o)}function le(i,a){return i.length<2?0:i.reduce((s,o)=>s+(o-a)**2,0)/(i.length-1)}function ve(i,a,s,o,e,f){const l=Math.sqrt(s/i+f/o),r=a-e;if(!Number.isFinite(l)||l===0)return{pValue:1,effectSize:0,delta:r};const n=r/l,c=Math.sqrt(Math.max(0,(s+f)/2)),t=c===0?r:r/c;return{pValue:be(2*(1-he(Math.abs(n)))),effectSize:t,delta:r}}function We(i,a){const s=X(i),o=X(a);return ve(i.length,s,le(i,s),a.length,o,le(a,o))}const De=250;function ke(i,a,s){const o=l=>l.filter(r=>j(s?.find(n=>n.testNumber===r)));if(a)return{testNumbers:o(a.slice().sort((l,r)=>l-r))};const e=new Set;let f=!1;e:for(const l of i)for(const r of Object.keys(l.testValues??{})){const n=Number(r);if(!e.has(n)&&(e.add(n),e.size>De)){f=!0;break e}}if(f){const l=`Test-value analysis was skipped: more than ${De} tests were found in the die data, so no test findings were computed. Pass testNumbers: [...] to analyse specific tests.`;return console.warn(`[wafermap] analyzeWaferMap: ${l}`),{testNumbers:[],warning:{code:"test-count-capped",message:l,severity:"warning"}}}return{testNumbers:o([...e].sort((l,r)=>l-r))}}function Q(i,a,s,o){const e=ke(i,o.testNumbers,s);if(e.warning)return{findings:[],warning:e.warning};const f=e.testNumbers,l=new Map;for(let g=0;g<a.length;g++)for(const p of a[g].dieKeys)l.set(p,g);const r=a.length,n=[],c=[];for(const g of i){const p=l.get(x(g));p!==void 0&&(n.push(g),c.push(p))}const t=[],h=new Float64Array(r),m=new Float64Array(r),d=new Float64Array(r);for(const g of f){h.fill(0),m.fill(0),d.fill(0);let p=0,S=0,D=0,y;for(let b=0;b<n.length;b++){const k=n[b].testValues?.[g];if(k===void 0)continue;y===void 0&&(y=k);const w=k-y,v=c[b];h[v]+=1,m[v]+=w,d[v]+=w*w,p+=1,S+=w,D+=w*w}if(y!==void 0)for(let b=0;b<r;b++){const $=h[b],k=p-$;if($<o.minimumSampleSize||k<o.minimumSampleSize)continue;const w=m[b]/$,R=(S-m[b])/k,u=w+y,M=R+y,B=$>1?Math.max(0,d[b]-$*w*w)/($-1):0,z=k>1?Math.max(0,D-d[b]-k*R*R)/(k-1):0,{pValue:F,effectSize:N,delta:E}=ve($,u,B,k,M,z),P=a[b],{label:U,unit:G}=Ye(g,s),A=M!==0?E/Math.abs(M):void 0;t.push({id:`test:${g}:${P.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:g,label:U,unit:G},comparison:{family:P.family,left:P.label,right:q(P.family)},effect:{direction:E===0?"different":E>0?"higher":"lower",absoluteDelta:E,relativeDelta:A,effectSize:N},stats:{method:"welch-z-approx",pValue:F,sampleSizeLeft:$,sampleSizeRight:k},summary:we(P.label,U,E,A,P.family,G),highlight:{kind:"region",regionFamily:P.family,regionKeys:[P.key],dieKeys:[...P.dieKeys]}})}}return ne(t),{activeTestNumbers:f,findings:t.filter(g=>{const p=g.stats.adjustedPValue??g.stats.pValue??1,S=Math.abs(g.effect.effectSize??0);return p<=o.significanceLevel&&S>=o.minimumEffectSize}).map(g=>({...g,severity:se(g.stats.adjustedPValue??g.stats.pValue??1,g.effect.effectSize??0)}))}}function Qe(i,a,s,o){if(!s?.length)return[];const e=s.filter(r=>j(r)&&(r.limitLow!==void 0||r.limitHigh!==void 0));if(!e.length)return[];const f=[],l=new Map(i.map(r=>[x(r),r]));for(const r of e){const n=r.testNumber;for(const c of a){const t=new Map;for(const m of c){const d=[];for(const g of m.dieKeys){const p=l.get(g);p&&d.push(p)}t.set(m.key,d)}const h=[];for(const m of c){const d=t.get(m.key),g=[];for(const[u,M]of t)if(u!==m.key)for(const B of M)g.push(B);const p=u=>u.testValues?.[n]!==void 0,S=u=>{const M=u.testValues?.[n];return M===void 0?!1:r.limitLow!==void 0&&M<r.limitLow||r.limitHigh!==void 0&&M>r.limitHigh},D=d.filter(p),y=g.filter(p);if(D.length<o.minimumSampleSize||y.length<o.minimumSampleSize)continue;const b=D.filter(S).length,$=y.filter(S).length,k=b/D.length,w=$/y.length,v=k-w,R=H(b,D.length,$,y.length);h.push({id:`specLimit:${n}:${m.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:n,label:r.name,unit:r.unit},comparison:{family:m.family,left:m.label,right:q(m.family)},effect:{direction:v===0?"different":v>0?"higher":"lower",absoluteDelta:v,relativeDelta:w===0?void 0:v/w,effectSize:v},stats:{method:"two-proportion-z",pValue:R,sampleSizeLeft:D.length,sampleSizeRight:y.length},summary:`${m.label} spec-fail rate for ${r.name} is ${(Math.abs(v)*100).toFixed(1)} pp ${v>0?"higher":"lower"} than the rest of the wafer`,highlight:{kind:"region",regionFamily:m.family,regionKeys:[m.key],dieKeys:[...m.dieKeys]}})}ne(h),f.push(...h.filter(m=>{const d=m.stats.adjustedPValue??m.stats.pValue??1,g=Math.abs(m.effect.absoluteDelta??0),p=Math.abs(m.effect.relativeDelta??0);return d<=o.significanceLevel&&(g>=o.minimumEffectSize||p>=o.minimumRelativeEffect)}).map(m=>({...m,severity:se(m.stats.adjustedPValue??m.stats.pValue??1,m.effect.effectSize??0)})))}}return f}function T(i){return i.highlight.regionKeys??[]}function Ue(i){const a=i.variable.bin??i.variable.index??"";return`${i.comparison.family}\0${i.variable.kind}\0${a}\0${i.effect.direction}`}function I(i,a){const s=T(i)[0]??"",o=O(s);return o.family==="ring"?o.ring??0:o.family==="sector"?ge(a).indexOf(o.sector??""):0}function Ge(i,a,s){if(a==="quadrant"){const l=new Map;for(const t of i){const h=O(T(t)[0]??"").quadrant;h&&l.set(h,t)}const r=[...l.keys()],n=new Set,c=[];for(const t of r){if(n.has(t))continue;const h=[],m=[t];for(n.add(t);m.length;){const d=m.pop();h.push(d);for(const g of r)!n.has(g)&&Ve(d,g)&&(n.add(g),m.push(g))}c.push(h.map(d=>l.get(d)))}return c}const o=[...i].sort((l,r)=>I(l,s)-I(r,s)),e=[];let f=[];for(const l of o){if(f.length===0){f.push(l);continue}const r=I(f[f.length-1],s);I(l,s)-r===1?f.push(l):(e.push(f),f=[l])}if(f.length&&e.push(f),a==="sector"&&e.length>1){const r=ge(s).length,n=I(e[0][0],s),c=I(e[e.length-1][e[e.length-1].length-1],s);if(n===0&&c===r-1){const t=e.pop();e[0]=[...t,...e[0]]}}return e}function Xe(i,a){const s=i.map(l=>O(T(l)[0]??"").ring).filter(l=>l!==void 0).sort((l,r)=>l-r),o=s[0],e=s[s.length-1];let f="";return o===e&&o===1&&a>1?f=" (core)":o===e&&o===a&&a>1&&(f=" (edge)"),o===e?`Ring ${o}${f}`:`Rings ${o}\u2013${e}`}function Je(i,a){const s=i.map(o=>O(T(o)[0]??"").sector??"");return s.length===1?`Sector ${s[0]}`:`Sectors ${s[0]}\u2013${s[s.length-1]}`}function Ze(i){const a=i.map(s=>O(T(s)[0]??"").quadrant??"");return a.length===1?a[0]:a.length===2?`Quadrants ${a[0]} & ${a[1]}`:`Quadrants ${a.slice(0,-1).join(", ")} & ${a[a.length-1]}`}function fe(i){return[...new Set(i)]}function et(i,a){const s=i[0],o=s.comparison.family,e=s.variable.kind,f=fe(i.flatMap(b=>b.highlight.dieKeys??[])),l=fe(i.flatMap(T)),r=new Set(f),n=o==="ring"?Xe(i,a.ringCount):o==="sector"?Je(i,a.sectorCount):Ze(i),c=e==="softBin"?a.softEligibleDies:e==="test"?a.testDies:a.eligibleDies,t=c.filter(b=>r.has(x(b))),h=c.filter(b=>!r.has(x(b)));let m,d,g,p,S;if(e==="test"){const b=s.variable.index,$=z=>z.testValues?.[b],k=t.map($).filter(z=>z!==void 0),w=h.map($).filter(z=>z!==void 0),{pValue:v,effectSize:R,delta:u}=We(k,w),M=X(w),B=M!==0?u/Math.abs(M):void 0;m={direction:u===0?"different":u>0?"higher":"lower",absoluteDelta:u,relativeDelta:B,effectSize:R},d={method:"welch-z-approx",pValue:v,sampleSizeLeft:k.length,sampleSizeRight:w.length},g=se(v,R),p=we(n,s.variable.label,u,B,o,s.variable.unit),S=`test:${b}`}else if(e==="yield"){const b=new Set(a.passBins),$=t.filter(N=>N.hbin!==void 0),k=h.filter(N=>N.hbin!==void 0),w=$.length,v=k.length,R=$.filter(N=>b.has(N.hbin)).length,u=k.filter(N=>b.has(N.hbin)).length,M=w>0?R/w:0,B=v>0?u/v:0,z=M-B,F=H(R,w,u,v);m={direction:z===0?"different":z>0?"higher":"lower",absoluteDelta:z,relativeDelta:B===0?void 0:z/B,effectSize:z},d={method:"two-proportion-z",pValue:F,sampleSizeLeft:w,sampleSizeRight:v},g=ie(F,z,m.relativeDelta),p=ye(n,z,o),S="yield"}else{const b=s.variable.bin,$=z=>e==="softBin"?z.sbin:z.hbin,k=t.filter(z=>$(z)===b).length,w=h.filter(z=>$(z)===b).length,v=k/t.length,R=w/h.length,u=v-R,M=H(k,t.length,w,h.length);m={direction:u===0?"different":u>0?"higher":"lower",absoluteDelta:u,relativeDelta:R===0?void 0:u/R,effectSize:u},d={method:"two-proportion-z",pValue:M,sampleSizeLeft:t.length,sampleSizeRight:h.length},g=ie(M,u,m.relativeDelta);const B=e==="softBin"?a.sbinDefs:a.hbinDefs;p=Se(n,$e(b,B,e==="softBin"?"SBin":"HBin"),u,o),S=`${e}:${b}`}const D=fe(l.map(b=>b.replace(`${o}:`,""))).join("-"),y=e==="hardBin"||e==="softBin"?{kind:"bin",bin:s.variable.bin,regionKeys:l,dieKeys:f}:{kind:"region",regionFamily:o,regionKeys:l,dieKeys:f};return{...s,id:`${S}:${o}:${D}`,severity:g,comparison:{family:o,left:n,right:q(o)},effect:m,stats:d,summary:p,highlight:y,relatedIds:i.map(b=>b.id)}}function tt(i,a){const s=new Set(["ring","quadrant","sector"]),o=[],e=new Map;for(const l of i){if(!s.has(l.comparison.family)){o.push(l);continue}const r=Ue(l),n=e.get(r)??[];n.push(l),e.set(r,n)}const f=[];for(const l of e.values()){if(l.length===1){f.push(l[0]);continue}const r=l[0].comparison.family;for(const n of Ge(l,r,a.sectorCount))f.push(n.length===1?n[0]:et(n,a))}return[...o,...f]}function nt(i){const a=new Map,s=new Map;for(const e of i){const f=x(e);e.hbin!==void 0&&e.hbin!==null&&(a.get(e.hbin)??a.set(e.hbin,new Set).get(e.hbin)).add(f),e.sbin!==void 0&&e.sbin!==null&&(s.get(e.sbin)??s.set(e.sbin,new Set).get(e.sbin)).add(f)}const o=new Set;for(const[e,f]of a)for(const[l,r]of s){if(f.size!==r.size)continue;let n=!0;for(const c of f)if(!r.has(c)){n=!1;break}n&&o.add(`${e}:${l}`)}return o}function it(i){return`${i.comparison.family}\0${i.comparison.left}`}function st(i,a,s){const o=(r,n)=>{r.absorbedIds=[...r.absorbedIds??[],n.id]},e=new Map;for(const r of i){const n=it(r);(e.get(n)??e.set(n,[]).get(n)).push(r)}const f=nt(a),l=new Set;for(const r of e.values()){for(const c of r)if(!(c.variable.kind!=="hardBin"||c.variable.bin===void 0))for(const t of r){if(t.variable.kind!=="softBin"||t.variable.bin===void 0||l.has(t.id)||!f.has(`${c.variable.bin}:${t.variable.bin}`)||t.effect.direction!==c.effect.direction)continue;o(c,t),l.add(t.id);const h=c.variable.label.replace(/^HBin\s+/,""),m=t.variable.label.replace(/^SBin\s+/,""),d=h===m?`HBin and SBin ${h} (same dies)`:`${c.variable.label} and ${t.variable.label} (same dies)`;c.summary=c.summary.replace(c.variable.label,d),c.variable.label=d;break}if(s.length!==1)continue;const n=r.find(c=>c.variable.kind==="yield");if(n){for(const c of r)if(!(c===n||l.has(c.id)||!((c.variable.kind==="hardBin"||c.variable.kind==="softBin")&&c.variable.bin===s[0]))){o(n,c),l.add(c.id);for(const h of c.absorbedIds??[])n.absorbedIds=[...n.absorbedIds??[],h]}}}}export function analyzeWaferMap(i,a={}){const{resolved:s,warnings:o}=Ke(a),e=Te(i),f=e.isLotStack,l=e.aggrMethod,r=!f||l==="mode"||l==="countBin"||l==="percent",n=e.dies.filter(u=>je(u,s)),c=n.filter(de),t=xe(s,n.length),h=e.dies.filter(u=>ee(u,t)),m=h.filter(de),d=Fe(m,e.wafer,t.ringCount),g=Be(m,e.wafer,t.ringCount),p=t.enableReticlePositionAnalysis?Ce(m,e.reticleConfig):[],S=t.enableTestSiteAnalysis===!1?[]:Pe(h,t.enableTestSiteAnalysis===!0),D=t.enableAngularAnalysis?Ne(m,e.wafer,t.sectorCount):[],y=[];if(t.enableYieldAnalysis&&r&&y.push(...Y(n,d,t.passBins,t),...Y(n,g,t.passBins,t),...Y(n,p,t.passBins,t),...Y(n,S,t.passBins,t),...Y(n,D,t.passBins,t)),t.enableHardBinAnalysis&&r&&y.push(...V(n,d,"hard",e.hbinDefs,"hardBin",t),...V(n,g,"hard",e.hbinDefs,"hardBin",t),...V(n,p,"hard",e.hbinDefs,"hardBin",t),...V(n,S,"hard",e.hbinDefs,"hardBin",t),...V(n,D,"hard",e.hbinDefs,"hardBin",t)),t.enableSoftBinAnalysis){const u=n.filter(M=>M.sbin!==void 0);y.push(...V(u,d,"soft",e.sbinDefs,"softBin",t),...V(u,g,"soft",e.sbinDefs,"softBin",t),...V(u,p,"soft",e.sbinDefs,"softBin",t),...V(u,S,"soft",e.sbinDefs,"softBin",t),...V(u,D,"soft",e.sbinDefs,"softBin",t))}const b=[...o];let $;if(t.enableTestValueAnalysis){const u=Q(n,d,e.testDefs,t),M=Q(n,g,e.testDefs,t),B=Q(n,p,e.testDefs,t),z=Q(n,S,e.testDefs,t),F=Q(n,D,e.testDefs,t);y.push(...u.findings,...M.findings,...B.findings,...z.findings,...F.findings),u.warning&&b.push(u.warning),$=u.activeTestNumbers,y.push(...Qe(n,[d,g,p,S,D],e.testDefs,t)),y.push(...W(n,d,e.testDefs,t),...W(n,g,e.testDefs,t),...W(n,p,e.testDefs,t),...W(n,S,e.testDefs,t),...W(n,D,e.testDefs,t))}if(t.enableClusterAnalysis){const u=Oe(f,r,e.testDefs);(!f||r||u!==void 0)&&y.push(...Le(c,e.wafer,{...t,isFailingDie:u}))}const k=tt(y,{eligibleDies:n,softEligibleDies:n.filter(u=>u.sbin!==void 0),testDies:n,passBins:t.passBins,ringCount:t.ringCount,sectorCount:t.sectorCount,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs});if(y.length=0,y.push(...k),t.enablePatternClassification&&r){const u=Ee(c,e.wafer,{passBins:t.passBins,ringCount:t.ringCount});if(u!==null&&u.pattern!=="random"&&u.pattern!=="none"){let Me=function(C){const L=T(C).map(K=>O(K).ring).filter(K=>K!==void 0);if(L.length===0)return!0;const ce=L.some(K=>K===ze),ue=L.some(K=>K===1);return A==="edge-ring"||A==="edge-local"?ce:A==="center"?ue:A==="donut"?!ce&&!ue:!0};const B={center:"Center cluster",donut:"Donut","edge-ring":"Edge-ring","edge-local":"Edge-local",scratch:"Scratch","near-full":"Near-full"}[u.pattern]??u.pattern,z=u.confidence==="high"?"unusual":u.confidence==="medium"?"notable":"info",F=u.features,N=C=>`${(C*100).toFixed(0)}%`,E=u.pattern==="edge-ring"||u.pattern==="edge-local"?`${N(F.edgeRdd)} of edge dies failing`:u.pattern==="center"||u.pattern==="donut"?`centroid at ${N(F.centroidDistNorm)} of wafer radius from centre`:u.pattern==="scratch"?`linear score ${F.linearScore.toFixed(2)}, eccentricity ${F.eccentricity.toFixed(2)}`:`${N(F.globalRdd)} of dies failing`,P=n.filter(C=>{const L=C.hbin??C.sbin;return L!==void 0&&!new Set(t.passBins).has(L)}),U={"edge-ring":["ring","edge-arc"],"edge-local":["edge-arc","sector","quadrant"],center:["ring","cluster"],donut:["ring"],scratch:["cluster","sector","quadrant"],"near-full":["ring"]},G=new Set(U[u.pattern]??[]),A=u.pattern,ze=t.ringCount,Z=[];for(const C of y){if(!G.has(C.comparison.family)||C.comparison.family==="ring"&&!Me(C))continue;Z.push(C.id);const L=C.comparison.family==="ring"||C.comparison.family==="quadrant"||C.comparison.family==="sector";z!=="info"&&L&&(C.severity="info")}y.push({id:`spatial-pattern:${u.pattern}`,level:"wafer",severity:z,variable:{kind:"spatialPattern",label:B},comparison:{family:"spatial-pattern",left:B,right:"Wafer"},effect:{direction:"different",effectSize:F.globalRdd},stats:{method:"geometry",sampleSizeLeft:P.length,sampleSizeRight:n.length},summary:`Spatial pattern: ${B.toLowerCase()} (${u.confidence} confidence) \u2014 ${E}${u.note?` [${u.note}]`:""}`,highlight:{kind:"dies",dieKeys:P.map(C=>x(C))},relatedIds:Z.length>0?Z:void 0})}}y.sort((u,M)=>{const B=u.stats.adjustedPValue??u.stats.pValue??1,z=M.stats.adjustedPValue??M.stats.pValue??1;return B!==z?B-z:Math.abs(M.effect.absoluteDelta??0)-Math.abs(u.effect.absoluteDelta??0)});const w=He(e.dies,n.length,e.yield.yieldPercent);f&&(w.isLotStack=!0,l&&(w.aggregationMethod=l));const v=qe(e.dies,e.testDefs);v&&(w.testSpecYield=v);const R=computeFunctionalYield(e.dies,e.testDefs);if(R&&(w.functionalYield=R),$===void 0&&t.computePerTestStats){const u=ke(n,t.testNumbers,e.testDefs);u.warning&&b.push(u.warning),$=u.testNumbers}if($?.length){const u=Ie(e.dies,$,e.testDefs,t.minimumSampleSize);u&&(w.perTestStats=u)}return b.length>0&&(w.warnings=b),st(y,n,t.passBins),{level:"wafer",hasNotableFindings:y.some(u=>u.severity==="notable"||u.severity==="unusual"),findings:y,wafer:e.wafer.metadata??void 0,stats:w}}
1
+ import{isYieldEligibleDie as te,getDieKey as x,isPositionedDie as ge}from"../core/dies.js";import{buildWaferMap as Be,getTestPassStatus as he,isParametricTest as A}from"../renderer/buildWaferMap.js";import{buildQuadrantRegions as Fe,buildReticlePositionRegions as Ne,buildRingRegions as Ve,buildSectorRegions as Ce,buildTestSiteRegions as Pe,sectorCompassNames as me,areQuadrantsAdjacent as Le,parseRegionKey as O}from"./regions.js";import{buildClusterFindings as Ee}from"./clusterDetection.js";import{classifyPattern as Ke}from"./patternClassification.js";import{normalCdf as pe}from"./math.js";import{quantile as ne}from"./math.js";import{mean as J,clamp01 as be}from"../core/utils.js";const xe={significanceLevel:{min:1e-6,max:1},minimumEffectSize:{min:0,max:1},minimumRelativeEffect:{min:0,max:Number.MAX_SAFE_INTEGER}},ye=[4,8,16,32];function Te(n){const a=Object.fromEntries(Object.entries(n).filter(([,t])=>t!==void 0)),s={...T,...a},o=[],e=[];for(const t of["significanceLevel","minimumEffectSize","minimumRelativeEffect"]){const{min:l,max:i}=xe[t],u=s[t];if(typeof u!="number"||!Number.isFinite(u)){e.push(`${t}=${String(u)} is not a finite number (using ${T[t]})`),s[t]=T[t];continue}const f=Math.min(i,Math.max(l,u));f!==u&&(e.push(`${t}=${u} is outside ${l}\u2013${i} (using ${f})`),s[t]=f)}if(ye.includes(s.sectorCount)||(e.push(`sectorCount=${s.sectorCount} is not one of ${ye.join(", ")} (using ${T.sectorCount})`),s.sectorCount=T.sectorCount),e.length){const t=`analyzeWaferMap: ${e.join("; ")}. The analysis ran with the corrected values.`;console.warn(`[wafermap] ${t}`),o.push({code:"analysis-option-corrected",message:t,severity:"warning"})}const c=["passBins","ringCount"].filter(t=>a[t]!==void 0);if(c.length){const t=`analyzeWaferMap: ${c.join(" and ")} ${c.length>1?"are":"is"} no longer an analysis option and was ignored. Set ${c.length>1?"them":"it"} on buildWaferMap; the analysis used the value the map was built with.`;console.warn(`[wafermap] ${t}`),o.push({code:"analysis-option-corrected",message:t,severity:"warning"})}const r=["enableYieldAnalysis","enableHardBinAnalysis","enableSoftBinAnalysis","enableReticlePositionAnalysis","enableTestSiteAnalysis","enableClusterAnalysis","enableAngularAnalysis","enablePatternClassification"].filter(t=>a[t]!==void 0);if(r.length){const t=`analyzeWaferMap: ${r.join(", ")} ${r.length>1?"are":"is"} no longer an option and was ignored: every analysis now runs. Filter the findings (filterFindings) to show fewer.`;console.warn(`[wafermap] ${t}`),o.push({code:"analysis-option-corrected",message:t,severity:"warning"})}return{resolved:s,warnings:o}}const T={ringCount:4,passBins:[1],significanceLevel:.05,minimumEffectSize:.2,minimumRelativeEffect:1,minimumSampleSize:5,enableTestValueAnalysis:!1,computePerTestStats:!1,sectorCount:8,minimumClusterSize:5};function je(n,a){const s={...n};return s.minimumClusterSize=Math.max(5,Math.round(a*.003)),s}function Ae(n){return"wafer"in n&&"dies"in n&&"view"in n?n:Be(n)}function Oe(n){return te(n)?n.hbin!==void 0||n.sbin!==void 0||n.testValues!==void 0&&Object.keys(n.testValues).length>0:!1}function qe(n,a,s){if(!n||a)return;const o=(s??[]).filter(e=>A(e)&&(e.limitLow!==void 0||e.limitHigh!==void 0));if(o.length!==0)return e=>{for(const c of o){const r=c.testNumber,t=e.testValues?.[r];if(t!==void 0&&(c.limitLow!==void 0&&t<c.limitLow||c.limitHigh!==void 0&&t>c.limitHigh))return!0}return!1}}function He(n,a,s){const o=new Set,e=new Set,c=new Set,r=new Map,t=new Map;for(const l of n){if(l.testValues)for(const i of Object.keys(l.testValues))o.add(Number(i));if(l.testPass)for(const i of Object.keys(l.testPass))o.add(Number(i));l.hbin!==void 0&&e.add(l.hbin),l.sbin!==void 0&&c.add(l.sbin),te(l)&&(l.hbin!==void 0&&r.set(l.hbin,(r.get(l.hbin)??0)+1),l.sbin!==void 0&&t.set(l.sbin,(t.get(l.sbin)??0)+1))}return{totalDies:n.length,analyzedDies:a,excludedDies:n.length-a,yieldPercent:s,testsConsidered:[...o].sort((l,i)=>l-i),hardBinsConsidered:[...e].sort((l,i)=>l-i),softBinsConsidered:[...c].sort((l,i)=>l-i),...r.size?{hardBinCounts:Object.fromEntries(r)}:{},...t.size?{softBinCounts:Object.fromEntries(t)}:{}}}function Ie(n,a){if(!a?.length)return;const s=a.filter(e=>A(e)&&(e.limitLow!==void 0||e.limitHigh!==void 0));if(!s.length)return;const o=[];for(const e of s){const c=e.testNumber;let r=0,t=0,l=0,i=0;for(const u of n){if(u.partial||u.edgeExcluded)continue;const f=u.testValues?.[c];f!==void 0&&(i++,e.limitLow!==void 0&&f<e.limitLow?t++:e.limitHigh!==void 0&&f>e.limitHigh?l++:r++)}o.push({testNumber:c,label:e.name,passDies:r,failLowDies:t,failHighDies:l,totalDies:i,yieldPercent:i>0?r/i*100:null})}return o.length?o:void 0}export function computeFunctionalYield(n,a){if(!a?.length)return;const s=a.filter(e=>!A(e));if(!s.length)return;const o=[];for(const e of s){const c=e.testNumber;let r=0,t=0;for(const i of n){if(i.partial||i.edgeExcluded)continue;const u=he(i,c,e);u!==void 0&&(u?r++:t++)}const l=r+t;o.push({testNumber:c,label:e.name,passDies:r,failDies:t,totalDies:l,passRatePercent:l>0?r/l*100:null})}return o.length?o:void 0}function _e(n,a,s,o){const e=[];for(const c of a){const r=[];for(const f of n){if(f.partial||f.edgeExcluded)continue;const h=f.testValues?.[c];h!==void 0&&r.push(h)}if(r.length<o)continue;const t=J(r),l=Math.sqrt(fe(r,t)),i=r.slice().sort((f,h)=>f-h),u=s?.find(f=>f.testNumber===c)?.name??String(c);e.push({testNumber:c,label:u,count:r.length,min:i[0],max:i[i.length-1],mean:t,stddev:l,median:ne(i,.5),q1:ne(i,.25),q3:ne(i,.75)})}return e.length?e:void 0}function q(n,a,s,o){const e=(n+s)/(a+o),c=e*(1-e)*(1/a+1/o);if(!Number.isFinite(c)||c<=0)return 1;const r=(n/a-s/o)/Math.sqrt(c);return be(2*(1-pe(Math.abs(r))))}function ie(n){const a=new Map;for(const s of n){const o=`${s.variable.kind}:${s.comparison.family}`,e=a.get(o)??[];e.push(s),a.set(o,e)}for(const s of a.values()){const o=[...s].filter(r=>r.stats.pValue!==void 0).sort((r,t)=>(r.stats.pValue??1)-(t.stats.pValue??1)),e=o.length;let c=1;for(let r=e-1;r>=0;r--){const t=o[r],l=t.stats.pValue??1,i=Math.min(1,Math.min(c,l*e/(r+1)));t.stats.adjustedPValue=i,c=i}}return n}function se(n,a,s){const o=Math.abs(a),e=s!==void 0?Math.abs(s):0;return n<=.01&&(o>=.3||e>=2.5)?"unusual":n<=.05&&(o>=.2||e>=1.5)?"notable":"info"}function oe(n,a){return n<=.01&&Math.abs(a)>=.5?"unusual":n<=.05&&Math.abs(a)>=.15?"notable":"info"}function Z(n){return n==="reticle-position"?"other reticle positions":n==="test-site"?"other test sites":"the rest of the map"}function H(n){return n==="reticle-position"?"Other reticle positions":n==="test-site"?"Other test sites":"Rest of map"}function Se(n,a,s){const o=(Math.abs(a)*100).toFixed(1);return`${n} yield is ${o} percentage points ${a>0?"higher":"lower"} than ${Z(s)}`}function ae(n,a){return a==="quadrant"&&!n.startsWith("Quadrant")?`quadrant ${n}`:n}function we(n,a,s,o){const e=ae(n,o),c=(Math.abs(s)*100).toFixed(1);return`${e} has ${a} occurrence ${c} percentage points ${s>0?"higher":"lower"} than ${Z(o)}`}function We(n,a,s,o){const e=ae(n,o),c=(Math.abs(s)*100).toFixed(1);return`${e} has ${a} pass rate ${c} percentage points ${s>0?"higher":"lower"} than ${Z(o)}`}function $e(n,a,s,o,e,c){const r=ae(n,e),t=Z(e),l=s>0?"higher":"lower";if(o!==void 0&&Number.isFinite(o)){const u=(Math.abs(o)*100).toFixed(1);return`${r} mean ${a} is ${u}% ${l} than ${t}`}const i=c?` ${c}`:"";return`${r} mean ${a} is ${Math.abs(s).toPrecision(3)}${i} ${l} than ${t}`}function ve(n,a,s){const o=a?.find(e=>e.bin===n);return o?.name?`${s} ${n} (${o.name})`:`${s} ${n}`}function Ye(n,a){const s=a?.find(o=>o.testNumber===n);return{label:s?.name??`Test ${n}`,unit:s?.unit}}function re(n,a){const s=new Map(n.map(e=>[x(e),e])),o=new Map;for(const e of a){const c=[];for(const r of e.dieKeys){const t=s.get(r);t&&c.push(t)}o.set(e.key,c)}return o}function le(n,a){return ie(n),n.filter(s=>{const o=s.stats.adjustedPValue??s.stats.pValue??1,e=Math.abs(s.effect.absoluteDelta??0),c=Math.abs(s.effect.relativeDelta??0);return o<=a.significanceLevel&&(e>=a.minimumEffectSize||c>=a.minimumRelativeEffect)}).map(s=>({...s,severity:se(s.stats.adjustedPValue??s.stats.pValue??1,s.effect.absoluteDelta??0,s.effect.relativeDelta)}))}function W(n,a,s,o){const e=new Set(s),c=re(n,a),r=new Map,t=new Map;for(const[i,u]of c){let f=0,h=0;for(const d of u)d.hbin!==void 0&&(h++,e.has(d.hbin)&&f++);r.set(i,f),t.set(i,h)}const l=[];for(const i of a){const u=t.get(i.key)??0,f=r.get(i.key)??0;let h=0,d=0;for(const[$,m]of t)$!==i.key&&(h+=m,d+=r.get($)??0);if(u<o.minimumSampleSize||h<o.minimumSampleSize)continue;const p=f/u,b=d/h,y=p-b,z=q(f,u,d,h);l.push({id:`yield:${i.key}`,level:"wafer",severity:"info",variable:{kind:"yield",label:"Yield"},comparison:{family:i.family,left:i.label,right:H(i.family)},effect:{direction:y===0?"different":y>0?"higher":"lower",absoluteDelta:y,relativeDelta:b===0?void 0:y/b,effectSize:y},stats:{method:"two-proportion-z",pValue:z,sampleSizeLeft:u,sampleSizeRight:h},summary:Se(i.label,y,i.family),highlight:{kind:"region",regionFamily:i.family,regionKeys:[i.key],dieKeys:[...i.dieKeys]}})}return le(l,o)}function P(n,a,s,o,e,c){const r=d=>s==="soft"?d.sbin:d.hbin,t=[...new Set(n.map(r).filter(d=>d!==void 0))].sort((d,p)=>d-p),l=re(n,a),i=new Map,u=new Map;for(const[d,p]of l){const b=new Map;for(const y of p){const z=r(y);z!==void 0&&b.set(z,(b.get(z)??0)+1)}i.set(d,b),u.set(d,p.length)}const f=[],h=e==="hardBin"?"HBin":"SBin";for(const d of a){const p=u.get(d.key)??0,b=i.get(d.key);let y=0;const z=new Map;for(const[$,m]of i)if($!==d.key){y+=u.get($)??0;for(const[w,D]of m)z.set(w,(z.get(w)??0)+D)}if(!(p<c.minimumSampleSize||y<c.minimumSampleSize))for(const $ of t){const m=b.get($)??0,w=z.get($)??0,D=m/p,v=w/y,S=D-v,R=q(m,p,w,y),M=ve($,o,h);f.push({id:`${e}:${$}:${d.key}`,level:"wafer",severity:"info",variable:{kind:e,bin:$,label:M},comparison:{family:d.family,left:d.label,right:H(d.family)},effect:{direction:S===0?"different":S>0?"higher":"lower",absoluteDelta:S,relativeDelta:v===0?void 0:S/v,effectSize:S},stats:{method:"two-proportion-z",pValue:R,sampleSizeLeft:p,sampleSizeRight:y},summary:we(d.label,M,S,d.family),highlight:{kind:"bin",bin:$,regionKeys:[d.key],dieKeys:[...d.dieKeys]}})}}return le(f,c)}function Y(n,a,s,o){const e=(s??[]).filter(u=>!A(u));if(!e.length)return[];const c=e.map(u=>u.testNumber),r=re(n,a),t=new Map,l=new Map;for(const[u,f]of r){const h=new Uint32Array(e.length),d=new Uint32Array(e.length);for(const p of f)for(let b=0;b<e.length;b++){const y=he(p,c[b],e[b]);y!==void 0&&(d[b]++,y&&h[b]++)}t.set(u,h),l.set(u,d)}const i=[];for(const u of a){const f=t.get(u.key),h=l.get(u.key);for(let d=0;d<e.length;d++){let p=0,b=0;for(const[S,R]of l)S!==u.key&&(b+=R[d],p+=t.get(S)[d]);const y=h[d],z=f[d];if(y<o.minimumSampleSize||b<o.minimumSampleSize)continue;const $=z/y,m=p/b,w=$-m,D=q(z,y,p,b),v=c[d];i.push({id:`functional:${v}:${u.key}`,level:"wafer",severity:"info",variable:{kind:"functionalTest",index:v,label:`${e[d].name} pass rate`},comparison:{family:u.family,left:u.label,right:H(u.family)},effect:{direction:w===0?"different":w>0?"higher":"lower",absoluteDelta:w,relativeDelta:m===0?void 0:w/m,effectSize:w},stats:{method:"two-proportion-z",pValue:D,sampleSizeLeft:y,sampleSizeRight:b},summary:We(u.label,e[d].name,w,u.family),highlight:{kind:"region",regionFamily:u.family,regionKeys:[u.key],dieKeys:[...u.dieKeys]}})}}return le(i,o)}function fe(n,a){return n.length<2?0:n.reduce((s,o)=>s+(o-a)**2,0)/(n.length-1)}function De(n,a,s,o,e,c){const r=Math.sqrt(s/n+c/o),t=a-e;if(!Number.isFinite(r)||r===0)return{pValue:1,effectSize:0,delta:t};const l=t/r,i=Math.sqrt(Math.max(0,(s+c)/2)),u=i===0?t:t/i;return{pValue:be(2*(1-pe(Math.abs(l)))),effectSize:u,delta:t}}function Qe(n,a){const s=J(n),o=J(a);return De(n.length,s,fe(n,s),a.length,o,fe(a,o))}const ke=250;function ze(n,a,s){const o=r=>r.filter(t=>A(s?.find(l=>l.testNumber===t)));if(a)return{testNumbers:o(a.slice().sort((r,t)=>r-t))};const e=new Set;let c=!1;e:for(const r of n)for(const t of Object.keys(r.testValues??{})){const l=Number(t);if(!e.has(l)&&(e.add(l),e.size>ke)){c=!0;break e}}if(c){const r=`Test-value analysis was skipped: more than ${ke} tests were found in the die data, so no test findings were computed. Pass testNumbers: [...] to analyse specific tests.`;return console.warn(`[wafermap] analyzeWaferMap: ${r}`),{testNumbers:[],warning:{code:"test-count-capped",message:r,severity:"warning"}}}return{testNumbers:o([...e].sort((r,t)=>r-t))}}function Q(n,a,s,o){const e=ze(n,o.testNumbers,s);if(e.warning)return{findings:[],warning:e.warning};const c=e.testNumbers,r=new Map;for(let p=0;p<a.length;p++)for(const b of a[p].dieKeys)r.set(b,p);const t=a.length,l=[],i=[];for(const p of n){const b=r.get(x(p));b!==void 0&&(l.push(p),i.push(b))}const u=[],f=new Float64Array(t),h=new Float64Array(t),d=new Float64Array(t);for(const p of c){f.fill(0),h.fill(0),d.fill(0);let b=0,y=0,z=0,$;for(let m=0;m<l.length;m++){const D=l[m].testValues?.[p];if(D===void 0)continue;$===void 0&&($=D);const v=D-$,S=i[m];f[S]+=1,h[S]+=v,d[S]+=v*v,b+=1,y+=v,z+=v*v}if($!==void 0)for(let m=0;m<t;m++){const w=f[m],D=b-w;if(w<o.minimumSampleSize||D<o.minimumSampleSize)continue;const v=h[m]/w,R=(y-h[m])/D,M=v+$,g=R+$,B=w>1?Math.max(0,d[m]-w*v*v)/(w-1):0,k=D>1?Math.max(0,z-d[m]-D*R*R)/(D-1):0,{pValue:V,effectSize:F,delta:L}=De(w,M,B,D,g,k),C=a[m],{label:_,unit:U}=Ye(p,s),G=g!==0?L/Math.abs(g):void 0;u.push({id:`test:${p}:${C.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:p,label:_,unit:U},comparison:{family:C.family,left:C.label,right:H(C.family)},effect:{direction:L===0?"different":L>0?"higher":"lower",absoluteDelta:L,relativeDelta:G,effectSize:F},stats:{method:"welch-z-approx",pValue:V,sampleSizeLeft:w,sampleSizeRight:D},summary:$e(C.label,_,L,G,C.family,U),highlight:{kind:"region",regionFamily:C.family,regionKeys:[C.key],dieKeys:[...C.dieKeys]}})}}return ie(u),{activeTestNumbers:c,findings:u.filter(p=>{const b=p.stats.adjustedPValue??p.stats.pValue??1,y=Math.abs(p.effect.effectSize??0);return b<=o.significanceLevel&&y>=o.minimumEffectSize}).map(p=>({...p,severity:oe(p.stats.adjustedPValue??p.stats.pValue??1,p.effect.effectSize??0)}))}}function Ue(n,a,s,o){if(!s?.length)return[];const e=s.filter(t=>A(t)&&(t.limitLow!==void 0||t.limitHigh!==void 0));if(!e.length)return[];const c=[],r=new Map(n.map(t=>[x(t),t]));for(const t of e){const l=t.testNumber;for(const i of a){const u=new Map;for(const h of i){const d=[];for(const p of h.dieKeys){const b=r.get(p);b&&d.push(b)}u.set(h.key,d)}const f=[];for(const h of i){const d=u.get(h.key),p=[];for(const[M,g]of u)if(M!==h.key)for(const B of g)p.push(B);const b=M=>M.testValues?.[l]!==void 0,y=M=>{const g=M.testValues?.[l];return g===void 0?!1:t.limitLow!==void 0&&g<t.limitLow||t.limitHigh!==void 0&&g>t.limitHigh},z=d.filter(b),$=p.filter(b);if(z.length<o.minimumSampleSize||$.length<o.minimumSampleSize)continue;const m=z.filter(y).length,w=$.filter(y).length,D=m/z.length,v=w/$.length,S=D-v,R=q(m,z.length,w,$.length);f.push({id:`specLimit:${l}:${h.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:l,label:t.name,unit:t.unit},comparison:{family:h.family,left:h.label,right:H(h.family)},effect:{direction:S===0?"different":S>0?"higher":"lower",absoluteDelta:S,relativeDelta:v===0?void 0:S/v,effectSize:S},stats:{method:"two-proportion-z",pValue:R,sampleSizeLeft:z.length,sampleSizeRight:$.length},summary:`${h.label} spec-fail rate for ${t.name} is ${(Math.abs(S)*100).toFixed(1)} pp ${S>0?"higher":"lower"} than the rest of the wafer`,highlight:{kind:"region",regionFamily:h.family,regionKeys:[h.key],dieKeys:[...h.dieKeys]}})}ie(f),c.push(...f.filter(h=>{const d=h.stats.adjustedPValue??h.stats.pValue??1,p=Math.abs(h.effect.absoluteDelta??0),b=Math.abs(h.effect.relativeDelta??0);return d<=o.significanceLevel&&(p>=o.minimumEffectSize||b>=o.minimumRelativeEffect)}).map(h=>({...h,severity:oe(h.stats.adjustedPValue??h.stats.pValue??1,h.effect.effectSize??0)})))}}return c}function j(n){return n.highlight.regionKeys??[]}function Ge(n){const a=n.variable.bin??n.variable.index??"";return`${n.comparison.family}\0${n.variable.kind}\0${a}\0${n.effect.direction}`}function I(n,a){const s=j(n)[0]??"",o=O(s);return o.family==="ring"?o.ring??0:o.family==="sector"?me(a).indexOf(o.sector??""):0}function Xe(n,a,s){if(a==="quadrant"){const r=new Map;for(const u of n){const f=O(j(u)[0]??"").quadrant;f&&r.set(f,u)}const t=[...r.keys()],l=new Set,i=[];for(const u of t){if(l.has(u))continue;const f=[],h=[u];for(l.add(u);h.length;){const d=h.pop();f.push(d);for(const p of t)!l.has(p)&&Le(d,p)&&(l.add(p),h.push(p))}i.push(f.map(d=>r.get(d)))}return i}const o=[...n].sort((r,t)=>I(r,s)-I(t,s)),e=[];let c=[];for(const r of o){if(c.length===0){c.push(r);continue}const t=I(c[c.length-1],s);I(r,s)-t===1?c.push(r):(e.push(c),c=[r])}if(c.length&&e.push(c),a==="sector"&&e.length>1){const t=me(s).length,l=I(e[0][0],s),i=I(e[e.length-1][e[e.length-1].length-1],s);if(l===0&&i===t-1){const u=e.pop();e[0]=[...u,...e[0]]}}return e}function Je(n,a){const s=n.map(r=>O(j(r)[0]??"").ring).filter(r=>r!==void 0).sort((r,t)=>r-t),o=s[0],e=s[s.length-1];let c="";return o===e&&o===1&&a>1?c=" (core)":o===e&&o===a&&a>1&&(c=" (edge)"),o===e?`Ring ${o}${c}`:`Rings ${o}\u2013${e}`}function Ze(n,a){const s=n.map(o=>O(j(o)[0]??"").sector??"");return s.length===1?`Sector ${s[0]}`:`Sectors ${s[0]}\u2013${s[s.length-1]}`}function et(n){const a=n.map(s=>O(j(s)[0]??"").quadrant??"");return a.length===1?a[0]:a.length===2?`Quadrants ${a[0]} & ${a[1]}`:`Quadrants ${a.slice(0,-1).join(", ")} & ${a[a.length-1]}`}function ce(n){return[...new Set(n)]}function tt(n,a){const s=n[0],o=s.comparison.family,e=s.variable.kind,c=ce(n.flatMap(m=>m.highlight.dieKeys??[])),r=ce(n.flatMap(j)),t=new Set(c),l=o==="ring"?Je(n,a.ringCount):o==="sector"?Ze(n,a.sectorCount):et(n),i=e==="softBin"?a.softEligibleDies:e==="test"?a.testDies:a.eligibleDies,u=i.filter(m=>t.has(x(m))),f=i.filter(m=>!t.has(x(m)));let h,d,p,b,y;if(e==="test"){const m=s.variable.index,w=k=>k.testValues?.[m],D=u.map(w).filter(k=>k!==void 0),v=f.map(w).filter(k=>k!==void 0),{pValue:S,effectSize:R,delta:M}=Qe(D,v),g=J(v),B=g!==0?M/Math.abs(g):void 0;h={direction:M===0?"different":M>0?"higher":"lower",absoluteDelta:M,relativeDelta:B,effectSize:R},d={method:"welch-z-approx",pValue:S,sampleSizeLeft:D.length,sampleSizeRight:v.length},p=oe(S,R),b=$e(l,s.variable.label,M,B,o,s.variable.unit),y=`test:${m}`}else if(e==="yield"){const m=new Set(a.passBins),w=u.filter(F=>F.hbin!==void 0),D=f.filter(F=>F.hbin!==void 0),v=w.length,S=D.length,R=w.filter(F=>m.has(F.hbin)).length,M=D.filter(F=>m.has(F.hbin)).length,g=v>0?R/v:0,B=S>0?M/S:0,k=g-B,V=q(R,v,M,S);h={direction:k===0?"different":k>0?"higher":"lower",absoluteDelta:k,relativeDelta:B===0?void 0:k/B,effectSize:k},d={method:"two-proportion-z",pValue:V,sampleSizeLeft:v,sampleSizeRight:S},p=se(V,k,h.relativeDelta),b=Se(l,k,o),y="yield"}else{const m=s.variable.bin,w=k=>e==="softBin"?k.sbin:k.hbin,D=u.filter(k=>w(k)===m).length,v=f.filter(k=>w(k)===m).length,S=D/u.length,R=v/f.length,M=S-R,g=q(D,u.length,v,f.length);h={direction:M===0?"different":M>0?"higher":"lower",absoluteDelta:M,relativeDelta:R===0?void 0:M/R,effectSize:M},d={method:"two-proportion-z",pValue:g,sampleSizeLeft:u.length,sampleSizeRight:f.length},p=se(g,M,h.relativeDelta);const B=e==="softBin"?a.sbinDefs:a.hbinDefs;b=we(l,ve(m,B,e==="softBin"?"SBin":"HBin"),M,o),y=`${e}:${m}`}const z=ce(r.map(m=>m.replace(`${o}:`,""))).join("-"),$=e==="hardBin"||e==="softBin"?{kind:"bin",bin:s.variable.bin,regionKeys:r,dieKeys:c}:{kind:"region",regionFamily:o,regionKeys:r,dieKeys:c};return{...s,id:`${y}:${o}:${z}`,severity:p,comparison:{family:o,left:l,right:H(o)},effect:h,stats:d,summary:b,highlight:$,relatedIds:n.map(m=>m.id)}}function nt(n,a){const s=new Set(["ring","quadrant","sector"]),o=[],e=new Map;for(const r of n){if(!s.has(r.comparison.family)){o.push(r);continue}const t=Ge(r),l=e.get(t)??[];l.push(r),e.set(t,l)}const c=[];for(const r of e.values()){if(r.length===1){c.push(r[0]);continue}const t=r[0].comparison.family;for(const l of Xe(r,t,a.sectorCount))c.push(l.length===1?l[0]:tt(l,a))}return[...o,...c]}function it(n){const a=new Map,s=new Map;for(const e of n){const c=x(e);e.hbin!==void 0&&e.hbin!==null&&(a.get(e.hbin)??a.set(e.hbin,new Set).get(e.hbin)).add(c),e.sbin!==void 0&&e.sbin!==null&&(s.get(e.sbin)??s.set(e.sbin,new Set).get(e.sbin)).add(c)}const o=new Set;for(const[e,c]of a)for(const[r,t]of s){if(c.size!==t.size)continue;let l=!0;for(const i of c)if(!t.has(i)){l=!1;break}l&&o.add(`${e}:${r}`)}return o}function st(n){return`${n.comparison.family}\0${n.comparison.left}`}function ot(n,a,s){const o=(t,l)=>{t.absorbedIds=[...t.absorbedIds??[],l.id]},e=new Map;for(const t of n){const l=st(t);(e.get(l)??e.set(l,[]).get(l)).push(t)}const c=it(a),r=new Set;for(const t of e.values()){for(const i of t)if(!(i.variable.kind!=="hardBin"||i.variable.bin===void 0))for(const u of t){if(u.variable.kind!=="softBin"||u.variable.bin===void 0||r.has(u.id)||!c.has(`${i.variable.bin}:${u.variable.bin}`)||u.effect.direction!==i.effect.direction)continue;o(i,u),r.add(u.id);const f=i.variable.label.replace(/^HBin\s+/,""),h=u.variable.label.replace(/^SBin\s+/,""),d=f===h?`HBin and SBin ${f} (same dies)`:`${i.variable.label} and ${u.variable.label} (same dies)`;i.summary=i.summary.replace(i.variable.label,d),i.variable.label=d;break}if(s.length!==1)continue;const l=t.find(i=>i.variable.kind==="yield");if(l){for(const i of t)if(!(i===l||r.has(i.id)||!((i.variable.kind==="hardBin"||i.variable.kind==="softBin")&&i.variable.bin===s[0]))){o(l,i),r.add(i.id);for(const f of i.absorbedIds??[])l.absorbedIds=[...l.absorbedIds??[],f]}}}}export function analyzeWaferMap(n,a={}){const{resolved:s,warnings:o}=Te(a),e=Ae(n),c={...s,passBins:e.passBins??T.passBins,ringCount:e.ringCount??T.ringCount},r=e.isLotStack,t=e.aggrMethod,l=!r||t==="mode"||t==="countBin"||t==="percent",i=e.dies.filter(g=>Oe(g)),u=i.filter(ge),f=je(c,i.length),h=e.dies.filter(g=>te(g)),d=h.filter(ge),p=Ve(d,e.wafer,f.ringCount),b=Fe(d,e.wafer,f.ringCount),y=Ne(d,e.reticleConfig),z=Pe(h,!1),$=Ce(d,e.wafer,f.sectorCount),m=[];l&&m.push(...W(i,p,f.passBins,f),...W(i,b,f.passBins,f),...W(i,y,f.passBins,f),...W(i,z,f.passBins,f),...W(i,$,f.passBins,f)),l&&m.push(...P(i,p,"hard",e.hbinDefs,"hardBin",f),...P(i,b,"hard",e.hbinDefs,"hardBin",f),...P(i,y,"hard",e.hbinDefs,"hardBin",f),...P(i,z,"hard",e.hbinDefs,"hardBin",f),...P(i,$,"hard",e.hbinDefs,"hardBin",f));{const g=i.filter(B=>B.sbin!==void 0);m.push(...P(g,p,"soft",e.sbinDefs,"softBin",f),...P(g,b,"soft",e.sbinDefs,"softBin",f),...P(g,y,"soft",e.sbinDefs,"softBin",f),...P(g,z,"soft",e.sbinDefs,"softBin",f),...P(g,$,"soft",e.sbinDefs,"softBin",f))}const w=[...o];let D;if(f.enableTestValueAnalysis){const g=Q(i,p,e.testDefs,f),B=Q(i,b,e.testDefs,f),k=Q(i,y,e.testDefs,f),V=Q(i,z,e.testDefs,f),F=Q(i,$,e.testDefs,f);m.push(...g.findings,...B.findings,...k.findings,...V.findings,...F.findings),g.warning&&w.push(g.warning),D=g.activeTestNumbers,m.push(...Ue(i,[p,b,y,z,$],e.testDefs,f)),m.push(...Y(i,p,e.testDefs,f),...Y(i,b,e.testDefs,f),...Y(i,y,e.testDefs,f),...Y(i,z,e.testDefs,f),...Y(i,$,e.testDefs,f))}{const g=qe(r,l,e.testDefs);(!r||l||g!==void 0)&&m.push(...Ee(u,e.wafer,{...f,isFailingDie:g}))}const v=nt(m,{eligibleDies:i,softEligibleDies:i.filter(g=>g.sbin!==void 0),testDies:i,passBins:f.passBins,ringCount:f.ringCount,sectorCount:f.sectorCount,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs});if(m.length=0,m.push(...v),l){const g=Ke(u,e.wafer,{passBins:f.passBins,ringCount:f.ringCount});if(g!==null&&g.pattern!=="random"&&g.pattern!=="none"){let Re=function(N){const E=j(N).map(K=>O(K).ring).filter(K=>K!==void 0);if(E.length===0)return!0;const ue=E.some(K=>K===Me),de=E.some(K=>K===1);return X==="edge-ring"||X==="edge-local"?ue:X==="center"?de:X==="donut"?!ue&&!de:!0};const k={center:"Center cluster",donut:"Donut","edge-ring":"Edge-ring","edge-local":"Edge-local",scratch:"Scratch","near-full":"Near-full"}[g.pattern]??g.pattern,V=g.confidence==="high"?"unusual":g.confidence==="medium"?"notable":"info",F=g.features,L=N=>`${(N*100).toFixed(0)}%`,C=g.pattern==="edge-ring"||g.pattern==="edge-local"?`${L(F.edgeRdd)} of edge dies failing`:g.pattern==="center"||g.pattern==="donut"?`centroid at ${L(F.centroidDistNorm)} of wafer radius from centre`:g.pattern==="scratch"?`linear score ${F.linearScore.toFixed(2)}, eccentricity ${F.eccentricity.toFixed(2)}`:`${L(F.globalRdd)} of dies failing`,_=i.filter(N=>{const E=N.hbin??N.sbin;return E!==void 0&&!new Set(f.passBins).has(E)}),U={"edge-ring":["ring","edge-arc"],"edge-local":["edge-arc","sector","quadrant"],center:["ring","cluster"],donut:["ring"],scratch:["cluster","sector","quadrant"],"near-full":["ring"]},G=new Set(U[g.pattern]??[]),X=g.pattern,Me=f.ringCount,ee=[];for(const N of m){if(!G.has(N.comparison.family)||N.comparison.family==="ring"&&!Re(N))continue;ee.push(N.id);const E=N.comparison.family==="ring"||N.comparison.family==="quadrant"||N.comparison.family==="sector";V!=="info"&&E&&(N.severity="info")}m.push({id:`spatial-pattern:${g.pattern}`,level:"wafer",severity:V,variable:{kind:"spatialPattern",label:k},comparison:{family:"spatial-pattern",left:k,right:"Wafer"},effect:{direction:"different",effectSize:F.globalRdd},stats:{method:"geometry",sampleSizeLeft:_.length,sampleSizeRight:i.length},summary:`Spatial pattern: ${k.toLowerCase()} (${g.confidence} confidence) \u2014 ${C}${g.note?` [${g.note}]`:""}`,highlight:{kind:"dies",dieKeys:_.map(N=>x(N))},relatedIds:ee.length>0?ee:void 0})}}m.sort((g,B)=>{const k=g.stats.adjustedPValue??g.stats.pValue??1,V=B.stats.adjustedPValue??B.stats.pValue??1;return k!==V?k-V:Math.abs(B.effect.absoluteDelta??0)-Math.abs(g.effect.absoluteDelta??0)});const S=He(e.dies,i.length,e.yield.yieldPercent);r&&(S.isLotStack=!0,t&&(S.aggregationMethod=t));const R=Ie(e.dies,e.testDefs);R&&(S.testSpecYield=R);const M=computeFunctionalYield(e.dies,e.testDefs);if(M&&(S.functionalYield=M),D===void 0&&f.computePerTestStats){const g=ze(i,f.testNumbers,e.testDefs);g.warning&&w.push(g.warning),D=g.testNumbers}if(D?.length){const g=_e(e.dies,D,e.testDefs,f.minimumSampleSize);g&&(S.perTestStats=g)}return w.length>0&&(S.warnings=w),ot(m,i,f.passBins),{level:"wafer",hasNotableFindings:m.some(g=>g.severity==="notable"||g.severity==="unusual"),findings:m,wafer:e.wafer.metadata??void 0,stats:S}}
@@ -59,6 +59,11 @@ export declare function buildBinClusterData(groups: {
59
59
  *
60
60
  * Pass bins are pinned to the top rather than competing on count: they are the
61
61
  * reference the failures are read against, not themselves a failure mode.
62
+ *
63
+ * `passBins` must be the passing bins OF THE TYPE BEING SORTED. For hard bins
64
+ * that is `passBins`; for soft bins it is `BinColors.pass.soft` (or
65
+ * `binPassSets(dies, passBins).soft`) — hard pass numbers applied to a soft
66
+ * tally pin the wrong rows.
62
67
  */
63
- export declare function sortBinsForDisplay(entries: Iterable<[bin: number, count: number]>, passBins?: number[]): Array<[bin: number, count: number]>;
68
+ export declare function sortBinsForDisplay(entries: Iterable<[bin: number, count: number]>, passBins?: Iterable<number>): Array<[bin: number, count: number]>;
64
69
  //# sourceMappingURL=binPareto.d.ts.map
@@ -8,8 +8,6 @@ interface ClusterOptions {
8
8
  minimumSampleSize: number;
9
9
  minimumClusterSize: number;
10
10
  ringCount: number;
11
- includePartial: boolean;
12
- includeEdgeExcluded: boolean;
13
11
  /** Optional override for the failure predicate. Default: hard bin, then soft bin, not in passBins. */
14
12
  isFailingDie?: (die: Die) => boolean;
15
13
  }
@@ -0,0 +1,87 @@
1
+ import { buildCapabilityData as buildCapabilityDataImpl } from './capability.js';
2
+ import { buildCorrelationMatrix as buildCorrelationMatrixImpl, filterCorrelationMatrix as filterCorrelationMatrixImpl } from './correlation.js';
3
+ import { buildTestBoxplotData as buildTestBoxplotDataImpl } from './boxplot.js';
4
+ import { buildTestTrendData as buildTestTrendDataImpl, trendCentre as trendCentreImpl } from './trend.js';
5
+ import { buildTestPassRateData as buildTestPassRateDataImpl, hasJudgeableTests as hasJudgeableTestsImpl } from './testPassRate.js';
6
+ import { buildTestHistogramData as buildTestHistogramDataImpl, buildTestHistogramSeries as buildTestHistogramSeriesImpl } from './histogram.js';
7
+ import { buildScatterData as buildScatterDataImpl, buildScatterDataGrouped as buildScatterDataGroupedImpl } from './scatter.js';
8
+ import { buildYieldData as buildYieldDataImpl, buildYieldDataCombined as buildYieldDataCombinedImpl } from './yield.js';
9
+ import { buildBinParetoData as buildBinParetoDataImpl, buildBinClusterData as buildBinClusterDataImpl } from './binPareto.js';
10
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
11
+ export declare const buildCapabilityData: typeof buildCapabilityDataImpl;
12
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
13
+ export declare const buildCorrelationMatrix: typeof buildCorrelationMatrixImpl;
14
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
15
+ export declare const filterCorrelationMatrix: typeof filterCorrelationMatrixImpl;
16
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
17
+ export declare const buildTestBoxplotData: typeof buildTestBoxplotDataImpl;
18
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
19
+ export declare const buildTestTrendData: typeof buildTestTrendDataImpl;
20
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
21
+ export declare const trendCentre: typeof trendCentreImpl;
22
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
23
+ export declare const buildTestPassRateData: typeof buildTestPassRateDataImpl;
24
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
25
+ export declare const hasJudgeableTests: typeof hasJudgeableTestsImpl;
26
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
27
+ export declare const buildTestHistogramData: typeof buildTestHistogramDataImpl;
28
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
29
+ export declare const buildTestHistogramSeries: typeof buildTestHistogramSeriesImpl;
30
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
31
+ export declare const buildScatterData: typeof buildScatterDataImpl;
32
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
33
+ export declare const buildScatterDataGrouped: typeof buildScatterDataGroupedImpl;
34
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
35
+ export declare const buildYieldData: typeof buildYieldDataImpl;
36
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
37
+ export declare const buildYieldDataCombined: typeof buildYieldDataCombinedImpl;
38
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
39
+ export declare const buildBinParetoData: typeof buildBinParetoDataImpl;
40
+ /** @deprecated Removed in 0.31.0. Prepared data for the Insights charts, which wmap now draws itself (`insights: { enabled: true }`). */
41
+ export declare const buildBinClusterData: typeof buildBinClusterDataImpl;
42
+ import { buildRingRegions as buildRingRegionsImpl, buildQuadrantRegions as buildQuadrantRegionsImpl, buildSectorRegions as buildSectorRegionsImpl, buildReticlePositionRegions as buildReticlePositionRegionsImpl, buildTestSiteRegions as buildTestSiteRegionsImpl, buildRegionYieldData as buildRegionYieldDataImpl, areQuadrantsAdjacent as areQuadrantsAdjacentImpl, parseRegionKey as parseRegionKeyImpl, sectorCompassNames as sectorCompassNamesImpl } from './regions.js';
43
+ import { classifyPattern as classifyPatternImpl } from './patternClassification.js';
44
+ import { visibleFindings as visibleFindingsImpl } from './filterFindings.js';
45
+ import { computeFunctionalYield as computeFunctionalYieldImpl } from './analyzeWaferMap.js';
46
+ import { resolveMetadataColumns as resolveMetadataColumnsImpl, discoverDieMetadataKeys as discoverDieMetadataKeysImpl } from './metadataColumns.js';
47
+ import { renderFindingsReportHtml as renderFindingsReportHtmlImpl, openHtmlReport as openHtmlReportImpl } from './renderFindingsReport.js';
48
+ import { renderSummaryReportHtml as renderSummaryReportHtmlImpl, renderLotSummaryReportHtml as renderLotSummaryReportHtmlImpl } from './renderSummaryReport.js';
49
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
50
+ export declare const buildRingRegions: typeof buildRingRegionsImpl;
51
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
52
+ export declare const buildQuadrantRegions: typeof buildQuadrantRegionsImpl;
53
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
54
+ export declare const buildSectorRegions: typeof buildSectorRegionsImpl;
55
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
56
+ export declare const buildReticlePositionRegions: typeof buildReticlePositionRegionsImpl;
57
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
58
+ export declare const buildTestSiteRegions: typeof buildTestSiteRegionsImpl;
59
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
60
+ export declare const buildRegionYieldData: typeof buildRegionYieldDataImpl;
61
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
62
+ export declare const areQuadrantsAdjacent: typeof areQuadrantsAdjacentImpl;
63
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
64
+ export declare const parseRegionKey: typeof parseRegionKeyImpl;
65
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
66
+ export declare const sectorCompassNames: typeof sectorCompassNamesImpl;
67
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` reports the classified pattern as a finding (comparison.family 'spatial-pattern'). */
68
+ export declare const classifyPattern: typeof classifyPatternImpl;
69
+ /** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
70
+ export declare const visibleFindings: typeof visibleFindingsImpl;
71
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` returns the same figures as `stats.functionalYield`. */
72
+ export declare const computeFunctionalYield: typeof computeFunctionalYieldImpl;
73
+ /** @deprecated Removed in 0.31.0. The die list resolves its metadata columns itself (the `dieList.metadataColumns` option). */
74
+ export declare const resolveMetadataColumns: typeof resolveMetadataColumnsImpl;
75
+ /** @deprecated Removed in 0.31.0. The die list resolves its metadata columns itself (the `dieList.metadataColumns` option). */
76
+ export declare const discoverDieMetadataKeys: typeof discoverDieMetadataKeysImpl;
77
+ /** @deprecated Removed in 0.31.0. Reports open from the Summary panel's report button; route them into your host with `setReportOpener`. */
78
+ export declare const renderFindingsReportHtml: typeof renderFindingsReportHtmlImpl;
79
+ /** @deprecated Removed in 0.31.0. Reports open from the Summary panel's report button; route them into your host with `setReportOpener`. */
80
+ export declare const openHtmlReport: typeof openHtmlReportImpl;
81
+ /** @deprecated Removed in 0.31.0. Reports open from the Summary panel's report button; route them into your host with `setReportOpener`. */
82
+ export declare const renderSummaryReportHtml: typeof renderSummaryReportHtmlImpl;
83
+ /** @deprecated Removed in 0.31.0. Reports open from the Summary panel's report button; route them into your host with `setReportOpener`. */
84
+ export declare const renderLotSummaryReportHtml: typeof renderLotSummaryReportHtmlImpl;
85
+ /** @deprecated Removed in 0.31.0. `buildFacetTable` applies it by default. */
86
+ export declare const DEFAULT_FACET_CURATION: Record<string, import("./facets.js").FacetCuration>;
87
+ //# sourceMappingURL=deprecated.d.ts.map
@@ -0,0 +1 @@
1
+ import{deprecated as t}from"../renderer/deprecate.js";import{buildCapabilityData as s}from"./capability.js";import{buildCorrelationMatrix as l,filterCorrelationMatrix as n}from"./correlation.js";import{buildTestBoxplotData as d}from"./boxplot.js";import{buildTestTrendData as p,trendCentre as m}from"./trend.js";import{buildTestPassRateData as u,hasJudgeableTests as b}from"./testPassRate.js";import{buildTestHistogramData as c,buildTestHistogramSeries as R}from"./histogram.js";import{buildScatterData as D,buildScatterDataGrouped as g}from"./scatter.js";import{buildYieldData as I,buildYieldDataCombined as T}from"./yield.js";import{buildBinParetoData as C,buildBinClusterData as x}from"./binPareto.js";const a="It prepared data for the Insights charts, which wmap now draws itself (insights: { enabled: true }). If you depend on it, say so at https://github.com/wafertools/wafermap/issues.";export const buildCapabilityData=t(s,"buildCapabilityData",a),buildCorrelationMatrix=t(l,"buildCorrelationMatrix",a),filterCorrelationMatrix=t(n,"filterCorrelationMatrix",a),buildTestBoxplotData=t(d,"buildTestBoxplotData",a),buildTestTrendData=t(p,"buildTestTrendData",a),trendCentre=t(m,"trendCentre",a),buildTestPassRateData=t(u,"buildTestPassRateData",a),hasJudgeableTests=t(b,"hasJudgeableTests",a),buildTestHistogramData=t(c,"buildTestHistogramData",a),buildTestHistogramSeries=t(R,"buildTestHistogramSeries",a),buildScatterData=t(D,"buildScatterData",a),buildScatterDataGrouped=t(g,"buildScatterDataGrouped",a),buildYieldData=t(I,"buildYieldData",a),buildYieldDataCombined=t(T,"buildYieldDataCombined",a),buildBinParetoData=t(C,"buildBinParetoData",a),buildBinClusterData=t(x,"buildBinClusterData",a);import{deprecatedValue as f}from"../renderer/deprecate.js";import{buildRingRegions as y,buildQuadrantRegions as $,buildSectorRegions as S,buildReticlePositionRegions as A,buildTestSiteRegions as h,buildRegionYieldData as H,areQuadrantsAdjacent as E,parseRegionKey as F,sectorCompassNames as M}from"./regions.js";import{classifyPattern as P}from"./patternClassification.js";import{visibleFindings as Y}from"./filterFindings.js";import{computeFunctionalYield as _}from"./analyzeWaferMap.js";import{resolveMetadataColumns as N,discoverDieMetadataKeys as v}from"./metadataColumns.js";import{renderFindingsReportHtml as B,openHtmlReport as L}from"./renderFindingsReport.js";import{renderSummaryReportHtml as w,renderLotSummaryReportHtml as U}from"./renderSummaryReport.js";import{DEFAULT_FACET_CURATION as O}from"./facets.js";const e="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",i="It is an internal helper that was exported by accident; buildWaferMap, analyzeWaferMap and the renderers already apply it.",K="analyzeWaferMap reports the classified pattern as a finding (comparison.family 'spatial-pattern').",Q="analyzeWaferMap returns the same figures as stats.functionalYield.",r="The die list resolves its metadata columns itself (the dieList.metadataColumns option).",o="Reports open from the Summary panel's report button; route them into your host with setReportOpener.";export const buildRingRegions=t(y,"buildRingRegions",`${i} ${e}`),buildQuadrantRegions=t($,"buildQuadrantRegions",`${i} ${e}`),buildSectorRegions=t(S,"buildSectorRegions",`${i} ${e}`),buildReticlePositionRegions=t(A,"buildReticlePositionRegions",`${i} ${e}`),buildTestSiteRegions=t(h,"buildTestSiteRegions",`${i} ${e}`),buildRegionYieldData=t(H,"buildRegionYieldData",`${i} ${e}`),areQuadrantsAdjacent=t(E,"areQuadrantsAdjacent",`${i} ${e}`),parseRegionKey=t(F,"parseRegionKey",`${i} ${e}`),sectorCompassNames=t(M,"sectorCompassNames",`${i} ${e}`),classifyPattern=t(P,"classifyPattern",`${K} ${e}`),visibleFindings=t(Y,"visibleFindings",`${i} ${e}`),computeFunctionalYield=t(_,"computeFunctionalYield",`${Q} ${e}`),resolveMetadataColumns=t(N,"resolveMetadataColumns",`${r} ${e}`),discoverDieMetadataKeys=t(v,"discoverDieMetadataKeys",`${r} ${e}`),renderFindingsReportHtml=t(B,"renderFindingsReportHtml",`${o} ${e}`),openHtmlReport=t(L,"openHtmlReport",`${o} ${e}`),renderSummaryReportHtml=t(w,"renderSummaryReportHtml",`${o} ${e}`),renderLotSummaryReportHtml=t(U,"renderLotSummaryReportHtml",`${o} ${e}`),DEFAULT_FACET_CURATION=f(O,"DEFAULT_FACET_CURATION");
@@ -3,33 +3,22 @@ export * from './regions.js';
3
3
  export * from './analyzeWaferMap.js';
4
4
  export * from './analyzeWaferLot.js';
5
5
  export * from './filterFindings.js';
6
- export { classifyPattern } from './patternClassification.js';
7
6
  export type { PatternLabel, PatternClassification, PatternFeatures } from './patternClassification.js';
8
- export { renderFindingsReportHtml, openHtmlReport, setReportOpener } from './renderFindingsReport.js';
7
+ export { setReportOpener } from './renderFindingsReport.js';
9
8
  export type { SummaryReportParams, LotSummaryReportParams } from './renderSummaryReport.js';
10
- export { renderSummaryReportHtml, renderLotSummaryReportHtml } from './renderSummaryReport.js';
11
- export { buildFacetTable, facetValueOf, FACET_NONE_VALUE, DEFAULT_FACET_CURATION } from './facets.js';
9
+ export { buildFacetTable, facetValueOf, FACET_NONE_VALUE } from './facets.js';
12
10
  export type { FacetField, FacetValue, FacetCuration, FacetItem, BuildFacetTableOptions } from './facets.js';
13
- export { resolveMetadataColumns, discoverDieMetadataKeys } from './metadataColumns.js';
14
11
  export type { MetadataColumn, MetadataColumnScope, MetadataColumnSet, MetadataKeySelection, ResolveMetadataColumnsOptions, } from './metadataColumns.js';
15
12
  export { mergeTestDefs } from './mergeTestDefs.js';
16
13
  export type { MergedTestDefs, TestDefConflict, TestDefConflictKind } from './mergeTestDefs.js';
17
- export { buildCapabilityData } from './capability.js';
18
14
  export type { CapabilityDatum, CapabilityItem } from './capability.js';
19
- export { buildCorrelationMatrix, filterCorrelationMatrix } from './correlation.js';
20
15
  export type { CorrelationTestInfo, CorrelationCell, CorrelationMatrix, CorrelationSummary } from './correlation.js';
21
- export { buildTestBoxplotData } from './boxplot.js';
22
16
  export type { BoxplotDatum, BoxplotItem } from './boxplot.js';
23
- export { buildTestTrendData, trendCentre } from './trend.js';
24
17
  export type { TrendDatum, TrendItem } from './trend.js';
25
- export { buildTestPassRateData, hasJudgeableTests } from './testPassRate.js';
26
18
  export type { TestPassKind, TestPassRateData, TestPassRateItem, TestPassRateRow, TestPassRateValue } from './testPassRate.js';
27
- export { buildTestHistogramData, buildTestHistogramSeries } from './histogram.js';
28
19
  export type { HistogramBucket, HistogramItem, HistogramSeries, HistogramSeriesData } from './histogram.js';
29
- export { buildScatterData, buildScatterDataGrouped } from './scatter.js';
30
20
  export type { ScatterPoint, ScatterItem } from './scatter.js';
31
- export { buildYieldData, buildYieldDataCombined } from './yield.js';
32
21
  export type { ChartDatum, YieldItem, YieldSortBy } from './yield.js';
33
- export { buildBinParetoData, buildBinClusterData } from './binPareto.js';
34
22
  export type { BinType, BinItem, BinCluster, BinClusterData } from './binPareto.js';
23
+ export { buildCapabilityData, buildCorrelationMatrix, filterCorrelationMatrix, buildTestBoxplotData, buildTestTrendData, trendCentre, buildTestPassRateData, hasJudgeableTests, buildTestHistogramData, buildTestHistogramSeries, buildScatterData, buildScatterDataGrouped, buildYieldData, buildYieldDataCombined, buildBinParetoData, buildBinClusterData, buildRingRegions, buildQuadrantRegions, buildSectorRegions, buildReticlePositionRegions, buildTestSiteRegions, buildRegionYieldData, areQuadrantsAdjacent, parseRegionKey, sectorCompassNames, classifyPattern, visibleFindings, computeFunctionalYield, resolveMetadataColumns, discoverDieMetadataKeys, renderFindingsReportHtml, openHtmlReport, renderSummaryReportHtml, renderLotSummaryReportHtml, DEFAULT_FACET_CURATION, } from './deprecated.js';
35
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export*from"./types.js";export*from"./regions.js";export*from"./analyzeWaferMap.js";export*from"./analyzeWaferLot.js";export*from"./filterFindings.js";export{classifyPattern}from"./patternClassification.js";export{renderFindingsReportHtml,openHtmlReport,setReportOpener}from"./renderFindingsReport.js";export{renderSummaryReportHtml,renderLotSummaryReportHtml}from"./renderSummaryReport.js";export{buildFacetTable,facetValueOf,FACET_NONE_VALUE,DEFAULT_FACET_CURATION}from"./facets.js";export{resolveMetadataColumns,discoverDieMetadataKeys}from"./metadataColumns.js";export{mergeTestDefs}from"./mergeTestDefs.js";export{buildCapabilityData}from"./capability.js";export{buildCorrelationMatrix,filterCorrelationMatrix}from"./correlation.js";export{buildTestBoxplotData}from"./boxplot.js";export{buildTestTrendData,trendCentre}from"./trend.js";export{buildTestPassRateData,hasJudgeableTests}from"./testPassRate.js";export{buildTestHistogramData,buildTestHistogramSeries}from"./histogram.js";export{buildScatterData,buildScatterDataGrouped}from"./scatter.js";export{buildYieldData,buildYieldDataCombined}from"./yield.js";export{buildBinParetoData,buildBinClusterData}from"./binPareto.js";
1
+ export*from"./types.js";export*from"./regions.js";export*from"./analyzeWaferMap.js";export*from"./analyzeWaferLot.js";export*from"./filterFindings.js";export{setReportOpener}from"./renderFindingsReport.js";export{buildFacetTable,facetValueOf,FACET_NONE_VALUE}from"./facets.js";export{mergeTestDefs}from"./mergeTestDefs.js";export{buildCapabilityData,buildCorrelationMatrix,filterCorrelationMatrix,buildTestBoxplotData,buildTestTrendData,trendCentre,buildTestPassRateData,hasJudgeableTests,buildTestHistogramData,buildTestHistogramSeries,buildScatterData,buildScatterDataGrouped,buildYieldData,buildYieldDataCombined,buildBinParetoData,buildBinClusterData,buildRingRegions,buildQuadrantRegions,buildSectorRegions,buildReticlePositionRegions,buildTestSiteRegions,buildRegionYieldData,areQuadrantsAdjacent,parseRegionKey,sectorCompassNames,classifyPattern,visibleFindings,computeFunctionalYield,resolveMetadataColumns,discoverDieMetadataKeys,renderFindingsReportHtml,openHtmlReport,renderSummaryReportHtml,renderLotSummaryReportHtml,DEFAULT_FACET_CURATION}from"./deprecated.js";
@@ -1,4 +1,4 @@
1
- import { type Die, type PositionedDie, type Wafer } from '../core/index.js';
1
+ import type { Die, PositionedDie, Wafer } from '../core/index.js';
2
2
  import type { ReticleConfig } from '../renderer/buildWaferMap.js';
3
3
  export interface StatsRegion {
4
4
  family: 'ring' | 'quadrant' | 'reticle-position' | 'test-site' | 'sector';
@@ -21,8 +21,13 @@ export interface RegionYieldDatum {
21
21
  * computation — consumed by both the summary panel's progress-bar rows and
22
22
  * the ring/quadrant yield diagrams, which previously each recomputed the
23
23
  * same pass/total tally independently.
24
+ *
25
+ * `passBins` is one set for every wafer, or a lookup by wafer index for a lot
26
+ * whose wafers were built with different pass bins (pass each item's
27
+ * `WaferMapResult.passBins`). Each die is judged by `diePassStatus`, the rule
28
+ * yield itself uses.
24
29
  */
25
- export declare function buildRegionYieldData(diesByWafer: Die[][], allWafers: Wafer[], ringCount: number, passBins: number[], regionBuilder: (dies: PositionedDie[], wafer: Wafer, ringCount: number) => StatsRegion[]): RegionYieldDatum[];
30
+ export declare function buildRegionYieldData(diesByWafer: Die[][], allWafers: Wafer[], ringCount: number, passBins: readonly number[] | ((waferIndex: number) => readonly number[]), regionBuilder: (dies: PositionedDie[], wafer: Wafer, ringCount: number) => StatsRegion[]): RegionYieldDatum[];
26
31
  /** Compass bearing names for a given sector count, ordered CCW from East. */
27
32
  export declare function sectorCompassNames(sectorCount: number): string[];
28
33
  /** True when two quadrants share an edge (NE–NW, NE–SE, NW–SW, SE–SW); diagonals are not adjacent. */