@wafertools/wafermap 0.30.0 → 0.30.1

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 (74) hide show
  1. package/AGENTS.md +13 -3
  2. package/CHANGELOG.md +175 -0
  3. package/README.md +3 -3
  4. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  5. package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  7. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
  9. package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
  10. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  11. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/trend.js +1 -1
  13. package/dist/packages/canvas-adapter/deprecated.d.ts +0 -3
  14. package/dist/packages/canvas-adapter/deprecated.js +1 -1
  15. package/dist/packages/canvas-adapter/dieList.js +1 -1
  16. package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
  17. package/dist/packages/canvas-adapter/exportName.js +1 -0
  18. package/dist/packages/canvas-adapter/icons.js +2 -2
  19. package/dist/packages/canvas-adapter/index.d.ts +2 -1
  20. package/dist/packages/canvas-adapter/index.js +1 -1
  21. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +25 -3
  22. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  23. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +24 -2
  24. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  25. package/dist/packages/canvas-adapter/summaryPanel.d.ts +21 -6
  26. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  27. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  28. package/dist/packages/canvas-adapter/toolbar.d.ts +6 -0
  29. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  30. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  31. package/dist/packages/canvas-adapter/userGuideHtml.js +15 -8
  32. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  33. package/dist/packages/canvas-adapter/version.js +1 -1
  34. package/dist/packages/canvas-adapter/warnings.js +1 -1
  35. package/dist/packages/core/deprecated.d.ts +8 -12
  36. package/dist/packages/core/deprecated.js +1 -1
  37. package/dist/packages/core/index.d.ts +1 -1
  38. package/dist/packages/core/index.js +1 -1
  39. package/dist/packages/core/utils.d.ts +12 -0
  40. package/dist/packages/core/utils.js +1 -1
  41. package/dist/packages/core/waferLabel.d.ts +12 -2
  42. package/dist/packages/core/waferLabel.js +1 -1
  43. package/dist/packages/renderer/binColors.d.ts +38 -0
  44. package/dist/packages/renderer/binColors.js +1 -1
  45. package/dist/packages/renderer/buildView.js +1 -1
  46. package/dist/packages/renderer/buildWaferMap.d.ts +30 -5
  47. package/dist/packages/renderer/buildWaferMap.js +1 -1
  48. package/dist/packages/renderer/deprecate.d.ts +7 -0
  49. package/dist/packages/renderer/deprecate.js +1 -1
  50. package/dist/packages/renderer/deprecated.d.ts +2 -2
  51. package/dist/packages/renderer/deprecated.js +1 -1
  52. package/dist/packages/renderer/index.d.ts +2 -1
  53. package/dist/packages/renderer/index.js +1 -1
  54. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  55. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  56. package/dist/packages/stats/capability.d.ts +16 -1
  57. package/dist/packages/stats/capability.js +1 -1
  58. package/dist/packages/stats/deprecated.d.ts +30 -35
  59. package/dist/packages/stats/deprecated.js +1 -1
  60. package/dist/packages/stats/index.d.ts +4 -2
  61. package/dist/packages/stats/index.js +1 -1
  62. package/dist/packages/stats/normalizeInput.d.ts +10 -0
  63. package/dist/packages/stats/normalizeInput.js +1 -0
  64. package/dist/packages/stats/regions.d.ts +2 -0
  65. package/dist/packages/stats/regions.js +1 -1
  66. package/dist/packages/stats/renderFindingsReport.js +1 -1
  67. package/dist/packages/stats/renderSummaryReport.d.ts +39 -1
  68. package/dist/packages/stats/renderSummaryReport.js +26 -26
  69. package/dist/packages/stats/reportHtml.d.ts +0 -1
  70. package/dist/packages/stats/reportHtml.js +10 -10
  71. package/dist/packages/stats/summaryFigures.d.ts +47 -0
  72. package/dist/packages/stats/summaryFigures.js +1 -0
  73. package/dist/packages/stats/types.d.ts +118 -0
  74. package/package.json +1 -1
