Summary
A repository-wide duplicate-code analysis was performed across Packages/MIES/*.ipf. It identified 15 clusters of near-duplicate functions, ranked by a Duplicate Risk Score (S × L × N × D × C, where S=similarity, L=normalized length, N=occurrences, D=module spread, C=complexity). This issue tracks consolidation of the highest-impact clusters.
The biggest risks are not lines of code, but divergence risk — when an algorithm is duplicated and a future bug fix is applied to only one copy.
Top duplicate clusters
| Rank |
Cluster |
File |
Score |
| 1 |
GetLBRowCache / GetLBIndexCache |
MIES_WaveDataFolderGetters.ipf (~2223–2382) |
5.52 |
| 2 |
55× GetXxx() / GetXxxAsString() DF getter pairs |
MIES_WaveDataFolderGetters.ipf |
2.61 |
| 3 |
GetLastSettingEach{RAC,SCI,TextRAC,TextSCI} |
MIES_MiesUtilities_Logbook.ipf (~1011–1272) |
2.59 |
| 4 |
SFO_Operation{Div,Minus,Mult,Plus}ImplDataSets |
MIES_SweepFormula_Operations.ipf (~1094–2063) |
2.51 |
| 5 |
SFO_Operation{RMS,Stdev,Variance,Max,Min} scaffolds |
MIES_SweepFormula_Operations.ipf (~1792–2527) |
2.23 |
| 6 |
GetLastSetting{RAC,SCI,TextRAC,TextSCI} |
MIES_MiesUtilities_Logbook.ipf (~893–1135) |
2.21 |
| 7 |
GetDA_EphysGuiState{Num,TxT} |
MIES_WaveDataFolderGetters.ipf (~6085–6225) |
1.77 |
| 8 |
GetLastSettingIndep{RAC,SCI,TextIndepRAC,TextIndepSCI} |
MIES_MiesUtilities_Logbook.ipf (~326–418) |
1.73 |
| 9 |
GetSweepSettingsWave / GetSweepSettingsTextWave |
MIES_WaveDataFolderGetters.ipf (~2457–2905) |
1.10 |
| 10 |
DC_Make{NI,SUTTER}ChannelWave |
MIES_DataConfigurator.ipf (~417–472) |
0.95 |
| 11 |
GetLastSettingIndep / GetLastSettingTextIndep |
MIES_MiesUtilities_Logbook.ipf (~279–325) |
0.53 |
| 12 |
SFO_Operation{Max,Min} entry points |
MIES_SweepFormula_Operations.ipf (~1792–1943) |
0.53 |
| 13 |
GetAnalysLB{NumericalValues,TextualValues,NumericalKeys,TextualKeys} |
MIES_WaveDataFolderGetters.ipf (~5875–5920) |
0.37 |
| 14 |
GetSweepFormula{X,Y} |
MIES_WaveDataFolderGetters.ipf (~7643–7673) |
0.35 |
| 15 |
EnableControls / DisableControls |
MIES_GuiUtilities.ipf (~81–116) |
0.23 |
Highest priority — divergence-risk fixes
🔴 1. GetLBRowCache / GetLBIndexCache (MIES_WaveDataFolderGetters.ipf)
~83 lines each, ~92% identical. Implements a wave-mod-count-aware labnotebook cache. Differs only in: cache-key helper (CA_CreateLBRowCacheKey vs CA_CreateLBIndexCacheKey), sentinel value (LABNOTEBOOK_GET_RANGE vs LABNOTEBOOK_UNCACHED_VALUE), and wave dimensionality.
Suggested refactor: Extract threadsafe static Function/WAVE GetLBCacheImpl(WAVE values, string key, variable sentinelValue, variable waveDim) and make the two public functions thin wrappers.
🔴 3 & 6 & 8 & 11. The GetLastSetting* family (MIES_MiesUtilities_Logbook.ipf)
Four interacting 2×2 grids ({RAC, SCI} × {numeric, textual}, plus Indep and Each variants — ~14 functions total). Loop bodies are byte-identical except for one AFH_GetSweepsFromSame{RACycle,SCI} call and the wave-type suffix (/D vs /T).
Suggested refactor: Two private impls parameterised by isRACycle / isText:
GetLastSettingScanImpl(...) (used by GetLastSetting{RAC,SCI,Text*})
GetLastSettingEachImpl(...) (used by the Each variants)
The public functions become 1–2 line wrappers, preserving the existing API.
🟠 4 & 5 & 12. SweepFormula operation duplication (MIES_SweepFormula_Operations.ipf)
SFO_Operation{Div,Minus,Mult,Plus}ImplDataSets: four ~27-line functions identical except for one MatrixOp operator. Suggested refactor: SFO_BinaryOpImplDataSets(WAVE/Z d0, WAVE/Z d1, string opSymbol).
SFO_Operation{RMS,Stdev,Variance,Max,Min}: five entry-point scaffolds plus their Impl functions sharing an 18-line dispatch/output template. Suggested refactor: A generic SFO_ColReduceEntry(...) plus Impl functions that only carry the unique MatrixOp line.
🟠 7 & 9 & 13 & 14. Numeric/text wave-getter pairs (MIES_WaveDataFolderGetters.ipf)
Pairs that differ only in /D vs /T, the default fill value (NaN vs ""), and one wave-name string. Suggested refactor: Private *Impl(..., variable isText) helpers.
🟠 10. DC_Make{NI,SUTTER}ChannelWave (MIES_DataConfigurator.ipf)
21-line clones differing only in time-unit scaling (MICRO_TO_MILLI / "ms" vs MICRO_TO_ONE / "s"). Suggested refactor: DC_MakeChannelWaveImpl(channel, samplingInterval, hwType) selecting unit by hardware type.
🟡 15. EnableControls / DisableControls (MIES_GuiUtilities.ipf)
Suggested refactor: SetControlsState(win, list, enable); keep existing two functions as one-line wrappers for source compatibility.
Out of scope / "document only"
Cluster 2 (55× GetXxx / GetXxxAsString pairs) is an intentional Igor Pro idiom for data-folder access. Eliminating it would require code generation and isn't recommended; this issue should not attempt to refactor it. We may instead want to add a comment block in MIES_WaveDataFolderGetters.ipf explaining the pattern so it isn't repeatedly flagged by future analyses.
Acceptance criteria
Notes
- Each cluster can be addressed in an independent PR; the items above are roughly ordered by risk reduction.
- The line numbers above were observed during analysis on the current
main and may drift slightly as the codebase evolves — verify against the current revision before refactoring.
Summary
A repository-wide duplicate-code analysis was performed across
Packages/MIES/*.ipf. It identified 15 clusters of near-duplicate functions, ranked by a Duplicate Risk Score (S × L × N × D × C, where S=similarity, L=normalized length, N=occurrences, D=module spread, C=complexity). This issue tracks consolidation of the highest-impact clusters.The biggest risks are not lines of code, but divergence risk — when an algorithm is duplicated and a future bug fix is applied to only one copy.
Top duplicate clusters
GetLBRowCache/GetLBIndexCacheMIES_WaveDataFolderGetters.ipf(~2223–2382)GetXxx()/GetXxxAsString()DF getter pairsMIES_WaveDataFolderGetters.ipfGetLastSettingEach{RAC,SCI,TextRAC,TextSCI}MIES_MiesUtilities_Logbook.ipf(~1011–1272)SFO_Operation{Div,Minus,Mult,Plus}ImplDataSetsMIES_SweepFormula_Operations.ipf(~1094–2063)SFO_Operation{RMS,Stdev,Variance,Max,Min}scaffoldsMIES_SweepFormula_Operations.ipf(~1792–2527)GetLastSetting{RAC,SCI,TextRAC,TextSCI}MIES_MiesUtilities_Logbook.ipf(~893–1135)GetDA_EphysGuiState{Num,TxT}MIES_WaveDataFolderGetters.ipf(~6085–6225)GetLastSettingIndep{RAC,SCI,TextIndepRAC,TextIndepSCI}MIES_MiesUtilities_Logbook.ipf(~326–418)GetSweepSettingsWave/GetSweepSettingsTextWaveMIES_WaveDataFolderGetters.ipf(~2457–2905)DC_Make{NI,SUTTER}ChannelWaveMIES_DataConfigurator.ipf(~417–472)GetLastSettingIndep/GetLastSettingTextIndepMIES_MiesUtilities_Logbook.ipf(~279–325)SFO_Operation{Max,Min}entry pointsMIES_SweepFormula_Operations.ipf(~1792–1943)GetAnalysLB{NumericalValues,TextualValues,NumericalKeys,TextualKeys}MIES_WaveDataFolderGetters.ipf(~5875–5920)GetSweepFormula{X,Y}MIES_WaveDataFolderGetters.ipf(~7643–7673)EnableControls/DisableControlsMIES_GuiUtilities.ipf(~81–116)Highest priority — divergence-risk fixes
🔴 1.
GetLBRowCache/GetLBIndexCache(MIES_WaveDataFolderGetters.ipf)~83 lines each, ~92% identical. Implements a wave-mod-count-aware labnotebook cache. Differs only in: cache-key helper (
CA_CreateLBRowCacheKeyvsCA_CreateLBIndexCacheKey), sentinel value (LABNOTEBOOK_GET_RANGEvsLABNOTEBOOK_UNCACHED_VALUE), and wave dimensionality.Suggested refactor: Extract
threadsafe static Function/WAVE GetLBCacheImpl(WAVE values, string key, variable sentinelValue, variable waveDim)and make the two public functions thin wrappers.🔴 3 & 6 & 8 & 11. The
GetLastSetting*family (MIES_MiesUtilities_Logbook.ipf)Four interacting 2×2 grids (
{RAC, SCI} × {numeric, textual}, plusIndepandEachvariants — ~14 functions total). Loop bodies are byte-identical except for oneAFH_GetSweepsFromSame{RACycle,SCI}call and the wave-type suffix (/Dvs/T).Suggested refactor: Two private impls parameterised by
isRACycle/isText:GetLastSettingScanImpl(...)(used byGetLastSetting{RAC,SCI,Text*})GetLastSettingEachImpl(...)(used by theEachvariants)The public functions become 1–2 line wrappers, preserving the existing API.
🟠 4 & 5 & 12. SweepFormula operation duplication (
MIES_SweepFormula_Operations.ipf)SFO_Operation{Div,Minus,Mult,Plus}ImplDataSets: four ~27-line functions identical except for oneMatrixOpoperator. Suggested refactor:SFO_BinaryOpImplDataSets(WAVE/Z d0, WAVE/Z d1, string opSymbol).SFO_Operation{RMS,Stdev,Variance,Max,Min}: five entry-point scaffolds plus theirImplfunctions sharing an 18-line dispatch/output template. Suggested refactor: A genericSFO_ColReduceEntry(...)plusImplfunctions that only carry the uniqueMatrixOpline.🟠 7 & 9 & 13 & 14. Numeric/text wave-getter pairs (
MIES_WaveDataFolderGetters.ipf)Pairs that differ only in
/Dvs/T, the default fill value (NaNvs""), and one wave-name string. Suggested refactor: Private*Impl(..., variable isText)helpers.🟠 10.
DC_Make{NI,SUTTER}ChannelWave(MIES_DataConfigurator.ipf)21-line clones differing only in time-unit scaling (
MICRO_TO_MILLI/"ms"vsMICRO_TO_ONE/"s"). Suggested refactor:DC_MakeChannelWaveImpl(channel, samplingInterval, hwType)selecting unit by hardware type.🟡 15.
EnableControls/DisableControls(MIES_GuiUtilities.ipf)Suggested refactor:
SetControlsState(win, list, enable); keep existing two functions as one-line wrappers for source compatibility.Out of scope / "document only"
Cluster 2 (55×
GetXxx/GetXxxAsStringpairs) is an intentional Igor Pro idiom for data-folder access. Eliminating it would require code generation and isn't recommended; this issue should not attempt to refactor it. We may instead want to add a comment block inMIES_WaveDataFolderGetters.ipfexplaining the pattern so it isn't repeatedly flagged by future analyses.Acceptance criteria
GetLBCacheImpl); existing tests pass.GetLastSetting*Implhelpers; logbook tests pass.MIES_SweepFormula_Operations.ipf; SweepFormula tests pass.WaveLocationModplumbing.MIES_DataConfigurator.ipf; NI and SUTTER acquisition tests pass.MIES_GuiUtilities.ipf.Notes
mainand may drift slightly as the codebase evolves — verify against the current revision before refactoring.