Skip to content

Commit

Permalink
Merge pull request #2103 from AllenInstitute/feature/2103-extend_find…
Browse files Browse the repository at this point in the history
…indices_with_not

Utils: Adapt FindIndizes with new property PROP_NOT to invert the matching
  • Loading branch information
t-b authored May 7, 2024
2 parents ead19ee + cf8dbdd commit 4d399b8
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 81 deletions.
11 changes: 5 additions & 6 deletions Packages/MIES/MIES_Constants.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -305,12 +305,11 @@ StrConstant NOTE_INDEX = "Index"
///@name Parameters for FindIndizes
///@anchor FindIndizesProps
///@{
Constant PROP_NON_EMPTY = 0x01 ///< Wave entry is not NaN or ""
Constant PROP_EMPTY = 0x02 ///< Wave entry is NaN or ""
Constant PROP_MATCHES_VAR_BIT_MASK = 0x04 ///< Wave entry matches the bitmask given in var
Constant PROP_NOT_MATCHES_VAR_BIT_MASK = 0x08 ///< Wave entry does not match the bitmask given in var
Constant PROP_GREP = 0x10 ///< Wave entry matches the regular expression given in str
Constant PROP_WILDCARD = 0x20 ///< Wave entry matches the wildcard expression given in str
Constant PROP_NOT = 0x01 ///< Inverts the matching
Constant PROP_EMPTY = 0x02 ///< Wave entry is NaN or ""
Constant PROP_MATCHES_VAR_BIT_MASK = 0x04 ///< Wave entry matches the bitmask given in var
Constant PROP_GREP = 0x08 ///< Wave entry matches the regular expression given in str
Constant PROP_WILDCARD = 0x10 ///< Wave entry matches the wildcard expression given in str
///@}

/// @name Parameters for GetPanelControl and IDX_GetSetsInRange, GetSetFolder, GetSetParamFolder and GetChanneListFromITCConfig
Expand Down
2 changes: 1 addition & 1 deletion Packages/MIES/MIES_Debugging.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ threadsafe static Function/S CheckDimensionLabels(WAVE/Z wv)
Multithread results[] = CheckDimensionLabels(waveRef[p])

if(HasOneValidEntry(results))
WAVE/Z indizes = FindIndizes(results, prop = PROP_NON_EMPTY)
WAVE/Z indizes = FindIndizes(results, prop = PROP_EMPTY | PROP_NOT)
numMatches = WaveExists(indizes) ? DimSize(indizes, ROWS) : 0

for(i = 0; i < numMatches; i += 1)
Expand Down
2 changes: 1 addition & 1 deletion Packages/MIES/MIES_Labnotebook.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ End
/// at least once, an empty string otherwise
threadsafe static Function/S LBV_IsLabnotebookColumnFilled(WAVE values, variable col)

WAVE/Z indizes = FindIndizes(values, col = col, prop = PROP_NON_EMPTY, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)
WAVE/Z indizes = FindIndizes(values, col = col, prop = PROP_EMPTY | PROP_NOT, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)