@@ -1 +1 @@
1
- import{analyzeWaferMap as h}from"./analyzeWaferMap.js";import{median as y}from"../core/utils.js";import{describeWaferPopulation as p,populationStat as g}from"./population.js";const b=1.3,w=2;function v(f,c){const i={info:0,notable:1,unusual:2};return i[f]>=i[c]?f:c}function I(f){const c=new Map;for(const l of f){const s=new Set,o=new Set(l.summary.findings.flatMap(a=>a.absorbedIds??[]));for(const a of l.summary.findings){if(o.has(a.id))continue;const r=[a.variable.kind,a.variable.index??"",a.variable.bin??"",a.comparison.family,a.comparison.left,a.effect.direction].join("|");if(s.has(r))continue;s.add(r);const n=c.get(r)??{finding:a,waferIndices:[],severity:a.severity};n.waferIndices.push(l.waferIndex),n.severity=v(n.severity,a.severity),c.set(r,n)}}const i=[];for(const[l,s]of c){if(s.waferIndices.length<w)continue;const o=s.waferIndices.length/Math.max(1,f.length),a=o>=.6||s.severity==="unusual"?"unusual":"notable",r=s.finding;i.push({id:`lot-repeat:${l}`,level:"lot",severity:a,variable:{...r.variable},comparison:{...r.comparison},effect:{direction:r.effect.direction,absoluteDelta:r.effect.absoluteDelta,relativeDelta:r.effect.relativeDelta,effectSize:r.effect.effectSize},stats:{method:"wafer-finding-frequency",sampleSizeLeft:s.waferIndices.length,sampleSizeRight:f.length-s.waferIndices.length},summary:`${r.summary} \u2014 seen on ${s.waferIndices.length}/${f.length} wafers (${Math.round(o*100)}%)`,highlight:{kind:"wafer",waferIndices:s.waferIndices}})}return i}function S(f,c){const i=g(c,"median"),l=f.map(n=>({waferIndex:n.waferIndex,yieldPercent:n.summary.stats.yieldPercent})).filter(n=>n.yieldPercent!==null);if(l.length<3)return[];const s=l.map(n=>n.yieldPercent),o=y(s),a=y(s.map(n=>Math.abs(n-o)));if(!Number.isFinite(a)||a===0)return[];const r=[];for(const n of l){const e=.6745*(n.yieldPercent-o)/a;if(Math.abs(e)<b)continue;const t=n.yieldPercent-o;r.push({id:`inter-wafer:yield:${n.waferIndex}`,level:"inter-wafer",severity:Math.abs(e)>=2?"unusual":"notable",variable:{kind:"yield",label:"Yield"},comparison:{family:"wafer",left:`Wafer ${n.waferIndex+1}`,right:i.charAt(0).toUpperCase()+i.slice(1)},effect:{direction:t>0?"higher":"lower",absoluteDelta:t,relativeDelta:o===0?void 0:t/o,effectSize:e},stats:{method:"mad-z-score",sampleSizeLeft:1,sampleSizeRight:l.length-1},summary:`Wafer ${n.waferIndex+1} yield is ${Math.abs(t).toFixed(1)} percentage points ${t>0?"higher":"lower"} than the ${i}`,highlight:{kind:"wafer",waferIndices:[n.waferIndex]}})}return r}export function analyzeWaferLot(f,c={}){const i=f.map((e,t)=>({waferIndex:t,summary:c.perWaferSummaries?.[t]??h(e,c)})),l=[...I(i),...S(i,p(i.map(e=>e.summary.wafer)))].sort((e,t)=>{const d=e.severity==="unusual"?2:e.severity==="notable"?1:0,m=t.severity==="unusual"?2:t.severity==="notable"?1:0;return d!==m?m-d:e.summary.localeCompare(t.summary,"en")}),s=i.map(e=>e.summary.wafer).filter(e=>!!e),o={},a=[];if(s.length>0){const e=new Set;for(const t of s)for(const d of Object.keys(t))e.add(d);e.delete("wafer"),e.delete("waferId");for(const t of e){const d=s.filter(u=>t in u).map(u=>u[t]);d.every(u=>u===d[0])?o[t]=d[0]:a.push(t)}}const r=i.map(({waferIndex:e,summary:t})=>({waferIndex:e,yieldPercent:t.stats.yieldPercent})),n=i.map(({waferIndex:e,summary:t})=>t.stats.perTestStats?.length?{waferIndex:e,tests:t.stats.perTestStats}:null).filter(e=>e!==null);return{level:"lot",hasNotableFindings:l.some(e=>e.severity!=="info")||i.some(e=>e.summary.hasNotableFindings),findings:l,lot:Object.keys(o).length>0?o:void 0,...a.length>0?{mixedIdentityFields:a}:{},stats:{waferCount:f.length},lotYieldSeries:r,perWafer:i,...n.length>0?{perWaferTestStats:n}:{}}}
1
+ import{analyzeWaferMap as g}from"./analyzeWaferMap.js";import{normalizeInput as h}from"./normalizeInput.js";import{poolCapability as b,poolRegionYield as w,poolPassRates as v}from"./summaryFigures.js";import{mergeTestDefs as I}from"./mergeTestDefs.js";import{median as p}from"../core/utils.js";import{describeWaferPopulation as S,populationStat as k}from"./population.js";const P=1.3,R=2;function x(f,c){const o={info:0,notable:1,unusual:2};return o[f]>=o[c]?f:c}function z(f){const c=new Map;for(const t of f){const i=new Set,l=new Set(t.summary.findings.flatMap(n=>n.absorbedIds??[]));for(const n of t.summary.findings){if(l.has(n.id))continue;const s=[n.variable.kind,n.variable.index??"",n.variable.bin??"",n.comparison.family,n.comparison.left,n.effect.direction].join("|");if(i.has(s))continue;i.add(s);const a=c.get(s)??{finding:n,waferIndices:[],severity:n.severity};a.waferIndices.push(t.waferIndex),a.severity=x(a.severity,n.severity),c.set(s,a)}}const o=[];for(const[t,i]of c){if(i.waferIndices.length<R)continue;const l=i.waferIndices.length/Math.max(1,f.length),n=l>=.6||i.severity==="unusual"?"unusual":"notable",s=i.finding;o.push({id:`lot-repeat:${t}`,level:"lot",severity:n,variable:{...s.variable},comparison:{...s.comparison},effect:{direction:s.effect.direction,absoluteDelta:s.effect.absoluteDelta,relativeDelta:s.effect.relativeDelta,effectSize:s.effect.effectSize},stats:{method:"wafer-finding-frequency",sampleSizeLeft:i.waferIndices.length,sampleSizeRight:f.length-i.waferIndices.length},summary:`${s.summary} \u2014 seen on ${i.waferIndices.length}/${f.length} wafers (${Math.round(l*100)}%)`,highlight:{kind:"wafer",waferIndices:i.waferIndices}})}return o}function $(f,c){const o=k(c,"median"),t=f.map(a=>({waferIndex:a.waferIndex,yieldPercent:a.summary.stats.yieldPercent})).filter(a=>a.yieldPercent!==null);if(t.length<3)return[];const i=t.map(a=>a.yieldPercent),l=p(i),n=p(i.map(a=>Math.abs(a-l)));if(!Number.isFinite(n)||n===0)return[];const s=[];for(const a of t){const u=.6745*(a.yieldPercent-l)/n;if(Math.abs(u)<P)continue;const e=a.yieldPercent-l;s.push({id:`inter-wafer:yield:${a.waferIndex}`,level:"inter-wafer",severity:Math.abs(u)>=2?"unusual":"notable",variable:{kind:"yield",label:"Yield"},comparison:{family:"wafer",left:`Wafer ${a.waferIndex+1}`,right:o.charAt(0).toUpperCase()+o.slice(1)},effect:{direction:e>0?"higher":"lower",absoluteDelta:e,relativeDelta:l===0?void 0:e/l,effectSize:u},stats:{method:"mad-z-score",sampleSizeLeft:1,sampleSizeRight:t.length-1},summary:`Wafer ${a.waferIndex+1} yield is ${Math.abs(e).toFixed(1)} percentage points ${e>0?"higher":"lower"} than the ${o}`,highlight:{kind:"wafer",waferIndices:[a.waferIndex]}})}return s}export function analyzeWaferLot(f,c={}){const o=f.map(h),t=o.map((e,r)=>({waferIndex:r,summary:c.perWaferSummaries?.[r]??g(e,c)})),i=[...z(t),...$(t,S(t.map(e=>e.summary.wafer)))].sort((e,r)=>{const d=e.severity==="unusual"?2:e.severity==="notable"?1:0,y=r.severity==="unusual"?2:r.severity==="notable"?1:0;return d!==y?y-d:e.summary.localeCompare(r.summary,"en")}),l=t.map(e=>e.summary.wafer).filter(e=>!!e),n={},s=[];if(l.length>0){const e=new Set;for(const r of l)for(const d of Object.keys(r))e.add(d);e.delete("wafer"),e.delete("waferId");for(const r of e){const d=l.filter(m=>r in m).map(m=>m[r]);d.every(m=>m===d[0])?n[r]=d[0]:s.push(r)}}const a=t.map(({waferIndex:e,summary:r})=>({waferIndex:e,yieldPercent:r.stats.yieldPercent})),u=t.map(({waferIndex:e,summary:r})=>r.stats.perTestStats?.length?{waferIndex:e,tests:r.stats.perTestStats}:null).filter(e=>e!==null);return{level:"lot",hasNotableFindings:i.some(e=>e.severity!=="info")||t.some(e=>e.summary.hasNotableFindings),findings:i,lot:Object.keys(n).length>0?n:void 0,...s.length>0?{mixedIdentityFields:s}:{},stats:{waferCount:f.length,...M(o,t.map(e=>e.summary))},lotYieldSeries:a,perWafer:t,...u.length>0?{perWaferTestStats:u}:{}}}function M(f,c){const o={...v(c)},t=b(c,I(f).defs);t&&(o.capability=t);const i=w(c,f.map(l=>l.ringCount));return i&&(o.regionYield=i),o}
@@ -1 +1 @@
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}}
1
+ import{normalizeInput as Ce}from"./normalizeInput.js";import{computeCapability as Ne,computeTestFlagYield as Pe,computeRegionYield as Ve}from"./summaryFigures.js";import{isYieldEligibleDie as te,getDieKey as T,isPositionedDie as ge}from"../core/dies.js";import{getTestPassStatus as me,isParametricTest as O}from"../renderer/buildWaferMap.js";import{buildQuadrantRegions as Le,buildReticlePositionRegions as Ee,buildRingRegions as Ke,buildSectorRegions as Te,buildTestSiteRegions as xe,sectorCompassNames as he,areQuadrantsAdjacent as je,parseRegionKey as q}from"./regions.js";import{buildClusterFindings as Ae}from"./clusterDetection.js";import{classifyPattern as Oe}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 qe={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 He(s){const a=Object.fromEntries(Object.entries(s).filter(([,t])=>t!==void 0)),i={...x,...a},o=[],e=[];for(const t of["significanceLevel","minimumEffectSize","minimumRelativeEffect"]){const{min:l,max:n}=qe[t],u=i[t];if(typeof u!="number"||!Number.isFinite(u)){e.push(`${t}=${String(u)} is not a finite number (using ${x[t]})`),i[t]=x[t];continue}const f=Math.min(n,Math.max(l,u));f!==u&&(e.push(`${t}=${u} is outside ${l}\u2013${n} (using ${f})`),i[t]=f)}if(ye.includes(i.sectorCount)||(e.push(`sectorCount=${i.sectorCount} is not one of ${ye.join(", ")} (using ${x.sectorCount})`),i.sectorCount=x.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:i,warnings:o}}const x={ringCount:4,passBins:[1],significanceLevel:.05,minimumEffectSize:.2,minimumRelativeEffect:1,minimumSampleSize:5,enableTestValueAnalysis:!1,computePerTestStats:!1,sectorCount:8,minimumClusterSize:5};function Ie(s,a){const i={...s};return i.minimumClusterSize=Math.max(5,Math.round(a*.003)),i}function Ye(s){return te(s)?s.hbin!==void 0||s.sbin!==void 0||s.testValues!==void 0&&Object.keys(s.testValues).length>0:!1}function _e(s,a,i){if(!s||a)return;const o=(i??[]).filter(e=>O(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 We(s,a,i){const o=new Set,e=new Set,c=new Set,r=new Map,t=new Map;for(const l of s){if(l.testValues)for(const n of Object.keys(l.testValues))o.add(Number(n));if(l.testPass)for(const n of Object.keys(l.testPass))o.add(Number(n));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:s.length,analyzedDies:a,excludedDies:s.length-a,yieldPercent:i,testsConsidered:[...o].sort((l,n)=>l-n),hardBinsConsidered:[...e].sort((l,n)=>l-n),softBinsConsidered:[...c].sort((l,n)=>l-n),...r.size?{hardBinCounts:Object.fromEntries(r)}:{},...t.size?{softBinCounts:Object.fromEntries(t)}:{}}}function Qe(s,a){if(!a?.length)return;const i=a.filter(e=>O(e)&&(e.limitLow!==void 0||e.limitHigh!==void 0));if(!i.length)return;const o=[];for(const e of i){const c=e.testNumber;let r=0,t=0,l=0,n=0;for(const u of s){if(u.partial||u.edgeExcluded)continue;const f=u.testValues?.[c];f!==void 0&&(n++,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:n,yieldPercent:n>0?r/n*100:null})}return o.length?o:void 0}export function computeFunctionalYield(s,a){if(!a?.length)return;const i=a.filter(e=>!O(e));if(!i.length)return;const o=[];for(const e of i){const c=e.testNumber;let r=0,t=0;for(const n of s){if(n.partial||n.edgeExcluded)continue;const u=me(n,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 Ue(s,a,i,o){const e=[];for(const c of a){const r=[];for(const f of s){if(f.partial||f.edgeExcluded)continue;const m=f.testValues?.[c];m!==void 0&&r.push(m)}if(r.length<o)continue;const t=J(r),l=Math.sqrt(fe(r,t)),n=r.slice().sort((f,m)=>f-m),u=i?.find(f=>f.testNumber===c)?.name??String(c);e.push({testNumber:c,label:u,count:r.length,min:n[0],max:n[n.length-1],mean:t,stddev:l,median:ne(n,.5),q1:ne(n,.25),q3:ne(n,.75)})}return e.length?e:void 0}function H(s,a,i,o){const e=(s+i)/(a+o),c=e*(1-e)*(1/a+1/o);if(!Number.isFinite(c)||c<=0)return 1;const r=(s/a-i/o)/Math.sqrt(c);return be(2*(1-pe(Math.abs(r))))}function ie(s){const a=new Map;for(const i of s){const o=`${i.variable.kind}:${i.comparison.family}`,e=a.get(o)??[];e.push(i),a.set(o,e)}for(const i of a.values()){const o=[...i].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,n=Math.min(1,Math.min(c,l*e/(r+1)));t.stats.adjustedPValue=n,c=n}}return s}function se(s,a,i){const o=Math.abs(a),e=i!==void 0?Math.abs(i):0;return s<=.01&&(o>=.3||e>=2.5)?"unusual":s<=.05&&(o>=.2||e>=1.5)?"notable":"info"}function oe(s,a){return s<=.01&&Math.abs(a)>=.5?"unusual":s<=.05&&Math.abs(a)>=.15?"notable":"info"}function Z(s){return s==="reticle-position"?"other reticle positions":s==="test-site"?"other test sites":"the rest of the map"}function I(s){return s==="reticle-position"?"Other reticle positions":s==="test-site"?"Other test sites":"Rest of map"}function Se(s,a,i){const o=(Math.abs(a)*100).toFixed(1);return`${s} yield is ${o} percentage points ${a>0?"higher":"lower"} than ${Z(i)}`}function ae(s,a){return a==="quadrant"&&!s.startsWith("Quadrant")?`quadrant ${s}`:s}function we(s,a,i,o){const e=ae(s,o),c=(Math.abs(i)*100).toFixed(1);return`${e} has ${a} occurrence ${c} percentage points ${i>0?"higher":"lower"} than ${Z(o)}`}function Ge(s,a,i,o){const e=ae(s,o),c=(Math.abs(i)*100).toFixed(1);return`${e} has ${a} pass rate ${c} percentage points ${i>0?"higher":"lower"} than ${Z(o)}`}function $e(s,a,i,o,e,c){const r=ae(s,e),t=Z(e),l=i>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 n=c?` ${c}`:"";return`${r} mean ${a} is ${Math.abs(i).toPrecision(3)}${n} ${l} than ${t}`}function ve(s,a,i){const o=a?.find(e=>e.bin===s);return o?.name?`${i} ${s} (${o.name})`:`${i} ${s}`}function Xe(s,a){const i=a?.find(o=>o.testNumber===s);return{label:i?.name??`Test ${s}`,unit:i?.unit}}function re(s,a){const i=new Map(s.map(e=>[T(e),e])),o=new Map;for(const e of a){const c=[];for(const r of e.dieKeys){const t=i.get(r);t&&c.push(t)}o.set(e.key,c)}return o}function le(s,a){return ie(s),s.filter(i=>{const o=i.stats.adjustedPValue??i.stats.pValue??1,e=Math.abs(i.effect.absoluteDelta??0),c=Math.abs(i.effect.relativeDelta??0);return o<=a.significanceLevel&&(e>=a.minimumEffectSize||c>=a.minimumRelativeEffect)}).map(i=>({...i,severity:se(i.stats.adjustedPValue??i.stats.pValue??1,i.effect.absoluteDelta??0,i.effect.relativeDelta)}))}function W(s,a,i,o){const e=new Set(i),c=re(s,a),r=new Map,t=new Map;for(const[n,u]of c){let f=0,m=0;for(const g of u)g.hbin!==void 0&&(m++,e.has(g.hbin)&&f++);r.set(n,f),t.set(n,m)}const l=[];for(const n of a){const u=t.get(n.key)??0,f=r.get(n.key)??0;let m=0,g=0;for(const[w,h]of t)w!==n.key&&(m+=h,g+=r.get(w)??0);if(u<o.minimumSampleSize||m<o.minimumSampleSize)continue;const p=f/u,b=g/m,y=p-b,z=H(f,u,g,m);l.push({id:`yield:${n.key}`,level:"wafer",severity:"info",variable:{kind:"yield",label:"Yield"},comparison:{family:n.family,left:n.label,right:I(n.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:m},summary:Se(n.label,y,n.family),highlight:{kind:"region",regionFamily:n.family,regionKeys:[n.key],dieKeys:[...n.dieKeys]}})}return le(l,o)}function L(s,a,i,o,e,c){const r=g=>i==="soft"?g.sbin:g.hbin,t=[...new Set(s.map(r).filter(g=>g!==void 0))].sort((g,p)=>g-p),l=re(s,a),n=new Map,u=new Map;for(const[g,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)}n.set(g,b),u.set(g,p.length)}const f=[],m=e==="hardBin"?"HBin":"SBin";for(const g of a){const p=u.get(g.key)??0,b=n.get(g.key);let y=0;const z=new Map;for(const[w,h]of n)if(w!==g.key){y+=u.get(w)??0;for(const[S,v]of h)z.set(S,(z.get(S)??0)+v)}if(!(p<c.minimumSampleSize||y<c.minimumSampleSize))for(const w of t){const h=b.get(w)??0,S=z.get(w)??0,v=h/p,D=S/y,$=v-D,k=H(h,p,S,y),M=ve(w,o,m);f.push({id:`${e}:${w}:${g.key}`,level:"wafer",severity:"info",variable:{kind:e,bin:w,label:M},comparison:{family:g.family,left:g.label,right:I(g.family)},effect:{direction:$===0?"different":$>0?"higher":"lower",absoluteDelta:$,relativeDelta:D===0?void 0:$/D,effectSize:$},stats:{method:"two-proportion-z",pValue:k,sampleSizeLeft:p,sampleSizeRight:y},summary:we(g.label,M,$,g.family),highlight:{kind:"bin",bin:w,regionKeys:[g.key],dieKeys:[...g.dieKeys]}})}}return le(f,c)}function Q(s,a,i,o){const e=(i??[]).filter(u=>!O(u));if(!e.length)return[];const c=e.map(u=>u.testNumber),r=re(s,a),t=new Map,l=new Map;for(const[u,f]of r){const m=new Uint32Array(e.length),g=new Uint32Array(e.length);for(const p of f)for(let b=0;b<e.length;b++){const y=me(p,c[b],e[b]);y!==void 0&&(g[b]++,y&&m[b]++)}t.set(u,m),l.set(u,g)}const n=[];for(const u of a){const f=t.get(u.key),m=l.get(u.key);for(let g=0;g<e.length;g++){let p=0,b=0;for(const[$,k]of l)$!==u.key&&(b+=k[g],p+=t.get($)[g]);const y=m[g],z=f[g];if(y<o.minimumSampleSize||b<o.minimumSampleSize)continue;const w=z/y,h=p/b,S=w-h,v=H(z,y,p,b),D=c[g];n.push({id:`functional:${D}:${u.key}`,level:"wafer",severity:"info",variable:{kind:"functionalTest",index:D,label:`${e[g].name} pass rate`},comparison:{family:u.family,left:u.label,right:I(u.family)},effect:{direction:S===0?"different":S>0?"higher":"lower",absoluteDelta:S,relativeDelta:h===0?void 0:S/h,effectSize:S},stats:{method:"two-proportion-z",pValue:v,sampleSizeLeft:y,sampleSizeRight:b},summary:Ge(u.label,e[g].name,S,u.family),highlight:{kind:"region",regionFamily:u.family,regionKeys:[u.key],dieKeys:[...u.dieKeys]}})}}return le(n,o)}function fe(s,a){return s.length<2?0:s.reduce((i,o)=>i+(o-a)**2,0)/(s.length-1)}function De(s,a,i,o,e,c){const r=Math.sqrt(i/s+c/o),t=a-e;if(!Number.isFinite(r)||r===0)return{pValue:1,effectSize:0,delta:t};const l=t/r,n=Math.sqrt(Math.max(0,(i+c)/2)),u=n===0?t:t/n;return{pValue:be(2*(1-pe(Math.abs(l)))),effectSize:u,delta:t}}function Je(s,a){const i=J(s),o=J(a);return De(s.length,i,fe(s,i),a.length,o,fe(a,o))}const ke=250;function ze(s,a,i){const o=r=>r.filter(t=>O(i?.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 s)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 U(s,a,i,o){const e=ze(s,o.testNumbers,i);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=[],n=[];for(const p of s){const b=r.get(T(p));b!==void 0&&(l.push(p),n.push(b))}const u=[],f=new Float64Array(t),m=new Float64Array(t),g=new Float64Array(t);for(const p of c){f.fill(0),m.fill(0),g.fill(0);let b=0,y=0,z=0,w;for(let h=0;h<l.length;h++){const v=l[h].testValues?.[p];if(v===void 0)continue;w===void 0&&(w=v);const D=v-w,$=n[h];f[$]+=1,m[$]+=D,g[$]+=D*D,b+=1,y+=D,z+=D*D}if(w!==void 0)for(let h=0;h<t;h++){const S=f[h],v=b-S;if(S<o.minimumSampleSize||v<o.minimumSampleSize)continue;const D=m[h]/S,k=(y-m[h])/v,M=D+w,R=k+w,P=S>1?Math.max(0,g[h]-S*D*D)/(S-1):0,d=v>1?Math.max(0,z-g[h]-v*k*k)/(v-1):0,{pValue:N,effectSize:B,delta:V}=De(S,M,P,v,R,d),F=a[h],{label:A,unit:G}=Xe(p,i),_=R!==0?V/Math.abs(R):void 0;u.push({id:`test:${p}:${F.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:p,label:A,unit:G},comparison:{family:F.family,left:F.label,right:I(F.family)},effect:{direction:V===0?"different":V>0?"higher":"lower",absoluteDelta:V,relativeDelta:_,effectSize:B},stats:{method:"welch-z-approx",pValue:N,sampleSizeLeft:S,sampleSizeRight:v},summary:$e(F.label,A,V,_,F.family,G),highlight:{kind:"region",regionFamily:F.family,regionKeys:[F.key],dieKeys:[...F.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 Ze(s,a,i,o){if(!i?.length)return[];const e=i.filter(t=>O(t)&&(t.limitLow!==void 0||t.limitHigh!==void 0));if(!e.length)return[];const c=[],r=new Map(s.map(t=>[T(t),t]));for(const t of e){const l=t.testNumber;for(const n of a){const u=new Map;for(const m of n){const g=[];for(const p of m.dieKeys){const b=r.get(p);b&&g.push(b)}u.set(m.key,g)}const f=[];for(const m of n){const g=u.get(m.key),p=[];for(const[M,R]of u)if(M!==m.key)for(const P of R)p.push(P);const b=M=>M.testValues?.[l]!==void 0,y=M=>{const R=M.testValues?.[l];return R===void 0?!1:t.limitLow!==void 0&&R<t.limitLow||t.limitHigh!==void 0&&R>t.limitHigh},z=g.filter(b),w=p.filter(b);if(z.length<o.minimumSampleSize||w.length<o.minimumSampleSize)continue;const h=z.filter(y).length,S=w.filter(y).length,v=h/z.length,D=S/w.length,$=v-D,k=H(h,z.length,S,w.length);f.push({id:`specLimit:${l}:${m.key}`,level:"wafer",severity:"info",variable:{kind:"test",index:l,label:t.name,unit:t.unit},comparison:{family:m.family,left:m.label,right:I(m.family)},effect:{direction:$===0?"different":$>0?"higher":"lower",absoluteDelta:$,relativeDelta:D===0?void 0:$/D,effectSize:$},stats:{method:"two-proportion-z",pValue:k,sampleSizeLeft:z.length,sampleSizeRight:w.length},summary:`${m.label} spec-fail rate for ${t.name} is ${(Math.abs($)*100).toFixed(1)} pp ${$>0?"higher":"lower"} than the rest of the wafer`,highlight:{kind:"region",regionFamily:m.family,regionKeys:[m.key],dieKeys:[...m.dieKeys]}})}ie(f),c.push(...f.filter(m=>{const g=m.stats.adjustedPValue??m.stats.pValue??1,p=Math.abs(m.effect.absoluteDelta??0),b=Math.abs(m.effect.relativeDelta??0);return g<=o.significanceLevel&&(p>=o.minimumEffectSize||b>=o.minimumRelativeEffect)}).map(m=>({...m,severity:oe(m.stats.adjustedPValue??m.stats.pValue??1,m.effect.effectSize??0)})))}}return c}function j(s){return s.highlight.regionKeys??[]}function et(s){const a=s.variable.bin??s.variable.index??"";return`${s.comparison.family}\0${s.variable.kind}\0${a}\0${s.effect.direction}`}function Y(s,a){const i=j(s)[0]??"",o=q(i);return o.family==="ring"?o.ring??0:o.family==="sector"?he(a).indexOf(o.sector??""):0}function tt(s,a,i){if(a==="quadrant"){const r=new Map;for(const u of s){const f=q(j(u)[0]??"").quadrant;f&&r.set(f,u)}const t=[...r.keys()],l=new Set,n=[];for(const u of t){if(l.has(u))continue;const f=[],m=[u];for(l.add(u);m.length;){const g=m.pop();f.push(g);for(const p of t)!l.has(p)&&je(g,p)&&(l.add(p),m.push(p))}n.push(f.map(g=>r.get(g)))}return n}const o=[...s].sort((r,t)=>Y(r,i)-Y(t,i)),e=[];let c=[];for(const r of o){if(c.length===0){c.push(r);continue}const t=Y(c[c.length-1],i);Y(r,i)-t===1?c.push(r):(e.push(c),c=[r])}if(c.length&&e.push(c),a==="sector"&&e.length>1){const t=he(i).length,l=Y(e[0][0],i),n=Y(e[e.length-1][e[e.length-1].length-1],i);if(l===0&&n===t-1){const u=e.pop();e[0]=[...u,...e[0]]}}return e}function nt(s,a){const i=s.map(r=>q(j(r)[0]??"").ring).filter(r=>r!==void 0).sort((r,t)=>r-t),o=i[0],e=i[i.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 it(s,a){const i=s.map(o=>q(j(o)[0]??"").sector??"");return i.length===1?`Sector ${i[0]}`:`Sectors ${i[0]}\u2013${i[i.length-1]}`}function st(s){const a=s.map(i=>q(j(i)[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(s){return[...new Set(s)]}function ot(s,a){const i=s[0],o=i.comparison.family,e=i.variable.kind,c=ce(s.flatMap(h=>h.highlight.dieKeys??[])),r=ce(s.flatMap(j)),t=new Set(c),l=o==="ring"?nt(s,a.ringCount):o==="sector"?it(s,a.sectorCount):st(s),n=e==="softBin"?a.softEligibleDies:e==="test"?a.testDies:a.eligibleDies,u=n.filter(h=>t.has(T(h))),f=n.filter(h=>!t.has(T(h)));let m,g,p,b,y;if(e==="test"){const h=i.variable.index,S=d=>d.testValues?.[h],v=u.map(S).filter(d=>d!==void 0),D=f.map(S).filter(d=>d!==void 0),{pValue:$,effectSize:k,delta:M}=Je(v,D),R=J(D),P=R!==0?M/Math.abs(R):void 0;m={direction:M===0?"different":M>0?"higher":"lower",absoluteDelta:M,relativeDelta:P,effectSize:k},g={method:"welch-z-approx",pValue:$,sampleSizeLeft:v.length,sampleSizeRight:D.length},p=oe($,k),b=$e(l,i.variable.label,M,P,o,i.variable.unit),y=`test:${h}`}else if(e==="yield"){const h=new Set(a.passBins),S=u.filter(B=>B.hbin!==void 0),v=f.filter(B=>B.hbin!==void 0),D=S.length,$=v.length,k=S.filter(B=>h.has(B.hbin)).length,M=v.filter(B=>h.has(B.hbin)).length,R=D>0?k/D:0,P=$>0?M/$:0,d=R-P,N=H(k,D,M,$);m={direction:d===0?"different":d>0?"higher":"lower",absoluteDelta:d,relativeDelta:P===0?void 0:d/P,effectSize:d},g={method:"two-proportion-z",pValue:N,sampleSizeLeft:D,sampleSizeRight:$},p=se(N,d,m.relativeDelta),b=Se(l,d,o),y="yield"}else{const h=i.variable.bin,S=d=>e==="softBin"?d.sbin:d.hbin,v=u.filter(d=>S(d)===h).length,D=f.filter(d=>S(d)===h).length,$=v/u.length,k=D/f.length,M=$-k,R=H(v,u.length,D,f.length);m={direction:M===0?"different":M>0?"higher":"lower",absoluteDelta:M,relativeDelta:k===0?void 0:M/k,effectSize:M},g={method:"two-proportion-z",pValue:R,sampleSizeLeft:u.length,sampleSizeRight:f.length},p=se(R,M,m.relativeDelta);const P=e==="softBin"?a.sbinDefs:a.hbinDefs;b=we(l,ve(h,P,e==="softBin"?"SBin":"HBin"),M,o),y=`${e}:${h}`}const z=ce(r.map(h=>h.replace(`${o}:`,""))).join("-"),w=e==="hardBin"||e==="softBin"?{kind:"bin",bin:i.variable.bin,regionKeys:r,dieKeys:c}:{kind:"region",regionFamily:o,regionKeys:r,dieKeys:c};return{...i,id:`${y}:${o}:${z}`,severity:p,comparison:{family:o,left:l,right:I(o)},effect:m,stats:g,summary:b,highlight:w,relatedIds:s.map(h=>h.id)}}function at(s,a){const i=new Set(["ring","quadrant","sector"]),o=[],e=new Map;for(const r of s){if(!i.has(r.comparison.family)){o.push(r);continue}const t=et(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 tt(r,t,a.sectorCount))c.push(l.length===1?l[0]:ot(l,a))}return[...o,...c]}function rt(s){const a=new Map,i=new Map;for(const e of s){const c=T(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&&(i.get(e.sbin)??i.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 i){if(c.size!==t.size)continue;let l=!0;for(const n of c)if(!t.has(n)){l=!1;break}l&&o.add(`${e}:${r}`)}return o}function lt(s){return`${s.comparison.family}\0${s.comparison.left}`}function ft(s,a,i){const o=(t,l)=>{t.absorbedIds=[...t.absorbedIds??[],l.id]},e=new Map;for(const t of s){const l=lt(t);(e.get(l)??e.set(l,[]).get(l)).push(t)}const c=rt(a),r=new Set;for(const t of e.values()){for(const n of t)if(!(n.variable.kind!=="hardBin"||n.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(`${n.variable.bin}:${u.variable.bin}`)||u.effect.direction!==n.effect.direction)continue;o(n,u),r.add(u.id);const f=n.variable.label.replace(/^HBin\s+/,""),m=u.variable.label.replace(/^SBin\s+/,""),g=f===m?`HBin and SBin ${f} (same dies)`:`${n.variable.label} and ${u.variable.label} (same dies)`;n.summary=n.summary.replace(n.variable.label,g),n.variable.label=g;break}if(i.length!==1)continue;const l=t.find(n=>n.variable.kind==="yield");if(l){for(const n of t)if(!(n===l||r.has(n.id)||!((n.variable.kind==="hardBin"||n.variable.kind==="softBin")&&n.variable.bin===i[0]))){o(l,n),r.add(n.id);for(const f of n.absorbedIds??[])l.absorbedIds=[...l.absorbedIds??[],f]}}}}export function analyzeWaferMap(s,a={}){const{resolved:i,warnings:o}=He(a),e=Ce(s),c={...i,passBins:e.passBins??x.passBins,ringCount:e.ringCount??x.ringCount},r=e.isLotStack,t=e.aggrMethod,l=!r||t==="mode"||t==="countBin"||t==="percent",n=e.dies.filter(d=>Ye(d)),u=n.filter(ge),f=Ie(c,n.length),m=e.dies.filter(d=>te(d)),g=m.filter(ge),p=Ke(g,e.wafer,f.ringCount),b=Le(g,e.wafer,f.ringCount),y=Ee(g,e.reticleConfig),z=xe(m,!1),w=Te(g,e.wafer,f.sectorCount),h=[];l&&h.push(...W(n,p,f.passBins,f),...W(n,b,f.passBins,f),...W(n,y,f.passBins,f),...W(n,z,f.passBins,f),...W(n,w,f.passBins,f)),l&&h.push(...L(n,p,"hard",e.hbinDefs,"hardBin",f),...L(n,b,"hard",e.hbinDefs,"hardBin",f),...L(n,y,"hard",e.hbinDefs,"hardBin",f),...L(n,z,"hard",e.hbinDefs,"hardBin",f),...L(n,w,"hard",e.hbinDefs,"hardBin",f));{const d=n.filter(N=>N.sbin!==void 0);h.push(...L(d,p,"soft",e.sbinDefs,"softBin",f),...L(d,b,"soft",e.sbinDefs,"softBin",f),...L(d,y,"soft",e.sbinDefs,"softBin",f),...L(d,z,"soft",e.sbinDefs,"softBin",f),...L(d,w,"soft",e.sbinDefs,"softBin",f))}const S=[...o];let v;if(f.enableTestValueAnalysis){const d=U(n,p,e.testDefs,f),N=U(n,b,e.testDefs,f),B=U(n,y,e.testDefs,f),V=U(n,z,e.testDefs,f),F=U(n,w,e.testDefs,f);h.push(...d.findings,...N.findings,...B.findings,...V.findings,...F.findings),d.warning&&S.push(d.warning),v=d.activeTestNumbers,h.push(...Ze(n,[p,b,y,z,w],e.testDefs,f)),h.push(...Q(n,p,e.testDefs,f),...Q(n,b,e.testDefs,f),...Q(n,y,e.testDefs,f),...Q(n,z,e.testDefs,f),...Q(n,w,e.testDefs,f))}{const d=_e(r,l,e.testDefs);(!r||l||d!==void 0)&&h.push(...Ae(u,e.wafer,{...f,isFailingDie:d}))}const D=at(h,{eligibleDies:n,softEligibleDies:n.filter(d=>d.sbin!==void 0),testDies:n,passBins:f.passBins,ringCount:f.ringCount,sectorCount:f.sectorCount,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs});h.length=0,h.push(...D);let $;if(l){const d=Oe(u,e.wafer,{passBins:f.passBins,ringCount:f.ringCount});if($=d??void 0,d!==null&&d.pattern!=="random"&&d.pattern!=="none"){let Fe=function(C){const E=j(C).map(K=>q(K).ring).filter(K=>K!==void 0);if(E.length===0)return!0;const ue=E.some(K=>K===Be),de=E.some(K=>K===1);return X==="edge-ring"||X==="edge-local"?ue:X==="center"?de:X==="donut"?!ue&&!de:!0};const B={center:"Center cluster",donut:"Donut","edge-ring":"Edge-ring","edge-local":"Edge-local",scratch:"Scratch","near-full":"Near-full"}[d.pattern]??d.pattern,V=d.confidence==="high"?"unusual":d.confidence==="medium"?"notable":"info",F=d.features,A=C=>`${(C*100).toFixed(0)}%`,G=d.pattern==="edge-ring"||d.pattern==="edge-local"?`${A(F.edgeRdd)} of edge dies failing`:d.pattern==="center"||d.pattern==="donut"?`centroid at ${A(F.centroidDistNorm)} of wafer radius from centre`:d.pattern==="scratch"?`linear score ${F.linearScore.toFixed(2)}, eccentricity ${F.eccentricity.toFixed(2)}`:`${A(F.globalRdd)} of dies failing`,_=n.filter(C=>{const E=C.hbin??C.sbin;return E!==void 0&&!new Set(f.passBins).has(E)}),Me={"edge-ring":["ring","edge-arc"],"edge-local":["edge-arc","sector","quadrant"],center:["ring","cluster"],donut:["ring"],scratch:["cluster","sector","quadrant"],"near-full":["ring"]},Re=new Set(Me[d.pattern]??[]),X=d.pattern,Be=f.ringCount,ee=[];for(const C of h){if(!Re.has(C.comparison.family)||C.comparison.family==="ring"&&!Fe(C))continue;ee.push(C.id);const E=C.comparison.family==="ring"||C.comparison.family==="quadrant"||C.comparison.family==="sector";V!=="info"&&E&&(C.severity="info")}h.push({id:`spatial-pattern:${d.pattern}`,level:"wafer",severity:V,variable:{kind:"spatialPattern",label:B},comparison:{family:"spatial-pattern",left:B,right:"Wafer"},effect:{direction:"different",effectSize:F.globalRdd},stats:{method:"geometry",sampleSizeLeft:_.length,sampleSizeRight:n.length},summary:`Spatial pattern: ${B.toLowerCase()} (${d.confidence} confidence) \u2014 ${G}${d.note?` [${d.note}]`:""}`,highlight:{kind:"dies",dieKeys:_.map(C=>T(C))},relatedIds:ee.length>0?ee:void 0})}}h.sort((d,N)=>{const B=d.stats.adjustedPValue??d.stats.pValue??1,V=N.stats.adjustedPValue??N.stats.pValue??1;return B!==V?B-V:Math.abs(N.effect.absoluteDelta??0)-Math.abs(d.effect.absoluteDelta??0)});const k=We(e.dies,n.length,e.yield.yieldPercent);r&&(k.isLotStack=!0,t&&(k.aggregationMethod=t));const M=Qe(e.dies,e.testDefs);M&&(k.testSpecYield=M);const R=computeFunctionalYield(e.dies,e.testDefs);if(R&&(k.functionalYield=R),$&&(k.spatialPattern=$),v===void 0&&f.computePerTestStats){const d=ze(n,f.testNumbers,e.testDefs);d.warning&&S.push(d.warning),v=d.testNumbers}if(v?.length){const d=Ue(e.dies,v,e.testDefs,f.minimumSampleSize);d&&(k.perTestStats=d);const N=Ne([{dies:e.dies}],e.testDefs,v);N&&(k.capability=N)}Object.assign(k,Pe(e.dies,e.testDefs));const P=Ve([{dies:e.dies,wafer:e.wafer,passBins:f.passBins,ringCount:f.ringCount}]);return P&&(k.regionYield=P),S.length>0&&(k.warnings=S),ft(h,n,f.passBins),{level:"wafer",hasNotableFindings:h.some(d=>d.severity==="notable"||d.severity==="unusual"),findings:h,wafer:e.wafer.metadata??void 0,stats:k}}
@@ -1,5 +1,6 @@
1
1
  import type { Die } from '../core/dies.js';
2
2
  import { type TestDef } from '../renderer/buildWaferMap.js';
3
+ import type { TestCapability } from './types.js';
3
4
  export interface CapabilityDatum {
4
5
  testNumber: number;
5
6
  label: string;
@@ -49,7 +50,21 @@ export interface CapabilityItem {
49
50
  * (`hasSpec: false`), normalized onto their own observed range instead, so a
50
51
  * lot where most tests lack full limits doesn't render as an empty chart.
51
52
  * Sorted spec'd-first (worst-Ppk-first within that tier), then unspec'd
52
- * (most-variable-first within that tier) — see the sort at the bottom.
53
+ * (most-variable-first within that tier).
53
54
  */
54
55
  export declare function buildCapabilityData(items: CapabilityItem[], testDefs: TestDef[]): CapabilityDatum[];
56
+ /**
57
+ * @internal Capability indices without the chart's normalised quantiles — what
58
+ * `stats.capability` carries. Same moments, formulas and order as
59
+ * `buildCapabilityData`, without collecting and sorting every value.
60
+ */
61
+ export declare function buildCapabilityFigures(items: CapabilityItem[], testDefs: TestDef[]): TestCapability[];
62
+ /**
63
+ * @internal Lot capability pooled exactly from each wafer's own capability
64
+ * figures, without revisiting a die: a wafer's (n, mean, stdOverall) give its
65
+ * sum and sum of squares, and — one wafer being one subgroup — its within-wafer
66
+ * variance. So a lot analysis given `perWaferSummaries` stays cheap. Specs come
67
+ * from the lot's merged `testDefs`, as they would for a direct computation.
68
+ */
69
+ export declare function poolCapabilityFigures(perWafer: readonly TestCapability[][], testDefs: TestDef[]): TestCapability[];
55
70
  //# sourceMappingURL=capability.d.ts.map
@@ -1 +1 @@
1
- import{isYieldEligibleDie as z}from"../core/dies.js";import{isParametricTest as A}from"../renderer/buildWaferMap.js";import{quantile as b}from"./math.js";export function buildCapabilityData(T,q){const k=new Map,N=new Map;for(const t of q){const n=t.testNumber;if(n===void 0||!A(t))continue;N.set(n,t);const o=t.limitLow,e=t.limitHigh;o!==void 0&&e!==void 0&&e>o&&k.set(n,{lsl:o,usl:e})}if(N.size===0)return[];const M=new Map,g=t=>{let n=M.get(t);return n||(n={n:0,sum:0,sumSq:0,withinNumerator:0,withinDenominator:0,allValues:[]},M.set(t,n)),n};for(const t of T){const n=new Map;for(const o of t.dies??[]){if(!z(o))continue;const e=o.testValues;if(e)for(const f of Object.keys(e)){const u=Number(f);if(!N.has(u))continue;const i=e[u];if(i===void 0||!Number.isFinite(i))continue;const a=g(u);a.n++,a.sum+=i,a.sumSq+=i*i,a.allValues.push(i);let s=n.get(u);s||(s={wn:0,wsum:0,wsumSq:0},n.set(u,s)),s.wn++,s.wsum+=i,s.wsumSq+=i*i}}for(const[o,e]of n){if(e.wn<2)continue;const f=g(o),u=e.wsum/e.wn,i=(e.wsumSq-e.wn*u*u)/(e.wn-1);f.withinNumerator+=(e.wn-1)*Math.max(0,i),f.withinDenominator+=e.wn-1}}const S=[];for(const t of q){const n=t.testNumber;if(n===void 0)continue;const o=M.get(n);if(!o||o.n===0)continue;const{n:e,sum:f,sumSq:u,withinNumerator:i,withinDenominator:a,allValues:s}=o;s.sort((l,h)=>l-h);const r=f/e,B=e>=2?Math.max(0,(u-e*r*r)/(e-1)):0,p=Math.sqrt(B),c=a>0?Math.sqrt(i/a):NaN,v=k.get(n),x=v!==void 0;let d,w,y=null,O=null,V=null,D=null,m;if(x){({lsl:d,usl:w}=v);const l=w-d;V=p>0?l/(6*p):null,D=p>0?Math.min((w-r)/(3*p),(r-d)/(3*p)):null,y=Number.isFinite(c)&&c>0?l/(6*c):null,O=Number.isFinite(c)&&c>0?Math.min((w-r)/(3*c),(r-d)/(3*c)):null,m=h=>(h-d)/l}else{const l=s[0],F=s[s.length-1]-l;m=F>0?j=>(j-l)/F:()=>.5}S.push({testNumber:n,label:t.name??`Test ${n}`,unit:t.unit,hasSpec:x,lsl:d,usl:w,mean:r,stdOverall:p,stdWithin:c,n:e,cp:y,cpk:O,pp:V,ppk:D,min:m(s[0]),q1:m(b(s,.25)),median:m(b(s,.5)),q3:m(b(s,.75)),max:m(s[s.length-1])})}return S.sort((t,n)=>t.hasSpec!==n.hasSpec?t.hasSpec?-1:1:t.hasSpec?t.ppk===null&&n.ppk===null?0:t.ppk===null?1:n.ppk===null?-1:t.ppk-n.ppk:n.stdOverall-t.stdOverall),S}
1
+ import{isYieldEligibleDie as B}from"../core/dies.js";import{isParametricTest as v}from"../renderer/buildWaferMap.js";import{quantile as S}from"./math.js";function q(h){const r=new Map,t=new Map;for(const m of h){const s=m.testNumber;if(s===void 0||!v(m))continue;t.set(s,m);const e=m.limitLow,o=m.limitHigh;e!==void 0&&o!==void 0&&o>e&&r.set(s,{lsl:e,usl:o})}return{defByTestNumber:t,specByTest:r}}function A(h,r,t){const m=new Map,s=[];for(const c of r.keys())m.set(c,s.length),s.push(c);const e=s.length,o=new Float64Array(e),n=new Float64Array(e),u=new Float64Array(e),a=new Float64Array(e),g=new Float64Array(e),N=new Float64Array(e),i=new Float64Array(e),d=new Float64Array(e),w=t?new Array(e):[],b=e<=64;for(const c of h){N.fill(0),i.fill(0),d.fill(0);for(const f of c.dies??[]){if(!B(f))continue;const y=f.testValues;if(y){if(b){for(let p=0;p<e;p++){const l=y[s[p]];l===void 0||!Number.isFinite(l)||(o[p]++,n[p]+=l,u[p]+=l*l,N[p]++,i[p]+=l,d[p]+=l*l,t&&(w[p]??=[]).push(l))}continue}for(const p in y){const l=m.get(+p);if(l===void 0)continue;const M=y[+p];M===void 0||!Number.isFinite(M)||(o[l]++,n[l]+=M,u[l]+=M*M,N[l]++,i[l]+=M,d[l]+=M*M,t&&(w[l]??=[]).push(M))}}}for(let f=0;f<e;f++){const y=N[f];if(y<2)continue;const p=i[f]/y,l=(d[f]-y*p*p)/(y-1);a[f]+=(y-1)*Math.max(0,l),g[f]+=y-1}}const F=new Map;for(let c=0;c<e;c++){if(o[c]===0)continue;const f=s[c];F.set(f,{n:o[c],sum:n[c],sumSq:u[c],withinNumerator:a[c],withinDenominator:g[c]}),t&&w[c]&&t.set(f,w[c])}return F}function T(h,r,t,m){const{n:s,sum:e,sumSq:o,withinNumerator:n,withinDenominator:u}=m,a=e/s,g=s>=2?Math.max(0,(o-s*a*a)/(s-1)):0,N=Math.sqrt(g),i=u>0?Math.sqrt(n/u):NaN;let d=null,w=null,b=null,F=null;if(t){const{lsl:c,usl:f}=t,y=f-c;b=N>0?y/(6*N):null,F=N>0?Math.min((f-a)/(3*N),(a-c)/(3*N)):null,d=Number.isFinite(i)&&i>0?y/(6*i):null,w=Number.isFinite(i)&&i>0?Math.min((f-a)/(3*i),(a-c)/(3*i)):null}return{testNumber:h,label:r.name??`Test ${h}`,unit:r.unit,hasSpec:t!==void 0,lsl:t?.lsl,usl:t?.usl,mean:a,stdOverall:N,stdWithin:i,n:s,cp:d,cpk:w,pp:b,ppk:F}}function k(h){return h.sort((r,t)=>r.hasSpec!==t.hasSpec?r.hasSpec?-1:1:r.hasSpec?r.ppk===null&&t.ppk===null?0:r.ppk===null?1:t.ppk===null?-1:r.ppk-t.ppk:t.stdOverall-r.stdOverall)}export function buildCapabilityData(h,r){const{defByTestNumber:t,specByTest:m}=q(r);if(t.size===0)return[];const s=new Map,e=A(h,t,s),o=[];for(const n of r){const u=n.testNumber;if(u===void 0)continue;const a=e.get(u);if(!a||a.n===0||!t.has(u))continue;const g=m.get(u),N=T(u,n,g,a),i=s.get(u).sort((w,b)=>w-b);let d;if(g){const w=g.usl-g.lsl;d=b=>(b-g.lsl)/w}else{const w=i[0],b=i[i.length-1]-w;d=b>0?F=>(F-w)/b:()=>.5}o.push({...N,min:d(i[0]),q1:d(S(i,.25)),median:d(S(i,.5)),q3:d(S(i,.75)),max:d(i[i.length-1])})}return k(o)}export function buildCapabilityFigures(h,r){const{defByTestNumber:t,specByTest:m}=q(r);if(t.size===0)return[];const s=A(h,t,null),e=[];for(const[o,n]of s)n.n!==0&&e.push(T(o,t.get(o),m.get(o),n));return k(e)}export function poolCapabilityFigures(h,r){const{defByTestNumber:t,specByTest:m}=q(r),s=new Map;for(const o of h)for(const n of o){if(!t.has(n.testNumber)||n.n===0)continue;let u=s.get(n.testNumber);u||(u={n:0,sum:0,sumSq:0,withinNumerator:0,withinDenominator:0},s.set(n.testNumber,u));const a=n.stdOverall*n.stdOverall;u.n+=n.n,u.sum+=n.n*n.mean,u.sumSq+=(n.n-1)*a+n.n*n.mean*n.mean,n.n>=2&&(u.withinNumerator+=(n.n-1)*a,u.withinDenominator+=n.n-1)}const e=[];for(const[o,n]of s)e.push(T(o,t.get(o),m.get(o),n));return k(e)}
@@ -7,80 +7,75 @@ import { buildTestHistogramData as buildTestHistogramDataImpl, buildTestHistogra
7
7
  import { buildScatterData as buildScatterDataImpl, buildScatterDataGrouped as buildScatterDataGroupedImpl } from './scatter.js';
8
8
  import { buildYieldData as buildYieldDataImpl, buildYieldDataCombined as buildYieldDataCombinedImpl } from './yield.js';
9
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 }`). */
10
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` and `analyzeWaferLot` now return Cp/Cpk/Pp/Ppk as `stats.capability` (enable `computePerTestStats`); a lot's uses the pooled within-wafer stddev. */
11
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 }`). */
12
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights correlation matrix, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement yet. */
13
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 }`). */
14
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights correlation matrix, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement. */
15
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 }`). */
16
+ /** @deprecated Removed in 0.31.0. Five-number summaries are `stats.perTestStats` (enable `computePerTestStats`), and `perWaferTestStats` on `analyzeWaferLot`'s result. */
17
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 }`). */
18
+ /** @deprecated Removed in 0.31.0. Per-wafer means and stddevs in slot order are `perWaferTestStats` on `analyzeWaferLot`'s result (enable `computePerTestStats`). */
19
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 }`). */
20
+ /** @deprecated Removed in 0.31.0. Compute the die-weighted mean from `perWaferTestStats` (mean × count) on `analyzeWaferLot`'s result. */
21
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 }`). */
22
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` and `analyzeWaferLot` now return per-test pass rates as `stats.testSpecYield` (spec limits), `stats.testFlagYield` (tester verdicts) and `stats.functionalYield`, with `stats.specVerdictDisagreementDies.` */
23
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 }`). */
24
+ /** @deprecated Removed in 0.31.0. Read which pass rates exist from `stats.testSpecYield`, `stats.testFlagYield` and `stats.functionalYield` on `analyzeWaferMap`'s or `analyzeWaferLot`'s result. */
25
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 }`). */
26
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights histogram, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement. */
27
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 }`). */
28
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights histogram, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement. */
29
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 }`). */
30
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights scatter plot, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement. */
31
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 }`). */
32
+ /** @deprecated Removed in 0.31.0. It prepared data for the Insights scatter plot, which wmap draws itself (`insights: { enabled: true }`); there is no data replacement. */
33
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 }`). */
34
+ /** @deprecated Removed in 0.31.0. Per-wafer yield is `lotYieldSeries` on `analyzeWaferLot`'s result, and `stats.yieldPercent` on each summary. */
35
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 }`). */
36
+ /** @deprecated Removed in 0.31.0. Per-wafer yield is `lotYieldSeries` on `analyzeWaferLot`'s result; weight it by each wafer's die count to combine groups. */
37
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 }`). */
38
+ /** @deprecated Removed in 0.31.0. Bin counts are `stats.hardBinCounts` and `stats.softBinCounts` on `analyzeWaferMap`'s result. */
39
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 }`). */
40
+ /** @deprecated Removed in 0.31.0. Bin counts per wafer are `stats.hardBinCounts` and `stats.softBinCounts` on each perWafer summary of `analyzeWaferLot`'s result. */
41
41
  export declare const buildBinClusterData: typeof buildBinClusterDataImpl;
42
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
43
  import { classifyPattern as classifyPatternImpl } from './patternClassification.js';
44
- import { visibleFindings as visibleFindingsImpl } from './filterFindings.js';
45
44
  import { computeFunctionalYield as computeFunctionalYieldImpl } from './analyzeWaferMap.js';
46
45
  import { resolveMetadataColumns as resolveMetadataColumnsImpl, discoverDieMetadataKeys as discoverDieMetadataKeysImpl } from './metadataColumns.js';
47
- import { renderFindingsReportHtml as renderFindingsReportHtmlImpl, openHtmlReport as openHtmlReportImpl } from './renderFindingsReport.js';
46
+ import { openHtmlReport as openHtmlReportImpl } from './renderFindingsReport.js';
48
47
  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. */
48
+ /** @deprecated Removed in 0.31.0. Ring yield is `stats.regionYield.ring` on `analyzeWaferMap`'s or `analyzeWaferLot`'s result; ring findings are in findings. */
50
49
  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. */
50
+ /** @deprecated Removed in 0.31.0. Quadrant yield is `stats.regionYield.quadrant` on `analyzeWaferMap`'s or `analyzeWaferLot`'s result; quadrant findings are in findings. */
52
51
  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. */
52
+ /** @deprecated Removed in 0.31.0. Sector findings are in `analyzeWaferMap`'s findings (`comparison.family` 'sector'). */
54
53
  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. */
54
+ /** @deprecated Removed in 0.31.0. Reticle-position findings are in `analyzeWaferMap`'s findings; a die's reticle cell is `getReticleCell(die, reticleConfig)`. */
56
55
  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. */
56
+ /** @deprecated Removed in 0.31.0. Test-site findings are in `analyzeWaferMap`'s findings (`comparison.family` 'test-site'). */
58
57
  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. */
58
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` and `analyzeWaferLot` now return ring and quadrant yield as `stats.regionYield.` */
60
59
  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. */