if(WaveExists(indizes))
return GetDimLabel(values, COLS, col)
Expand Down
2 changes: 1 addition & 1 deletion Packages/MIES/MIES_LogbookViewer.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ static Function LBV_AddTraceToLBGraphTPStorage(string graph, DFREF dfr, string k
// ignore completely empty headstages
if(!legacyActiveADColumns)
searchLayer = FindDimLabel(TPStorage, LAYERS, "Headstage")
WAVE/Z indizes = FindIndizes(TPStorage, col = j, prop = PROP_NON_EMPTY, startLayer = searchLayer, endLayer = searchLayer)
WAVE/Z indizes = FindIndizes(TPStorage, col = j, prop = PROP_EMPTY | PROP_NOT, startLayer = searchLayer, endLayer = searchLayer)

if(!WaveExists(indizes))
continue
Expand Down
142 changes: 87 additions & 55 deletions Packages/MIES/MIES_MiesUtilities.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ threadsafe static Function FindRange(wv, col, val, entrySourceType, first, last)

// "TP Peak Resistance" introduced in 666d761a (TP documenting is implemented using David Reid's documenting functions, 2014-07-28)
if(FindDimLabel(wv, COLS, "TP Peak Resistance") >= 0)
WAVE/Z indizesDefinitlyTP = FindIndizes(wv, colLabel = "TP Peak Resistance", prop = PROP_NON_EMPTY, startRow = firstRow, endRow = lastRow, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)
WAVE/Z indizesDefinitlyTP = FindIndizes(wv, colLabel = "TP Peak Resistance", prop = PROP_EMPTY | PROP_NOT, startRow = firstRow, endRow = lastRow, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)
if(WaveExists(indizesDefinitlyTP) && WaveExists(indizesSetting))
WAVE/Z indizesSettingRemoved = GetSetDifference(indizesSetting, indizesDefinitlyTP)
WAVE/Z indizesSetting = indizesSettingRemoved
Expand All @@ -586,7 +586,7 @@ threadsafe static Function FindRange(wv, col, val, entrySourceType, first, last)

// "TP Baseline Fraction" introduced in 4f4649a2 (Document the testpulse settings in the labnotebook, 2015-07-28)
if(FindDimLabel(wv, COLS, "TP Baseline Fraction") >= 0)
WAVE/Z indizesDefinitlyTP = FindIndizes(wv, colLabel = "TP Baseline Fraction", prop = PROP_NON_EMPTY, startRow = firstRow, endRow = lastRow, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)
WAVE/Z indizesDefinitlyTP = FindIndizes(wv, colLabel = "TP Baseline Fraction", prop = PROP_EMPTY | PROP_NOT, startRow = firstRow, endRow = lastRow, startLayer = 0, endLayer = LABNOTEBOOK_LAYER_COUNT - 1)
if(WaveExists(indizesDefinitlyTP) && WaveExists(indizesSetting))
WAVE/Z indizesSettingRemoved = GetSetDifference(indizesSetting, indizesDefinitlyTP)
WAVE/Z indizesSetting = indizesSettingRemoved
Expand Down Expand Up @@ -1794,7 +1794,7 @@ threadsafe Function/WAVE GetNonEmptyLBNRows(labnotebookValues, setting)
return $""
endif

return FindIndizes(labnotebookValues, col = col, prop = PROP_NON_EMPTY, \
return FindIndizes(labnotebookValues, col = col, prop = PROP_EMPTY | PROP_NOT, \
startLayer = 0, endLayer = DimSize(labnotebookValues, LAYERS) - 1)
End

Expand Down Expand Up @@ -2800,7 +2800,7 @@ static Function [string oodDAQRegionsAll, variable totalXRange] GetOodDAQFullRan
// Fixup buggy entries introduced since 88323d8d (Replacement of oodDAQ offset calculation routines, 2019-06-13)
// The regions from the second active headstage are duplicated into the
// first region in case we had more than two active headstages taking part in oodDAQ.
WAVE/Z indizes = FindIndizes(oodDAQRegions, prop = PROP_NON_EMPTY)
WAVE/Z indizes = FindIndizes(oodDAQRegions, prop = PROP_EMPTY | PROP_NOT)
if(WaveExists(indizes) && DimSize(indizes, ROWS) > 2)
oodDAQRegions[indizes[0]] = ReplaceString(oodDAQRegions[indizes[1]], oodDAQRegions[indizes[0]], "")
endif
Expand Down Expand Up @@ -7182,10 +7182,12 @@ End
/// result contains matches from all layers, and this also means the resulting wave is still 1D.
///
/// Exactly one of `var`/`str`/`prop` has to be given except for
/// `prop == PROP_MATCHES_VAR_BIT_MASK` and `prop == PROP_NOT_MATCHES_VAR_BIT_MASK`
/// `prop == PROP_MATCHES_VAR_BIT_MASK`
/// which requires a `var`/`str` parameter as well.
/// `prop == PROP_GREP` requires `str`.
/// `prop == PROP_WILDCARD` requires `str`.
/// `PROP_NOT` can be set by logical ORing it to one of the other PROP_* constants
/// `prop == PROP_NOT` can also be set solely to invert the matching of the default behavior
///
/// Exactly one of `col`/`colLabel` has to be given.
///
Expand All @@ -7209,16 +7211,17 @@ threadsafe Function/WAVE FindIndizes(numericOrTextWave, [col, colLabel, var, str
variable startRow, endRow
variable startLayer, endLayer

variable numCols, numRows, numLayers
variable numCols, numRows, numLayers, maskedProp
string key

ASSERT_TS(ParamIsDefault(prop) + ParamIsDefault(var) + ParamIsDefault(str) == 2 \
|| (!ParamIsDefault(prop) \
&& (((prop == PROP_MATCHES_VAR_BIT_MASK || prop == PROP_NOT_MATCHES_VAR_BIT_MASK) \
&& (ParamIsDefault(var) + ParamIsDefault(str)) == 1) \
|| (prop == PROP_GREP && !ParamIsDefault(str) && ParamIsDefault(var)) \
|| (prop == PROP_WILDCARD && !ParamIsDefault(str) && ParamIsDefault(var)) \
)), \
ASSERT_TS(ParamIsDefault(prop) + ParamIsDefault(var) + ParamIsDefault(str) == 2 \
|| (!ParamIsDefault(prop) \
&& ( \
prop == PROP_NOT \
|| ((prop & PROP_MATCHES_VAR_BIT_MASK) && (ParamIsDefault(var) + ParamIsDefault(str)) == 1) \
|| (prop & PROP_GREP && !ParamIsDefault(str) && ParamIsDefault(var)) \
|| (prop & PROP_WILDCARD && !ParamIsDefault(str) && ParamIsDefault(var)) \
)), \
"Invalid combination of var/str/prop arguments")

ASSERT_TS(WaveExists(numericOrTextWave), "numericOrTextWave does not exist")
Expand Down Expand Up @@ -7253,22 +7256,23 @@ threadsafe Function/WAVE FindIndizes(numericOrTextWave, [col, colLabel, var, str
endif

if(!ParamIsDefault(prop))
ASSERT_TS(prop == PROP_NON_EMPTY \
|| prop == PROP_EMPTY \
|| prop == PROP_MATCHES_VAR_BIT_MASK \
|| prop == PROP_NOT_MATCHES_VAR_BIT_MASK \
|| prop == PROP_GREP \
|| prop == PROP_WILDCARD, \
"Invalid property")

if(prop == PROP_MATCHES_VAR_BIT_MASK || prop == PROP_NOT_MATCHES_VAR_BIT_MASK)
if(ParamIsDefault(var))
var = str2numSafe(str)
elseif(ParamIsDefault(str))
str = num2str(var)
maskedProp = prop & (PROP_NOT %^ -1)
if(maskedProp)
ASSERT_TS(maskedProp == PROP_EMPTY \
|| maskedProp == PROP_MATCHES_VAR_BIT_MASK \
|| maskedProp == PROP_GREP \
|| maskedProp == PROP_WILDCARD, \
"Invalid property")

if(prop & PROP_MATCHES_VAR_BIT_MASK)
if(ParamIsDefault(var))
var = str2numSafe(str)
elseif(ParamIsDefault(str))
str = num2str(var)
endif
elseif(prop & PROP_GREP)
ASSERT_TS(IsValidRegexp(str), "Invalid regular expression")
endif
elseif(prop == PROP_GREP)
ASSERT_TS(IsValidRegexp(str), "Invalid regular expression")
endif
elseif(!ParamIsDefault(var))
str = num2str(var)
Expand Down Expand Up @@ -7326,40 +7330,68 @@ threadsafe Function/WAVE FindIndizes(numericOrTextWave, [col, colLabel, var, str

if(WaveExists(wv))
if(!ParamIsDefault(prop))
if(prop == PROP_EMPTY)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (numtype(wv[p][col][q]) == 2 ? p : -1)
elseif(prop == PROP_NON_EMPTY)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (numtype(wv[p][col][q]) != 2 ? p : -1)
elseif(prop == PROP_MATCHES_VAR_BIT_MASK)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (wv[p][col][q] & var ? p : -1)
elseif(prop == PROP_NOT_MATCHES_VAR_BIT_MASK)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (!(wv[p][col][q] & var) ? p : -1)
elseif(prop == PROP_GREP)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (GrepString(num2strHighPrec(wv[p][col][q]), str) ? p : -1)
elseif(prop == PROP_WILDCARD)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (StringMatch(num2strHighPrec(wv[p][col][q]), str) ? p : -1)
if(prop & PROP_EMPTY)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = numtype(wv[p][col][q]) != 2 ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = numtype(wv[p][col][q]) == 2 ? p : -1
endif
elseif(prop & PROP_MATCHES_VAR_BIT_MASK)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !(wv[p][col][q] & var) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = wv[p][col][q] & var ? p : -1
endif
elseif(prop & PROP_GREP)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !GrepString(num2strHighPrec(wv[p][col][q]), str) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = GrepString(num2strHighPrec(wv[p][col][q]), str) ? p : -1
endif
elseif(prop & PROP_WILDCARD)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !StringMatch(num2strHighPrec(wv[p][col][q]), str) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = StringMatch(num2strHighPrec(wv[p][col][q]), str) ? p : -1
endif
elseif(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (wv[p][col][q] != var) ? p : -1
endif
else
ASSERT_TS(!IsNaN(var), "Use PROP_EMPTY to search for NaN")
MultiThread matches[startRow, endRow][startLayer, endLayer] = ((wv[p][col][q] == var) ? p : -1)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (wv[p][col][q] == var) ? p : -1
endif
else
if(!ParamIsDefault(prop))
if(prop == PROP_EMPTY)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (!cmpstr(wvText[p][col][q], "") ? p : -1)
elseif(prop == PROP_NON_EMPTY)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (cmpstr(wvText[p][col][q], "") ? p : -1)
elseif(prop == PROP_MATCHES_VAR_BIT_MASK)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (str2num(wvText[p][col][q]) & var ? p : -1)
elseif(prop == PROP_NOT_MATCHES_VAR_BIT_MASK)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (!(str2num(wvText[p][col][q]) & var) ? p : -1)
elseif(prop == PROP_GREP)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (GrepString(wvText[p][col][q], str) ? p : -1)
elseif(prop == PROP_WILDCARD)
MultiThread matches[startRow, endRow][startLayer, endLayer] = (StringMatch(wvText[p][col][q], str) ? p : -1)
if(prop & PROP_EMPTY)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = strlen(wvText[p][col][q]) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = !strlen(wvText[p][col][q]) ? p : -1
endif
elseif(prop & PROP_MATCHES_VAR_BIT_MASK)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !(str2num(wvText[p][col][q]) & var) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = str2num(wvText[p][col][q]) & var ? p : -1
endif
elseif(prop & PROP_GREP)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !GrepString(wvText[p][col][q], str) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = GrepString(wvText[p][col][q], str) ? p : -1
endif
elseif(prop & PROP_WILDCARD)
if(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !StringMatch(wvText[p][col][q], str) ? p : -1
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = StringMatch(wvText[p][col][q], str) ? p : -1
endif
elseif(prop & PROP_NOT)
MultiThread matches[startRow, endRow][startLayer, endLayer] = CmpStr(wvText[p][col][q], str) ? p : -1
endif
else
MultiThread matches[startRow, endRow][startLayer, endLayer] = (!cmpstr(wvText[p][col][q], str) ? p : -1)
MultiThread matches[startRow, endRow][startLayer, endLayer] = !CmpStr(wvText[p][col][q], str) ? p : -1
endif
endif

Expand All @@ -7386,7 +7418,7 @@ Function/S GetLastNonEmptyEntry(wv, colLabel, endRow)
string colLabel
variable endRow

WAVE/Z indizes = FindIndizes(wv, colLabel = colLabel, prop = PROP_NON_EMPTY, endRow = endRow)
WAVE/Z indizes = FindIndizes(wv, colLabel = colLabel, prop = PROP_EMPTY | PROP_NOT, endRow = endRow)

if(!WaveExists(indizes))
return ""
Expand Down
2 changes: 1 addition & 1 deletion Packages/MIES/MIES_NeuroDataWithoutBorders.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ threadsafe static Function NWB_AppendSweepLowLevel(STRUCT NWBAsyncParameters &s)
if(!WaveExists(electrodeNames))
Make/FREE/T/N=(NUM_HEADSTAGES) electrodeNames = GetDefaultElectrodeName(p)
else
WAVE/Z nonEmptyElectrodes = FindIndizes(electrodeNames, prop = PROP_NON_EMPTY)
WAVE/Z nonEmptyElectrodes = FindIndizes(electrodeNames, prop = PROP_EMPTY | PROP_NOT)
if(!WaveExists(nonEmptyElectrodes)) // all are empty
electrodeNames[] = GetDefaultElectrodeName(p)
endif
Expand Down
27 changes: 21 additions & 6 deletions Packages/MIES/MIES_Utilities.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -5467,16 +5467,31 @@ End
/// @brief Turn a list of entries into a regular expression with alternations.
///
/// Can be used for GetListOfObjects() if you know in advance which entries to filter out.
Function/S ConvertListToRegexpWithAlternations(list)
string list
///
/// @param list semicolon separated list of strings to match
/// @param literal [optional, default = 1] when this flag is cleared the string elements of the list are treated as regular expressions
/// @param sep [optional, default = ";"] separator for list
Function/S ConvertListToRegexpWithAlternations(string list, [variable literal, string sep])

variable i, numEntries
string entry
string regexpList = ""
string regexpList = ""
string literalPrefix = "\\Q"
string literalSuffix = "\\E"

numEntries = ItemsInList(list)
literal = ParamIsDefault(literal) ? 1 : !!literal
if(ParamIsDefault(sep))
sep = ";"
else
ASSERT(!IsEmpty(sep), "separator can not be empty.")
endif

if(!literal)
literalPrefix = ""
literalSuffix = ""
endif
numEntries = ItemsInList(list, sep)
for(i = 0; i < numEntries; i += 1)
regexpList = AddListItem("\\Q" + StringFromList(i, list) + "\\E", regexpList, "|", Inf)
regexpList = AddListItem(literalPrefix + StringFromList(i, list, sep) + literalSuffix, regexpList, "|", Inf)
endfor

regexpList = "(?:" + RemoveEnding(regexpList, "|") + ")"
Expand Down
Loading

0 comments on commit 4d399b8

Please sign in to comment.