60
+ /** @deprecated Removed in 0.31.0. It is an internal helper of the quadrant findings, which `analyzeWaferMap` already reports. */
62
61
  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. */
62
+ /** @deprecated Removed in 0.31.0. Findings name their region in prose (`comparison.left`) and region yield in `RegionYield.label`; region keys are identities, not to be parsed. */
64
63
  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. */
64
+ /** @deprecated Removed in 0.31.0. Sector findings name their sector in prose (`comparison.left`). */
66
65
  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'). */
66
+ /** @deprecated Removed in 0.31.0. `analyzeWaferMap` returns the classification, with its geometry features, as `stats.spatialPattern` for every wafer, and a detected pattern as a finding (`comparison.family` `'spatial-pattern'`). */
68
67
  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
68
  /** @deprecated Removed in 0.31.0. `analyzeWaferMap` returns the same figures as `stats.functionalYield`. */
72
69
  export declare const computeFunctionalYield: typeof computeFunctionalYieldImpl;
73
70
  /** @deprecated Removed in 0.31.0. The die list resolves its metadata columns itself (the `dieList.metadataColumns` option). */
74
71
  export declare const resolveMetadataColumns: typeof resolveMetadataColumnsImpl;
75
72
  /** @deprecated Removed in 0.31.0. The die list resolves its metadata columns itself (the `dieList.metadataColumns` option). */
76
73
  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`. */
74
+ /** @deprecated Removed in 0.31.0. Open reports with `openReportModal(html)`, or route them into your host with `setReportOpener`. */
80
75
  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`. */
76
+ /** @deprecated Removed in 0.31.0. Use `renderWaferReportHtml(result, summary)`, which reads pass bins and ring count from the built map. */
82
77
  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`. */
78
+ /** @deprecated Removed in 0.31.0. Use `renderLotReportHtml(results)`, which reads each wafer's pass bins and ring count from its built map. */
84
79
  export declare const renderLotSummaryReportHtml: typeof renderLotSummaryReportHtmlImpl;
85
80
  /** @deprecated Removed in 0.31.0. `buildFacetTable` applies it by default. */
86
81
  export declare const DEFAULT_FACET_CURATION: Record<string, import("./facets.js").FacetCuration>;
@@ -1 +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");
1
+ import{deprecated as e}from"../renderer/deprecate.js";import{buildCapabilityData as s}from"./capability.js";import{buildCorrelationMatrix as r,filterCorrelationMatrix as i}from"./correlation.js";import{buildTestBoxplotData as o}from"./boxplot.js";import{buildTestTrendData as n,trendCentre as l}from"./trend.js";import{buildTestPassRateData as d,hasJudgeableTests as p}from"./testPassRate.js";import{buildTestHistogramData as m,buildTestHistogramSeries as u}from"./histogram.js";import{buildScatterData as c,buildScatterDataGrouped as f}from"./scatter.js";import{buildYieldData as b,buildYieldDataCombined as g}from"./yield.js";import{buildBinParetoData as h,buildBinClusterData as y}from"./binPareto.js";const t="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.";export const buildCapabilityData=e(s,"buildCapabilityData",`analyzeWaferMap and analyzeWaferLot now return Cp/Cpk/Pp/Ppk as stats.capability (enable computePerTestStats); a lot's uses the pooled within-wafer stddev. ${t}`),buildCorrelationMatrix=e(r,"buildCorrelationMatrix",`It prepared data for the Insights correlation matrix, which wmap draws itself (insights: { enabled: true }); there is no data replacement yet. ${t}`),filterCorrelationMatrix=e(i,"filterCorrelationMatrix",`It prepared data for the Insights correlation matrix, which wmap draws itself (insights: { enabled: true }); there is no data replacement. ${t}`),buildTestBoxplotData=e(o,"buildTestBoxplotData",`Five-number summaries are stats.perTestStats (enable computePerTestStats), and perWaferTestStats on analyzeWaferLot's result. ${t}`),buildTestTrendData=e(n,"buildTestTrendData",`Per-wafer means and stddevs in slot order are perWaferTestStats on analyzeWaferLot's result (enable computePerTestStats). ${t}`),trendCentre=e(l,"trendCentre",`Compute the die-weighted mean from perWaferTestStats (mean \xD7 count) on analyzeWaferLot's result. ${t}`),buildTestPassRateData=e(d,"buildTestPassRateData",`analyzeWaferMap and analyzeWaferLot now return per-test pass rates as stats.testSpecYield (spec limits), stats.testFlagYield (tester verdicts) and stats.functionalYield, with stats.specVerdictDisagreementDies. ${t}`),hasJudgeableTests=e(p,"hasJudgeableTests",`Read which pass rates exist from stats.testSpecYield, stats.testFlagYield and stats.functionalYield on analyzeWaferMap's or analyzeWaferLot's result. ${t}`),buildTestHistogramData=e(m,"buildTestHistogramData",`It prepared data for the Insights histogram, which wmap draws itself (insights: { enabled: true }); there is no data replacement. ${t}`),buildTestHistogramSeries=e(u,"buildTestHistogramSeries",`It prepared data for the Insights histogram, which wmap draws itself (insights: { enabled: true }); there is no data replacement. ${t}`),buildScatterData=e(c,"buildScatterData",`It prepared data for the Insights scatter plot, which wmap draws itself (insights: { enabled: true }); there is no data replacement. ${t}`),buildScatterDataGrouped=e(f,"buildScatterDataGrouped",`It prepared data for the Insights scatter plot, which wmap draws itself (insights: { enabled: true }); there is no data replacement. ${t}`),buildYieldData=e(b,"buildYieldData",`Per-wafer yield is lotYieldSeries on analyzeWaferLot's result, and stats.yieldPercent on each summary. ${t}`),buildYieldDataCombined=e(g,"buildYieldDataCombined",`Per-wafer yield is lotYieldSeries on analyzeWaferLot's result; weight it by each wafer's die count to combine groups. ${t}`),buildBinParetoData=e(h,"buildBinParetoData",`Bin counts are stats.hardBinCounts and stats.softBinCounts on analyzeWaferMap's result. ${t}`),buildBinClusterData=e(y,"buildBinClusterData",`Bin counts per wafer are stats.hardBinCounts and stats.softBinCounts on each perWafer summary of analyzeWaferLot's result. ${t}`);import{deprecatedValue as R}from"../renderer/deprecate.js";import{buildRingRegions as D,buildQuadrantRegions as T,buildSectorRegions as I,buildReticlePositionRegions as C,buildTestSiteRegions as x,buildRegionYieldData as S,areQuadrantsAdjacent as w,parseRegionKey as $,sectorCompassNames as M}from"./regions.js";import{classifyPattern as W}from"./patternClassification.js";import{computeFunctionalYield as Y}from"./analyzeWaferMap.js";import{resolveMetadataColumns as P,discoverDieMetadataKeys as z}from"./metadataColumns.js";import{openHtmlReport as L}from"./renderFindingsReport.js";import{renderSummaryReportHtml as A,renderLotSummaryReportHtml as H}from"./renderSummaryReport.js";import{DEFAULT_FACET_CURATION as B}from"./facets.js";const F="analyzeWaferMap returns the classification, with its geometry features, as stats.spatialPattern for every wafer, and a detected pattern as a finding (comparison.family 'spatial-pattern').",E="analyzeWaferMap returns the same figures as stats.functionalYield.",a="The die list resolves its metadata columns itself (the dieList.metadataColumns option).";export const buildRingRegions=e(D,"buildRingRegions",`Ring yield is stats.regionYield.ring on analyzeWaferMap's or analyzeWaferLot's result; ring findings are in findings. ${t}`),buildQuadrantRegions=e(T,"buildQuadrantRegions",`Quadrant yield is stats.regionYield.quadrant on analyzeWaferMap's or analyzeWaferLot's result; quadrant findings are in findings. ${t}`),buildSectorRegions=e(I,"buildSectorRegions",`Sector findings are in analyzeWaferMap's findings (comparison.family 'sector'). ${t}`),buildReticlePositionRegions=e(C,"buildReticlePositionRegions",`Reticle-position findings are in analyzeWaferMap's findings; a die's reticle cell is getReticleCell(die, reticleConfig). ${t}`),buildTestSiteRegions=e(x,"buildTestSiteRegions",`Test-site findings are in analyzeWaferMap's findings (comparison.family 'test-site'). ${t}`),buildRegionYieldData=e(S,"buildRegionYieldData",`analyzeWaferMap and analyzeWaferLot now return ring and quadrant yield as stats.regionYield. ${t}`),areQuadrantsAdjacent=e(w,"areQuadrantsAdjacent",`It is an internal helper of the quadrant findings, which analyzeWaferMap already reports. ${t}`),parseRegionKey=e($,"parseRegionKey",`Findings name their region in prose (comparison.left) and region yield in RegionYield.label; region keys are identities, not to be parsed. ${t}`),sectorCompassNames=e(M,"sectorCompassNames",`Sector findings name their sector in prose (comparison.left). ${t}`),classifyPattern=e(W,"classifyPattern",`${F} ${t}`),computeFunctionalYield=e(Y,"computeFunctionalYield",`${E} ${t}`),resolveMetadataColumns=e(P,"resolveMetadataColumns",`${a} ${t}`),discoverDieMetadataKeys=e(z,"discoverDieMetadataKeys",`${a} ${t}`),openHtmlReport=e(L,"openHtmlReport",`Open reports with openReportModal(html), or route them into your host with setReportOpener. ${t}`),renderSummaryReportHtml=e(A,"renderSummaryReportHtml",`Use renderWaferReportHtml(result, summary), which reads pass bins and ring count from the built map. ${t}`),renderLotSummaryReportHtml=e(H,"renderLotSummaryReportHtml",`Use renderLotReportHtml(results), which reads each wafer's pass bins and ring count from its built map. ${t}`),DEFAULT_FACET_CURATION=R(B,"DEFAULT_FACET_CURATION");
@@ -4,7 +4,9 @@ export * from './analyzeWaferMap.js';
4
4
  export * from './analyzeWaferLot.js';
5
5
  export * from './filterFindings.js';
6
6
  export type { PatternLabel, PatternClassification, PatternFeatures } from './patternClassification.js';
7
- export { setReportOpener } from './renderFindingsReport.js';
7
+ export { setReportOpener, renderFindingsReportHtml } from './renderFindingsReport.js';
8
+ export { renderWaferReportHtml, renderLotReportHtml } from './renderSummaryReport.js';
9
+ export type { ReportMap } from './renderSummaryReport.js';
8
10
  export type { SummaryReportParams, LotSummaryReportParams } from './renderSummaryReport.js';
9
11
  export { buildFacetTable, facetValueOf, FACET_NONE_VALUE } from './facets.js';
10
12
  export type { FacetField, FacetValue, FacetCuration, FacetItem, BuildFacetTableOptions } from './facets.js';
@@ -20,5 +22,5 @@ export type { HistogramBucket, HistogramItem, HistogramSeries, HistogramSeriesDa
20
22
  export type { ScatterPoint, ScatterItem } from './scatter.js';
21
23
  export type { ChartDatum, YieldItem, YieldSortBy } from './yield.js';
22
24
  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';
25
+ 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, computeFunctionalYield, resolveMetadataColumns, discoverDieMetadataKeys, openHtmlReport, renderSummaryReportHtml, renderLotSummaryReportHtml, DEFAULT_FACET_CURATION, } from './deprecated.js';
24
26
  //# 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{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
+ export*from"./types.js";export*from"./regions.js";export*from"./analyzeWaferMap.js";export*from"./analyzeWaferLot.js";export*from"./filterFindings.js";export{setReportOpener,renderFindingsReportHtml}from"./renderFindingsReport.js";export{renderWaferReportHtml,renderLotReportHtml}from"./renderSummaryReport.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,computeFunctionalYield,resolveMetadataColumns,discoverDieMetadataKeys,openHtmlReport,renderSummaryReportHtml,renderLotSummaryReportHtml,DEFAULT_FACET_CURATION}from"./deprecated.js";
@@ -0,0 +1,10 @@
1
+ import { type WaferMapResult } from '../renderer/buildWaferMap.js';
2
+ import type { AnalyzeWaferMapInput } from './types.js';
3
+ /**
4
+ * Build an analysis input if it is not already a built map. A module of its own
5
+ * so it stays internal: stats/index.ts re-exports analyzeWaferMap.ts wholesale.
6
+ * analyzeWaferLot builds each wafer once here and passes the built result down,
7
+ * so its lot figures and each wafer's analysis read the same dies.
8
+ */
9
+ export declare function normalizeInput(input: AnalyzeWaferMapInput): WaferMapResult;
10
+ //# sourceMappingURL=normalizeInput.d.ts.map
@@ -0,0 +1 @@
1
+ import{buildWaferMap as e}from"../renderer/buildWaferMap.js";export function normalizeInput(r){return"wafer"in r&&"dies"in r&&"view"in r?r:e(r)}
@@ -14,6 +14,8 @@ export interface RegionYieldDatum {
14
14
  yieldPercent: number;
15
15
  /** Yield-eligible, binned die count in this region. */
16
16
  n: number;
17
+ /** Of those, the dies that pass. */
18
+ passDies: number;
17
19
  }
18
20
  /**
19
21
  * Pass/fail yield per region (ring, quadrant, or any other `StatsRegion`
@@ -1 +1 @@
1
- import{classifyDie as E,getRingLabel as W}from"../core/classify.js";import{getReticleCell as b}from"../core/reticle.js";import{isPositionedDie as M,diePassStatus as k,getDieKey as x}from"../core/dies.js";function y(t){return x(t)}export function buildRegionYieldData(t,o,r,e,n){const i=new Map,a=[];for(let s=0;s<o.length;s++){const c=t[s];if(!c?.length)continue;const u=new Set(typeof e=="function"?e(s):e),f=n(c.filter(M),o[s],r),S=new Map(c.map(l=>[y(l),l]));for(const l of f){a.includes(l.key)||a.push(l.key);const g=i.get(l.key)??{label:l.label,pass:0,total:0};for(const m of l.dieKeys){const d=S.get(m);if(!d||d.partial||d.edgeExcluded)continue;const p=k(d,u);p!==void 0&&(g.total++,p&&g.pass++)}i.set(l.key,g)}}return a.map(s=>{const c=i.get(s);return c.total===0?null:{key:s,label:c.label,n:c.total,yieldPercent:c.pass/c.total*100}}).filter(s=>s!==null)}const R=["E","ENE","NE","NNE","N","NNW","NW","WNW","W","WSW","SW","SSW","S","SSE","SE","ESE"],K=["E","NE","N","NW","W","SW","S","SE"],q=["E","N","W","S"];export function sectorCompassNames(t){const o=[4,8,16,32].includes(t)?t:16;return o===4?q:o===8?K:R}const P={NE:["NW","SE"],NW:["NE","SW"],SE:["NE","SW"],SW:["NW","SE"]};export function areQuadrantsAdjacent(t,o){return P[t]?.includes(o)??!1}export function parseRegionKey(t){return t.startsWith("ring:")?{family:"ring",ring:Number(t.slice(5))}:t.startsWith("quadrant:")?{family:"quadrant",quadrant:t.slice(9)}:t.startsWith("sector:")?{family:"sector",sector:t.slice(7)}:{family:"unknown"}}export function buildRingRegions(t,o,r){const e=new Map;for(const n of t){const{ring:i}=E(n,o,{ringCount:r}),a=`ring:${i}`,s=e.get(a)??{family:"ring",key:a,label:W(i,r),dieKeys:[]};s.dieKeys.push(y(n)),e.set(a,s)}return[...e.values()].sort((n,i)=>n.key.localeCompare(i.key))}export function buildQuadrantRegions(t,o,r){const e=new Map;for(const i of t){const{quadrant:a}=E(i,o,{ringCount:r}),s=`quadrant:${a}`,c=e.get(s)??{family:"quadrant",key:s,label:a,dieKeys:[]};c.dieKeys.push(y(i)),e.set(s,c)}const n=new Map([["quadrant:NE",0],["quadrant:NW",1],["quadrant:SE",2],["quadrant:SW",3]]);return[...e.values()].sort((i,a)=>(n.get(i.key)??4)-(n.get(a.key)??4))}export function buildReticlePositionRegions(t,o){if(!o)return[];const r=new Map;for(const e of t){const{column:n,row:i}=b(e,o),a=`reticle-position:cell:${n},${i}`,s=r.get(a)??{family:"reticle-position",key:a,label:`Reticle cell (${n}, ${i})`,dieKeys:[]};s.dieKeys.push(y(e)),r.set(a,s)}return[...r.values()].sort((e,n)=>e.key.localeCompare(n.key))}const w=3;export function buildTestSiteRegions(t,o=!1){const r=new Map;for(const e of t){if(e.siteNum===void 0)continue;const n=r.get(e.siteNum)??[];n.push(y(e)),r.set(e.siteNum,n)}if(!o){let e=0;for(const n of r.values())n.length>=w&&e++;if(e<2)return[]}return[...r.entries()].sort(([e],[n])=>e-n).map(([e,n])=>({family:"test-site",key:`test-site:${e}`,label:`Site ${e}`,dieKeys:n}))}export function buildSectorRegions(t,o,r){const e=[4,8,16,32].includes(r)?r:16,n=sectorCompassNames(e),i=new Map,a=o.center.x,s=o.center.y,c=o.radius;for(const u of t){const f=u.physX-a,S=u.physY-s;if(Math.hypot(f,S)/c<.2)continue;const g=Math.PI/e,m=(Math.atan2(S,f)+2*Math.PI+g)%(2*Math.PI),d=Math.floor(m/(2*Math.PI)*e)%e,p=n[d],N=`sector:${p}`,h=i.get(N)??{family:"sector",key:N,label:`Sector ${p}`,dieKeys:[]};h.dieKeys.push(y(u)),i.set(N,h)}return[...i.values()].sort((u,f)=>u.key.localeCompare(f.key))}
1
+ import{classifyDie as E,getRingLabel as W}from"../core/classify.js";import{getReticleCell as b}from"../core/reticle.js";import{isPositionedDie as M,diePassStatus as k,getDieKey as x}from"../core/dies.js";function y(t){return x(t)}export function buildRegionYieldData(t,o,r,e,n){const i=new Map,a=[];for(let s=0;s<o.length;s++){const c=t[s];if(!c?.length)continue;const u=new Set(typeof e=="function"?e(s):e),f=n(c.filter(M),o[s],r),S=new Map(c.map(l=>[y(l),l]));for(const l of f){a.includes(l.key)||a.push(l.key);const g=i.get(l.key)??{label:l.label,pass:0,total:0};for(const m of l.dieKeys){const d=S.get(m);if(!d||d.partial||d.edgeExcluded)continue;const p=k(d,u);p!==void 0&&(g.total++,p&&g.pass++)}i.set(l.key,g)}}return a.map(s=>{const c=i.get(s);return c.total===0?null:{key:s,label:c.label,n:c.total,passDies:c.pass,yieldPercent:c.pass/c.total*100}}).filter(s=>s!==null)}const R=["E","ENE","NE","NNE","N","NNW","NW","WNW","W","WSW","SW","SSW","S","SSE","SE","ESE"],K=["E","NE","N","NW","W","SW","S","SE"],q=["E","N","W","S"];export function sectorCompassNames(t){const o=[4,8,16,32].includes(t)?t:16;return o===4?q:o===8?K:R}const P={NE:["NW","SE"],NW:["NE","SW"],SE:["NE","SW"],SW:["NW","SE"]};export function areQuadrantsAdjacent(t,o){return P[t]?.includes(o)??!1}export function parseRegionKey(t){return t.startsWith("ring:")?{family:"ring",ring:Number(t.slice(5))}:t.startsWith("quadrant:")?{family:"quadrant",quadrant:t.slice(9)}:t.startsWith("sector:")?{family:"sector",sector:t.slice(7)}:{family:"unknown"}}export function buildRingRegions(t,o,r){const e=new Map;for(const n of t){const{ring:i}=E(n,o,{ringCount:r}),a=`ring:${i}`,s=e.get(a)??{family:"ring",key:a,label:W(i,r),dieKeys:[]};s.dieKeys.push(y(n)),e.set(a,s)}return[...e.values()].sort((n,i)=>n.key.localeCompare(i.key))}export function buildQuadrantRegions(t,o,r){const e=new Map;for(const i of t){const{quadrant:a}=E(i,o,{ringCount:r}),s=`quadrant:${a}`,c=e.get(s)??{family:"quadrant",key:s,label:a,dieKeys:[]};c.dieKeys.push(y(i)),e.set(s,c)}const n=new Map([["quadrant:NE",0],["quadrant:NW",1],["quadrant:SE",2],["quadrant:SW",3]]);return[...e.values()].sort((i,a)=>(n.get(i.key)??4)-(n.get(a.key)??4))}export function buildReticlePositionRegions(t,o){if(!o)return[];const r=new Map;for(const e of t){const{column:n,row:i}=b(e,o),a=`reticle-position:cell:${n},${i}`,s=r.get(a)??{family:"reticle-position",key:a,label:`Reticle cell (${n}, ${i})`,dieKeys:[]};s.dieKeys.push(y(e)),r.set(a,s)}return[...r.values()].sort((e,n)=>e.key.localeCompare(n.key))}const w=3;export function buildTestSiteRegions(t,o=!1){const r=new Map;for(const e of t){if(e.siteNum===void 0)continue;const n=r.get(e.siteNum)??[];n.push(y(e)),r.set(e.siteNum,n)}if(!o){let e=0;for(const n of r.values())n.length>=w&&e++;if(e<2)return[]}return[...r.entries()].sort(([e],[n])=>e-n).map(([e,n])=>({family:"test-site",key:`test-site:${e}`,label:`Site ${e}`,dieKeys:n}))}export function buildSectorRegions(t,o,r){const e=[4,8,16,32].includes(r)?r:16,n=sectorCompassNames(e),i=new Map,a=o.center.x,s=o.center.y,c=o.radius;for(const u of t){const f=u.physX-a,S=u.physY-s;if(Math.hypot(f,S)/c<.2)continue;const g=Math.PI/e,m=(Math.atan2(S,f)+2*Math.PI+g)%(2*Math.PI),d=Math.floor(m/(2*Math.PI)*e)%e,p=n[d],N=`sector:${p}`,h=i.get(N)??{family:"sector",key:N,label:`Sector ${p}`,dieKeys:[]};h.dieKeys.push(y(u)),i.set(N,h)}return[...i.values()].sort((u,f)=>u.key.localeCompare(f.key))}
@@ -1,4 +1,4 @@
1
- import{describeWaferPopulation as b,populationLabel as h}from"./population.js";import{formatFindingDelta as g,formatFindingCoverage as v,formatFindingTooltip as y,escHtml as o,buildMetadataRows as d,renderDefinitionList as p,renderSection as c,renderSeverityBadge as R,reportStyles as $}from"./reportHtml.js";import{buildFindingsNarrative as F}from"./findingsNarrative.js";import{plainBinTerms as f}from"../renderer/fmt.js";import{visibleFindings as S}from"./filterFindings.js";function T(e,n){if(e.level==="lot"){const a=e,l=a.perWafer.map(r=>r.summary.wafer?.wafer??r.summary.wafer?.waferId).filter(r=>r!=null).map(String).join(", ");return p([...d(a.perWafer.map(r=>({metadata:r.summary.wafer}))),...l?[{label:"Wafers",value:l}]:[],{label:"Wafer count",value:String(a.stats.waferCount)},{label:"Generated",value:n}])}const t=e,i=t.stats.yieldPercent!==null?`${t.stats.yieldPercent.toFixed(1)}%`:"N/A";return p([...d([{metadata:t.wafer}]),{label:"Total dies",value:String(t.stats.totalDies)},{label:"Analysed dies",value:String(t.stats.analyzedDies)},{label:"Yield",value:i},{label:"Generated",value:n}])}function H(e,n){return e.length?e.map(t=>`<tr title="${o(y(t))}">
1
+ import{describeWaferPopulation as b,populationLabel as h}from"./population.js";import{formatFindingDelta as g,formatFindingCoverage as v,formatFindingTooltip as y,buildMetadataRows as d,renderDefinitionList as p,renderSection as c,renderSeverityBadge as R,reportStyles as $}from"./reportHtml.js";import{escHtml as o}from"../core/utils.js";import{buildFindingsNarrative as F}from"./findingsNarrative.js";import{plainBinTerms as f}from"../renderer/fmt.js";import{visibleFindings as S}from"./filterFindings.js";function T(e,n){if(e.level==="lot"){const a=e,l=a.perWafer.map(r=>r.summary.wafer?.wafer??r.summary.wafer?.waferId).filter(r=>r!=null).map(String).join(", ");return p([...d(a.perWafer.map(r=>({metadata:r.summary.wafer}))),...l?[{label:"Wafers",value:l}]:[],{label:"Wafer count",value:String(a.stats.waferCount)},{label:"Generated",value:n}])}const t=e,i=t.stats.yieldPercent!==null?`${t.stats.yieldPercent.toFixed(1)}%`:"N/A";return p([...d([{metadata:t.wafer}]),{label:"Total dies",value:String(t.stats.totalDies)},{label:"Analysed dies",value:String(t.stats.analyzedDies)},{label:"Yield",value:i},{label:"Generated",value:n}])}function H(e,n){return e.length?e.map(t=>`<tr title="${o(y(t))}">
2
2
  <td class="tight">${R(t.severity)}</td>
3
3
  <td class="tight">${o(t.comparison.left)}</td>
4
4
  <td>${o(f(t.variable.label))}</td>
@@ -1,6 +1,6 @@
1
1
  import type { Die } from '../core/dies.js';
2
2
  import type { Wafer } from '../core/wafer.js';
3
- import { type BinDef, type TestDef, type YieldSummary } from '../renderer/buildWaferMap.js';
3
+ import { type BinDef, type TestDef, type YieldSummary, type WaferMapResult } from '../renderer/buildWaferMap.js';
4
4
  import type { StatsSummary, AnalyzeWaferMapOptions } from './types.js';
5
5
  import { openHtmlReport } from './renderFindingsReport.js';
6
6
  export interface SummaryReportParams {
@@ -37,6 +37,14 @@ export interface LotSummaryReportParams {
37
37
  /** Reused directly as `analyzeWaferLot`'s `perWaferSummaries` — the expensive
38
38
  * per-wafer pass (`analyzeWaferMap`) is never re-run here. */
39
39
  statsSummary?: StatsSummary;
40
+ /**
41
+ * @internal The built map these pieces came from, analysed in place of them.
42
+ * The pieces alone are not an analysable map: `analyzeWaferLot` took them for
43
+ * a fresh input with no results and analysed an EMPTY wafer, so a report for
44
+ * wafers without a precomputed summary had no findings, no yields and lost
45
+ * its lot title. `renderLotReportHtml` always sets it.
46
+ */
47
+ source?: WaferMapResult;
40
48
  }>;
41
49
  hbinDefs?: BinDef[];
42
50
  sbinDefs?: BinDef[];
@@ -60,4 +68,34 @@ export declare function renderLotSummaryReportHtml(params: LotSummaryReportParam
60
68
  title?: string;
61
69
  }): string;
62
70
  export { openHtmlReport };
71
+ /** A built map as the report builders read it. A `WaferMapResult` is one. */
72
+ export type ReportMap = Pick<WaferMapResult, 'wafer' | 'dies' | 'passBins' | 'ringCount'> & Partial<Pick<WaferMapResult, 'hbinDefs' | 'sbinDefs' | 'testDefs'>> & {
73
+ /** The name to show for this wafer; defaults to its wafer ID. */
74
+ label?: string;
75
+ /** This wafer's analysis, when already run — reused, never recomputed. */
76
+ statsSummary?: StatsSummary;
77
+ };
78
+ /**
79
+ * The wafer summary report for one built map, as a standalone HTML document.
80
+ * Analyses the map with `analyzeWaferMap` when `summary` is not given and the
81
+ * map is a built `WaferMapResult`; a map assembled from pieces is not analysable
82
+ * (the analysis would take it for a fresh input with no results), so its report
83
+ * has no findings section rather than findings of an empty wafer.
84
+ */
85
+ export declare function renderWaferReportHtml(map: ReportMap & {
86
+ yield: YieldSummary;
87
+ dataCoverage: SummaryReportParams['dataCoverage'];
88
+ }, summary?: StatsSummary, options?: {
89
+ title?: string;
90
+ }): string;
91
+ /**
92
+ * The lot summary report for several built maps, as a standalone HTML document.
93
+ * Wafers are grouped by lot identity inside; bin and test definitions are merged
94
+ * across the maps; each wafer is judged by its own pass bins. Lot-level ring
95
+ * figures use the first map's ring count, as the gallery does.
96
+ */
97
+ export declare function renderLotReportHtml(maps: readonly ReportMap[], options?: {
98
+ title?: string;
99
+ analyzeOptions?: AnalyzeWaferMapOptions;
100
+ }): string;
63
101
  //# sourceMappingURL=renderSummaryReport.d.ts.map