diff --git a/.vs/IsncsciAlgorithm/DesignTimeBuild/.dtbcache.v2 b/.vs/IsncsciAlgorithm/DesignTimeBuild/.dtbcache.v2
new file mode 100644
index 0000000..b4a98f4
Binary files /dev/null and b/.vs/IsncsciAlgorithm/DesignTimeBuild/.dtbcache.v2 differ
diff --git a/IsncsciAlgorithm.sln b/IsncsciAlgorithm.sln
index 3512b11..d481236 100644
--- a/IsncsciAlgorithm.sln
+++ b/IsncsciAlgorithm.sln
@@ -1,25 +1,15 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30110.0
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rhi.Isncsci", "Rhi.Isncsci\Rhi.Isncsci.csproj", "{CFD110C1-CE77-40FD-932B-3EF70F8F56EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rhi.Isncsci.Tests", "Rhi.Isncsci.Tests\Rhi.Isncsci.Tests.csproj", "{A354D889-1BE4-4439-9FD9-E4DB9E470B7D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rhi.Isncsci.Core", "Rhi.Isncsci.Core\Rhi.Isncsci.Core.csproj", "{98729432-48E7-424C-9A0D-3487A4F16B90}"
+EndProject
Global
- GlobalSection(TeamFoundationVersionControl) = preSolution
- SccNumberOfProjects = 3
- SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
- SccTeamFoundationServer = https://eddiemachete.visualstudio.com/defaultcollection
- SccLocalPath0 = .
- SccProjectUniqueName1 = Rhi.Isncsci\\Rhi.Isncsci.csproj
- SccProjectName1 = Rhi.Isncsci
- SccLocalPath1 = Rhi.Isncsci
- SccProjectUniqueName2 = Rhi.Isncsci.Tests\\Rhi.Isncsci.Tests.csproj
- SccProjectName2 = Rhi.Isncsci.Tests
- SccLocalPath2 = Rhi.Isncsci.Tests
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
@@ -33,8 +23,15 @@ Global
{A354D889-1BE4-4439-9FD9-E4DB9E470B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A354D889-1BE4-4439-9FD9-E4DB9E470B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A354D889-1BE4-4439-9FD9-E4DB9E470B7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {98729432-48E7-424C-9A0D-3487A4F16B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {98729432-48E7-424C-9A0D-3487A4F16B90}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {98729432-48E7-424C-9A0D-3487A4F16B90}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {98729432-48E7-424C-9A0D-3487A4F16B90}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {80A3FAA5-1BCF-4618-9B21-FFE1B06C7D7D}
+ EndGlobalSection
EndGlobal
diff --git a/Rhi.Isncsci.Core/Algorithm.cs b/Rhi.Isncsci.Core/Algorithm.cs
new file mode 100644
index 0000000..7205db7
--- /dev/null
+++ b/Rhi.Isncsci.Core/Algorithm.cs
@@ -0,0 +1,762 @@
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace Rhi.Isncsci.Core
+{
+ public sealed class Algorithm
+ {
+ private static readonly Regex NtRegex = new Regex(@"\bNT\b", RegexOptions.IgnoreCase);
+ private const string NotDeterminable = "UTD";
+ private const string NotApplicable = "NA";
+ private const string Intact = "INT";
+
+ ///
+ /// Produces a summarized version of a NeurologyFormTotals object which can be used to be displayed in an interface or be stored in a database.
+ /// The values in the summary return will have ranges instead of lists of values.
+ ///
+ /// Neurology form that has been populated with the values to be used in the algorithm calculations.
+ ///
+ /// Summarized version of the totals which presents the results as ranges, rather than lists containing every possible value for every field.
+ ///
+ public static NeurologyFormTotalsSummary GetTotalsSummaryFor(NeurologyForm neurologyForm)
+ {
+ return GetTotalsSummaryFor(GetTotalsFor(neurologyForm));
+ }
+
+ ///
+ /// Produces a summarized version of a NeurologyFormTotals object which can be used to be displayed in an interface or be stored in a database.
+ /// The values in the summary return will have ranges instead of lists of values.
+ ///
+ /// The form totals object to be used to generate the summary.
+ ///
+ /// Summarized version of the totals where the enumerations get replaced by ranges.
+ ///
+ public static NeurologyFormTotalsSummary GetTotalsSummaryFor(NeurologyFormTotals totals)
+ {
+ var ais = totals.GetAsiaImpairmentScaleValues();
+ var isAsiaA = ais.Contains("A");
+ var couldBeOtherThanAsiaA = !isAsiaA || ais.Length > 1;
+
+ var summary = new NeurologyFormTotalsSummary
+ {
+ AsiaImpairmentScale = ais,
+ Completeness = isAsiaA ? (couldBeOtherThanAsiaA ? "C,I" : "C") : "I",
+ LeftLowerMotorTotal = GetSummaryStringFor(totals.LeftLowerMotorTotal, totals.LeftLowerMotorTotalHasImpairmentNotDueToSci, totals.LeftLowerMotorContainsNt),
+ LeftMotor = GetLevelsRange(totals.GetLeftMotorValues(), false),
+ LeftMotorZpp = isAsiaA ? GetLevelsRange(totals.GetLeftMotorZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
+ LeftMotorTotal = GetSummaryStringFor(totals.LeftUpperMotorTotal + totals.LeftLowerMotorTotal,
+ totals.LeftUpperMotorTotalHasImpairmentNotDueToSci || totals.LeftLowerMotorTotalHasImpairmentNotDueToSci,
+ totals.LeftUpperMotorContainsNt || totals.LeftLowerMotorContainsNt),
+ LeftPrickTotal = GetSummaryStringFor(totals.LeftPrickTotal, totals.LeftPrickTotalHasImpairmentNotDueToSci, totals.LeftPrickContainsNt),
+ LeftSensory = GetLevelsRange(totals.GetLeftSensoryValues(), false),
+ LeftSensoryZpp = isAsiaA ? GetLevelsRange(totals.GetLeftSensoryZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
+ LeftTouchTotal = GetSummaryStringFor(totals.LeftTouchTotal, totals.LeftTouchTotalHasImpairmentNotDueToSci, totals.LeftTouchContainsNt),
+ LeftUpperMotorTotal = GetSummaryStringFor(totals.LeftUpperMotorTotal, totals.LeftUpperMotorTotalHasImpairmentNotDueToSci, totals.LeftUpperMotorContainsNt),
+ LowerMotorTotal = GetSummaryStringFor(totals.LowerMotorTotal,
+ totals.RightLowerMotorTotalHasImpairmentNotDueToSci || totals.LeftLowerMotorTotalHasImpairmentNotDueToSci,
+ totals.RightLowerMotorContainsNt || totals.LeftLowerMotorContainsNt),
+ NeurologicalLevelOfInjury = GetLevelsRange(totals.GetNeurologicalLevelsOfInjury(), false),
+ PrickTotal = GetSummaryStringFor(totals.RightPrickTotal + totals.LeftPrickTotal,
+ totals.RightPrickTotalHasImpairmentNotDueToSci || totals.LeftPrickTotalHasImpairmentNotDueToSci,
+ totals.RightPrickContainsNt || totals.LeftPrickContainsNt),
+ RightLowerMotorTotal = GetSummaryStringFor(totals.RightLowerMotorTotal, totals.RightLowerMotorTotalHasImpairmentNotDueToSci, totals.RightLowerMotorContainsNt),
+ RightMotor = GetLevelsRange(totals.GetRightMotorValues(), false),
+ RightMotorZpp = isAsiaA ? GetLevelsRange(totals.GetRightMotorZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
+ RightMotorTotal = GetSummaryStringFor(totals.RightUpperMotorTotal + totals.RightLowerMotorTotal,
+ totals.RightUpperMotorTotalHasImpairmentNotDueToSci || totals.RightLowerMotorTotalHasImpairmentNotDueToSci,
+ totals.RightUpperMotorContainsNt || totals.RightLowerMotorContainsNt),
+ RightPrickTotal = GetSummaryStringFor(totals.RightPrickTotal, totals.RightPrickTotalHasImpairmentNotDueToSci, totals.RightPrickContainsNt),
+ RightSensory = GetLevelsRange(totals.GetRightSensoryValues(), false),
+ RightSensoryZpp = isAsiaA ? GetLevelsRange(totals.GetRightSensoryZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
+ RightTouchTotal = GetSummaryStringFor(totals.RightTouchTotal, totals.RightTouchTotalHasImpairmentNotDueToSci, totals.RightTouchContainsNt),
+ RightUpperMotorTotal = GetSummaryStringFor(totals.RightUpperMotorTotal, totals.RightUpperMotorTotalHasImpairmentNotDueToSci, totals.RightUpperMotorContainsNt),
+ TouchTotal = GetSummaryStringFor(totals.RightTouchTotal + totals.LeftTouchTotal,
+ totals.RightTouchTotalHasImpairmentNotDueToSci || totals.LeftTouchTotalHasImpairmentNotDueToSci,
+ totals.RightTouchContainsNt || totals.LeftTouchContainsNt),
+ UpperMotorTotal = GetSummaryStringFor(totals.UpperMotorTotal,
+ totals.RightUpperMotorTotalHasImpairmentNotDueToSci || totals.LeftUpperMotorTotalHasImpairmentNotDueToSci,
+ totals.RightUpperMotorContainsNt || totals.LeftUpperMotorContainsNt)
+ };
+
+ return summary;
+ }
+
+ ///
+ /// Returns the results produced by the ISNCSCI Algorithm ir a raw values format.
+ ///
+ /// Neurology form that has been populated with the values to be used in the algorithm calculations.
+ ///
+ /// Totals in raw values format. The results contain lists with every prossible value for each field.
+ /// You can use the resulting object to obtained a summarized version, which uses ranges, by passing the result to the method GetTotalsSummaryFor
+ ///
+ public static NeurologyFormTotals GetTotalsFor(NeurologyForm neurologyForm)
+ {
+ var totals = new NeurologyFormTotals();
+
+ UpdateTotalsWithLevelAt(neurologyForm, totals, neurologyForm.GetLevelWithName("C2"), false, false);
+
+ totals.UpperMotorTotal = totals.RightUpperMotorTotal + totals.LeftUpperMotorTotal;
+ totals.LowerMotorTotal = totals.RightLowerMotorTotal + totals.LeftLowerMotorTotal;
+ totals.TouchTotal = totals.RightTouchTotal + totals.LeftTouchTotal;
+ totals.PrickTotal = totals.RightPrickTotal + totals.LeftPrickTotal;
+
+ var s4_5 = neurologyForm.GetLevelWithName("S4_5");
+
+ var c1 = neurologyForm.GetLevelWithName("C2").Previous;
+
+ if (totals.RightSensoryZppHasOnlySoftValues)
+ totals.AddRightSensoryZppValue(c1);
+
+ if (totals.LeftSensoryZppHasOnlySoftValues)
+ totals.AddLeftSensoryZppValue(c1);
+
+ if (totals.RightMotorZppHasOnlySoftValues)
+ totals.AddRightMotorZppValue(c1);
+
+ if (totals.LeftMotorZppHasOnlySoftValues)
+ totals.AddLeftMotorZppValue(c1);
+
+ if (totals.MostRostralRightLevelWithMotorFunction == null)
+ totals.MostRostralRightLevelWithMotorFunction = c1;
+
+ if (totals.MostRostralLeftLevelWithMotorFunction == null)
+ totals.MostRostralLeftLevelWithMotorFunction = c1;
+
+ if (totals.MostCaudalRightLevelWithMotorFunction == null)
+ totals.MostCaudalRightLevelWithMotorFunction = c1;
+
+ if (totals.MostCaudalLeftLevelWithMotorFunction == null)
+ totals.MostCaudalLeftLevelWithMotorFunction = c1;
+
+ // [ASIA learning center 2012-11-14] Sensory incomplete: Sacral sparing of sensory function
+ var isSensoryIncomplete = neurologyForm.AnalSensation == BinaryObservation.Yes || neurologyForm.AnalSensation == BinaryObservation.NT
+ || !"0".Equals(s4_5.RightTouchName) || !"0".Equals(s4_5.LeftTouchName)
+ || !"0".Equals(s4_5.RightPrickName) || !"0".Equals(s4_5.LeftPrickName);
+
+ var couldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel = CouldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel(neurologyForm, totals);
+
+ var couldNotBeMotorIncomplete = (neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT)
+ && isSensoryIncomplete && couldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel;
+
+ if ((neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT)
+ && (neurologyForm.AnalSensation == BinaryObservation.No || neurologyForm.AnalSensation == BinaryObservation.NT)
+ && s4_5.RightTouchValue == 0 && !s4_5.RightTouchImpairmentNotDueToSci
+ && s4_5.RightPrickValue == 0 && !s4_5.RightPrickImpairmentNotDueToSci
+ && s4_5.LeftTouchValue == 0 && !s4_5.LeftTouchImpairmentNotDueToSci
+ && s4_5.LeftPrickValue == 0 && !s4_5.LeftPrickImpairmentNotDueToSci)
+ totals.AddAsiaImpairmentScaleValue("A");
+
+ if (couldNotBeMotorIncomplete && totals.MostRostralNeurologicalLevelOfInjury.Name != "S4_5")
+ totals.AddAsiaImpairmentScaleValue("B");
+
+
+ //// Not an ASIA E only
+ //// AND not an ASIA A only
+ if (totals.MostRostralNeurologicalLevelOfInjury.Name != "S4_5"
+ && (isSensoryIncomplete || neurologyForm.AnalContraction == BinaryObservation.Yes || neurologyForm.AnalContraction == BinaryObservation.NT))
+ {
+ CouldBeAsiaCorD(neurologyForm, totals, out bool couldBeAsiaC, out bool couldBeAsiaD);
+
+ if (couldBeAsiaC)
+ totals.AddAsiaImpairmentScaleValue("C");
+
+ if (couldBeAsiaD)
+ totals.AddAsiaImpairmentScaleValue("D");
+ }
+
+ if (totals.RightSensoryContains("S4_5") && totals.LeftSensoryContains("S4_5")
+ && totals.RightMotorContains("S4_5") && totals.LeftMotorContains("S4_5"))
+ totals.AddAsiaImpairmentScaleValue("E");
+
+ return totals;
+ }
+
+ ///
+ /// Formats a total value depending on the specified flags.
+ ///
+ /// Raw total value.
+ /// Flag indicating if any value used in the calculation of this total presents impairment not due to a spinal cord injury.
+ /// Flag indicating if any value used in the calculation of this total is set to Not Testable.
+ ///
+ /// The value, followed by an exclamation mark if the hasImpairmentNotDueToSci is set to true
+ /// or
+ /// UTD (Unable to determine) if the containsNt flag is set to true.
+ ///
+ private static string GetSummaryStringFor(int total, bool hasImpairmentNotDueToSci, bool containsNt)
+ {
+ if (containsNt)
+ return NotDeterminable;
+
+ return string.Format("{0}{1}", total, hasImpairmentNotDueToSci ? "!" : string.Empty);
+ }
+
+ ///
+ /// Returns a range string based on the specified list.
+ ///
+ /// Neurological levels returned in a calculation.
+ /// Used in the Zone of Partial Preservation to indicate that one of the possible Asia Impairment Scale values is "A"
+ /// A representation of the list as a range string. E.g. NA,C4-C6,T1,T2
+ private static string GetLevelsRange(List levels, bool addNa)
+ {
+ if (levels.Count == 0)
+ return addNa ? NotApplicable : string.Empty;
+
+ levels.Sort(CompareNeurologyFormTotalsLevelByOrdinal);
+ NeurologyFormLevel previous = null;
+ var text = string.Empty;
+ var isRange = false;
+
+ foreach (var level in levels)
+ {
+ if (previous == null || previous.Ordinal < level.Ordinal)
+ {
+ var name = level.Ordinal == 28 ? Intact : level.Name;
+
+ if (previous == null)
+ {
+ text = name;
+ }
+ else if (level.Ordinal == previous.Ordinal + 1)
+ {
+ isRange = true;
+ }
+ else
+ {
+ text += (isRange ? "-" + previous.Name + "," : ",") + name;
+ isRange = false;
+ }
+
+ previous = level;
+ }
+ }
+
+ if (isRange)
+ text += "-" + (previous.Ordinal == 28 ? Intact : previous.Name);
+
+ return addNa ? string.IsNullOrEmpty(text) ? NotApplicable : string.Format("{0},{1}", NotApplicable, text) : text;
+ }
+
+ ///
+ /// Compares two neurology levels based on property "Ordinal".
+ ///
+ /// Neurology form level A
+ /// Neurology form level B
+ ///
+ /// Returns
+ /// – 1 if a is more caudal (the ordinal of a is greater than the one of b)
+ /// – 0 if both ordinal values are the same
+ /// – -1 if b is more caudal (the ordinal of b is greater than the one of a).
+ ///
+ private static int CompareNeurologyFormTotalsLevelByOrdinal(NeurologyFormLevel a,
+ NeurologyFormLevel b)
+ {
+ // Both are null so they are equal
+ // OR
+ // a is null and b is not so b is greater
+ if (a == null)
+ return b == null ? 0 : -1;
+
+ // b is null and a is not so a is greater
+ if (b == null)
+ return 1;
+
+ return a.Ordinal > b.Ordinal ? 1 : a.Ordinal == b.Ordinal ? 0 : -1;
+ }
+
+ ///
+ /// Recursive method which iterates through the values in a nuerology form while it updates the totals generating the results produced by the algorithm.
+ ///
+ /// Form being evaluated.
+ /// Brand new totals object where the results are to be stored.
+ /// Current neurology level being evaluated
+ /// Flag used to evaluate the Kathy Collins condition on the right motor results.
+ /// Flag used to evaluate the Kathy Collins condition on the left motor results.
+ private static void UpdateTotalsWithLevelAt(NeurologyForm neurologyForm, NeurologyFormTotals totals, NeurologyFormLevel level, bool nextNonKeyMuscleShouldBeRightMotor, bool nextNonKeyMuscleShouldBeLeftMotor)
+ {
+ totals.RightTouchTotal += level.RightTouchValue;
+ totals.LeftTouchTotal += level.LeftTouchValue;
+ totals.RightPrickTotal += level.RightPrickValue;
+ totals.LeftPrickTotal += level.LeftPrickValue;
+
+ if (level.IsKeyMuscle)
+ {
+ if (level.IsLowerMuscle)
+ {
+ if (level.RightMotorImpairmentNotDueToSci && !totals.RightLowerMotorTotalHasImpairmentNotDueToSci)
+ totals.RightLowerMotorTotalHasImpairmentNotDueToSci = true;
+
+ if (level.LeftMotorImpairmentNotDueToSci && !totals.LeftLowerMotorTotalHasImpairmentNotDueToSci)
+ totals.LeftLowerMotorTotalHasImpairmentNotDueToSci = true;
+
+ if (NtRegex.IsMatch(level.RightMotorName) && !level.RightMotorImpairmentNotDueToSci)
+ totals.RightLowerMotorContainsNt = true;
+
+ if (NtRegex.IsMatch(level.LeftMotorName) && !level.LeftMotorImpairmentNotDueToSci)
+ totals.LeftLowerMotorContainsNt = true;
+ }
+ else
+ {
+ if (level.RightMotorImpairmentNotDueToSci && !totals.RightUpperMotorTotalHasImpairmentNotDueToSci)
+ totals.RightUpperMotorTotalHasImpairmentNotDueToSci = true;
+
+ if (level.LeftMotorImpairmentNotDueToSci && !totals.LeftUpperMotorTotalHasImpairmentNotDueToSci)
+ totals.LeftUpperMotorTotalHasImpairmentNotDueToSci = true;
+
+ if (NtRegex.IsMatch(level.RightMotorName) && !level.RightMotorImpairmentNotDueToSci)
+ totals.RightUpperMotorContainsNt = true;
+
+ if (NtRegex.IsMatch(level.LeftMotorName) && !level.LeftMotorImpairmentNotDueToSci)
+ totals.LeftUpperMotorContainsNt = true;
+ }
+ }
+ else
+ {
+ if (nextNonKeyMuscleShouldBeRightMotor)
+ {
+ nextNonKeyMuscleShouldBeRightMotor = false;
+ totals.AddRightMotorValue(level.Previous);
+
+ if (!totals.RightSensoryHasOnlySoftValues)
+ totals.RightMotorHasOnlySoftValues = false;
+ }
+
+ if (nextNonKeyMuscleShouldBeLeftMotor)
+ {
+ nextNonKeyMuscleShouldBeLeftMotor = false;
+ totals.AddLeftMotorValue(level.Previous);
+
+ if (!totals.LeftSensoryHasOnlySoftValues)
+ totals.LeftMotorHasOnlySoftValues = false;
+ }
+ }
+
+ if (level.RightTouchImpairmentNotDueToSci && !totals.RightTouchTotalHasImpairmentNotDueToSci)
+ totals.RightTouchTotalHasImpairmentNotDueToSci = true;
+
+ if (level.LeftTouchImpairmentNotDueToSci && !totals.LeftTouchTotalHasImpairmentNotDueToSci)
+ totals.LeftTouchTotalHasImpairmentNotDueToSci = true;
+
+ if (level.RightPrickImpairmentNotDueToSci && !totals.RightPrickTotalHasImpairmentNotDueToSci)
+ totals.RightPrickTotalHasImpairmentNotDueToSci = true;
+
+ if (level.LeftPrickImpairmentNotDueToSci && !totals.LeftPrickTotalHasImpairmentNotDueToSci)
+ totals.LeftPrickTotalHasImpairmentNotDueToSci = true;
+
+ // Check if a column contains any NT value so we can properly format the total presented to the user
+ if (NtRegex.IsMatch(level.RightTouchName) && !level.RightTouchImpairmentNotDueToSci && !totals.RightTouchContainsNt)
+ totals.RightTouchContainsNt = true;
+
+ if (NtRegex.IsMatch(level.LeftTouchName) && !level.LeftTouchImpairmentNotDueToSci && !totals.LeftTouchContainsNt)
+ totals.LeftTouchContainsNt = true;
+
+ if (NtRegex.IsMatch(level.RightPrickName) && !level.RightPrickImpairmentNotDueToSci && !totals.RightPrickContainsNt)
+ totals.RightPrickContainsNt = true;
+
+ if (NtRegex.IsMatch(level.LeftPrickName) && !level.LeftPrickImpairmentNotDueToSci && !totals.LeftPrickContainsNt)
+ totals.LeftPrickContainsNt = true;
+
+
+
+
+ if (totals.RightSensoryHasOnlySoftValues &&
+ ((level.RightTouchValue != 2 && !level.RightTouchImpairmentNotDueToSci) ||
+ (level.RightPrickValue != 2 && !level.RightPrickImpairmentNotDueToSci)))
+ {
+ totals.AddRightSensoryValue(level.Previous);
+
+ if ("S4_5".Equals(level.Name)
+ && (level.RightTouchValue == 2 || NtRegex.IsMatch(level.RightTouchName))
+ && (level.RightPrickValue == 2 || NtRegex.IsMatch(level.RightPrickName)))
+ {
+ totals.AddRightSensoryValue(level);
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level);
+ }
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level.Previous);
+
+ if ((level.RightTouchValue != 2 && !NtRegex.IsMatch(level.RightTouchName))
+ || (level.RightPrickValue != 2 && !NtRegex.IsMatch(level.RightPrickName)))
+ {
+ totals.RightSensoryHasOnlySoftValues = false;
+ totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
+ }
+
+ if (level.IsKeyMuscle)
+ {
+ nextNonKeyMuscleShouldBeRightMotor = true;
+ totals.HasRightCollins = true;
+ }
+ }
+
+ if (totals.LeftSensoryHasOnlySoftValues &&
+ ((level.LeftTouchValue != 2 && !level.LeftTouchImpairmentNotDueToSci) ||
+ (level.LeftPrickValue != 2 && !level.LeftPrickImpairmentNotDueToSci)))
+ {
+ totals.AddLeftSensoryValue(level.Previous);
+
+ if ("S4_5".Equals(level.Name)
+ && (level.LeftTouchValue == 2 || NtRegex.IsMatch(level.LeftTouchName))
+ && (level.LeftPrickValue == 2 || NtRegex.IsMatch(level.LeftPrickName)))
+ {
+ totals.AddLeftSensoryValue(level);
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level);
+ }
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level.Previous);
+
+ if ((level.LeftTouchValue != 2 && !NtRegex.IsMatch(level.LeftTouchName))
+ || (level.LeftPrickValue != 2 && !NtRegex.IsMatch(level.LeftPrickName)))
+ {
+ totals.LeftSensoryHasOnlySoftValues = false;
+ totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
+ }
+
+ if (level.IsKeyMuscle)
+ {
+ nextNonKeyMuscleShouldBeLeftMotor = true;
+ totals.HasLeftCollins = true;
+ }
+ }
+
+ if (totals.RightMotorHasOnlySoftValues && level.RightMotorValue != 5 && !level.RightMotorImpairmentNotDueToSci)
+ {
+ if (level.IsKeyMuscle && (level.RightMotorValue >= 3 || NtRegex.IsMatch(level.RightMotorName)))
+ {
+ totals.AddRightMotorValue(level);
+
+ // Check if left won't make the NLI have to be the previous level.
+ // Let the logic for left motor handle the SNL instead
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues
+ && (level.LeftMotorValue > 2 || level.LeftMotorImpairmentNotDueToSci || NtRegex.IsMatch(level.LeftMotorName)))
+ {
+ totals.AddNeurologicalLevelOfInjury(level);
+
+ if (!NtRegex.IsMatch(level.RightMotorName))
+ totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
+ }
+ }
+
+ if (level.RightMotorValue < 3 || NtRegex.IsMatch(level.RightMotorName))
+ {
+ totals.AddRightMotorValue(level.Previous);
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ {
+ totals.AddNeurologicalLevelOfInjury(level.Previous);
+
+ if (!NtRegex.IsMatch(level.RightMotorName))
+ totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
+ }
+ }
+
+ if (!NtRegex.IsMatch(level.RightMotorName))
+ totals.RightMotorHasOnlySoftValues = false;
+ }
+
+ if (totals.LeftMotorHasOnlySoftValues && level.LeftMotorValue != 5 && !level.LeftMotorImpairmentNotDueToSci)
+ {
+ if (level.IsKeyMuscle && (level.LeftMotorValue >= 3 || NtRegex.IsMatch(level.LeftMotorName)))
+ {
+ totals.AddLeftMotorValue(level);
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level);
+ }
+
+ if (level.LeftMotorValue < 3 || NtRegex.IsMatch(level.LeftMotorName))
+ {
+ totals.AddLeftMotorValue(level.Previous);
+
+ if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level.Previous);
+ }
+
+ if (!NtRegex.IsMatch(level.LeftMotorName))
+ {
+ totals.LeftMotorHasOnlySoftValues = false;
+ totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
+ }
+ }
+
+ /* -- RECURSIVE CALL --------------- */
+ if (level.Next != null)
+ UpdateTotalsWithLevelAt(neurologyForm, totals, level.Next,
+ nextNonKeyMuscleShouldBeRightMotor && totals.RightMotorHasOnlySoftValues,
+ nextNonKeyMuscleShouldBeLeftMotor && totals.LeftMotorHasOnlySoftValues);
+
+ #region This happens when there are INTACT values -------------------------------------------
+ if ("S4_5".Equals(level.Name))
+ {
+ if (totals.RightSensoryHasOnlySoftValues && totals.LeftSensoryHasOnlySoftValues
+ && totals.RightMotorHasOnlySoftValues && totals.LeftMotorHasOnlySoftValues)
+ totals.AddNeurologicalLevelOfInjury(level);
+
+ if (totals.RightSensoryHasOnlySoftValues)
+ {
+ totals.AddRightSensoryValue(level);
+ totals.RightSensoryHasOnlySoftValues = false;
+ }
+
+ if (totals.LeftSensoryHasOnlySoftValues)
+ {
+ totals.AddLeftSensoryValue(level);
+ totals.LeftSensoryHasOnlySoftValues = false;
+ }
+
+ if (totals.RightMotorHasOnlySoftValues)
+ {
+ totals.AddRightMotorValue(level);
+ totals.RightMotorHasOnlySoftValues = false;
+ }
+
+ if (totals.LeftMotorHasOnlySoftValues)
+ {
+ totals.AddLeftMotorValue(level);
+ totals.LeftMotorHasOnlySoftValues = false;
+ }
+ }
+ #endregion
+
+ if (totals.RightSensoryZppHasOnlySoftValues && (!"0".Equals(level.RightTouchName) || !"0".Equals(level.RightPrickName)))
+ {
+ if ((level.RightTouchValue > 0 || level.RightTouchImpairmentNotDueToSci)
+ || (level.RightPrickValue > 0 || level.RightPrickImpairmentNotDueToSci))
+ totals.RightSensoryZppHasOnlySoftValues = false;
+
+ totals.AddRightSensoryZppValue(level);
+ }
+
+ if (totals.LeftSensoryZppHasOnlySoftValues && (!"0".Equals(level.LeftTouchName) || !"0".Equals(level.LeftPrickName)))
+ {
+ if ((level.LeftTouchValue > 0 || level.LeftTouchImpairmentNotDueToSci)
+ || (level.LeftPrickValue > 0 || level.LeftPrickImpairmentNotDueToSci))
+ totals.LeftSensoryZppHasOnlySoftValues = false;
+
+ totals.AddLeftSensoryZppValue(level);
+ }
+
+ if (totals.RightMotorZppHasOnlySoftValues
+ && (level.HasOtherRightMotorFunction
+ || (!"0".Equals(level.RightMotorName) && (level.IsKeyMuscle || totals.RightMotorContains(level.Name)))))
+ {
+ if ((level.RightMotorImpairmentNotDueToSci || level.HasOtherRightMotorFunction || !NtRegex.IsMatch(level.RightMotorName)) &&
+ (level.IsKeyMuscle
+ || level.Ordinal < 4
+ || (level.Ordinal > 25 && !totals.RightUpperMotorContainsNt && !totals.RightLowerMotorContainsNt && !totals.HasRightCollins)
+ || (level.Ordinal > 8 && level.Ordinal < 21 && !totals.RightUpperMotorContainsNt)))
+ totals.RightMotorZppHasOnlySoftValues = false;
+
+ totals.AddRightMotorZppValue(level);
+ }
+
+ if (totals.LeftMotorZppHasOnlySoftValues
+ && (level.HasOtherLeftMotorFunction
+ || (!"0".Equals(level.LeftMotorName) && (level.IsKeyMuscle || totals.LeftMotorContains(level.Name)))))
+ {
+ if ((level.LeftMotorImpairmentNotDueToSci || level.HasOtherLeftMotorFunction || !NtRegex.IsMatch(level.LeftMotorName)) &&
+ (level.IsKeyMuscle
+ || level.Ordinal < 4
+ || (level.Ordinal > 25 && !totals.LeftUpperMotorContainsNt && !totals.LeftLowerMotorContainsNt && !totals.HasLeftCollins)
+ || (level.Ordinal > 8 && level.Ordinal < 21 && !totals.LeftUpperMotorContainsNt)))
+ totals.LeftMotorZppHasOnlySoftValues = false;
+
+ totals.AddLeftMotorZppValue(level);
+ }
+
+
+ // Update most Rostral levels with motor function
+ if ((level.IsKeyMuscle || level.HasOtherRightMotorFunction)
+ && totals.MostRostralRightLevelWithMotorFunction == null
+ && (level.RightMotorImpairmentNotDueToSci || level.HasOtherRightMotorFunction || (level.RightMotorValue != 0 && level.IsKeyMuscle)))
+ totals.MostRostralRightLevelWithMotorFunction = level;
+
+ if ((level.IsKeyMuscle || level.HasOtherLeftMotorFunction)
+ && totals.MostRostralLeftLevelWithMotorFunction == null
+ && (level.LeftMotorImpairmentNotDueToSci || level.HasOtherLeftMotorFunction || (level.LeftMotorValue != 0 && level.IsKeyMuscle)))
+ totals.MostRostralLeftLevelWithMotorFunction = level;
+
+ // Update most Caudal levels with motor function
+ if ((level.IsKeyMuscle || level.HasOtherRightMotorFunction)
+ && totals.MostCaudalRightLevelWithMotorFunction == null
+ && (!"0".Equals(level.RightMotorName) || level.HasOtherRightMotorFunction))
+ totals.MostCaudalRightLevelWithMotorFunction = level;
+
+ if ((level.IsKeyMuscle || level.HasOtherLeftMotorFunction)
+ && totals.MostCaudalLeftLevelWithMotorFunction == null
+ && (!"0".Equals(level.LeftMotorName) || level.HasOtherLeftMotorFunction))
+ totals.MostCaudalLeftLevelWithMotorFunction = level;
+
+
+ if (!level.IsKeyMuscle)
+ return;
+
+ if (level.IsLowerMuscle)
+ {
+ totals.RightLowerMotorTotal += level.RightMotorValue;
+ totals.LeftLowerMotorTotal += level.LeftMotorValue;
+ }
+ else
+ {
+ totals.RightUpperMotorTotal += level.RightMotorValue;
+ totals.LeftUpperMotorTotal += level.LeftMotorValue;
+ }
+ }
+
+ ///
+ /// Evaluates the specified form and totals to determine if any of the different
+ /// return conditions could produce a case where there could be motor function more
+ /// than 3 levels below the injury level.
+ ///
+ /// Form that was used to produce the totals.
+ /// Totals retunred by the algorithm.
+ /// Flag indicating if any combination in the totals could have a case with motor function more than 3 levels below the injury level.
+ private static bool CouldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel(NeurologyForm neurologyForm, NeurologyFormTotals totals)
+ {
+ NeurologyFormLevel mostRostralRightLevelWithMotor = null;
+ NeurologyFormLevel mostRostralLeftLevelWithMotor = null;
+
+ var currentLevel = neurologyForm.GetLevelWithName("S1");
+
+ while ((mostRostralRightLevelWithMotor == null || mostRostralLeftLevelWithMotor == null)
+ && currentLevel != null // Could happen if SNL is C1
+ && currentLevel.Ordinal >= totals.MostCaudalNeurologicalLevelOfInjury.Ordinal)
+ {
+ if (mostRostralRightLevelWithMotor == null &&
+ (currentLevel.RightMotorImpairmentNotDueToSci || currentLevel.HasOtherRightMotorFunction || (currentLevel.RightMotorValue != 0 && currentLevel.IsKeyMuscle)))
+ mostRostralRightLevelWithMotor = currentLevel;
+
+ if (mostRostralLeftLevelWithMotor == null &&
+ (currentLevel.LeftMotorImpairmentNotDueToSci || currentLevel.HasOtherLeftMotorFunction || (currentLevel.LeftMotorValue != 0 && currentLevel.IsKeyMuscle)))
+ mostRostralLeftLevelWithMotor = currentLevel;
+
+ currentLevel = currentLevel.Previous;
+ }
+
+ return (mostRostralRightLevelWithMotor == null || mostRostralRightLevelWithMotor.Ordinal - totals.MostCaudalRightMotor.Ordinal <= 3)
+ && (mostRostralLeftLevelWithMotor == null || mostRostralLeftLevelWithMotor.Ordinal - totals.MostCaudalLeftMotor.Ordinal <= 3);
+ }
+
+ ///
+ /// Evaluates the specified form and totals to determine if any of the different
+ /// return conditions could produce a case where the Asia Impairment Scale is C o D.
+ ///
+ /// Form that was used to produce the totals.
+ /// Totals retunred by the algorithm.
+ /// out variable use as flag indicating if this case is a possible ASIA C.
+ /// out variable use as flag indicating if this case is a possible ASIA D.
+ private static void CouldBeAsiaCorD(NeurologyForm neurologyForm, NeurologyFormTotals totals, out bool couldBeAsiaC, out bool couldBeAsiaD)
+ {
+ couldBeAsiaC = false;
+ couldBeAsiaD = false;
+ NeurologyFormLevel rightMotor = null;
+ NeurologyFormLevel leftMotor = null;
+
+ // Check if the patient could be motor incoplete via sphincter contraction
+ // Otherwise we need to check for motor function more than 3 levels below motor level.
+ var couldHaveAnalContraction = neurologyForm.AnalContraction == BinaryObservation.Yes || neurologyForm.AnalContraction == BinaryObservation.NT;
+ //var couldNotHaveAnalContraction = neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT;
+
+ var nliEnum = totals.GetNeurologicalLevelsOfInjury().GetEnumerator();
+
+ while (nliEnum.MoveNext() && (!couldBeAsiaC || !couldBeAsiaD))
+ {
+ //var otherMotorIsMoreThanThreeLevelsBelowNli = (neurologyForm.)
+
+ // RIGHT MOTOR
+ // If not motor incomplete already, find the right motor level that correspond to this particular neurological level
+ if (!couldHaveAnalContraction && (rightMotor == null || rightMotor.Ordinal < nliEnum.Current.Ordinal))
+ {
+ var rightMotorValues = totals.GetRightMotorValues().GetEnumerator();
+ rightMotor = null;
+
+ while (rightMotor == null && rightMotorValues.MoveNext())
+ {
+ if (rightMotorValues.Current.Ordinal >= nliEnum.Current.Ordinal)
+ rightMotor = rightMotorValues.Current;
+ }
+
+ rightMotorValues.Dispose();
+ }
+
+ // LEFT MOTOR
+ // If not motor incomplete already, find the left motor level that correspond to this particular neurological level
+ if (!couldHaveAnalContraction && (leftMotor == null || leftMotor.Ordinal < nliEnum.Current.Ordinal))
+ {
+ var leftMotorValues = totals.GetLeftMotorValues().GetEnumerator();
+ leftMotor = null;
+
+ while (leftMotor == null && leftMotorValues.MoveNext())
+ {
+ if (leftMotorValues.Current.Ordinal >= nliEnum.Current.Ordinal)
+ leftMotor = leftMotorValues.Current;
+ }
+
+ leftMotorValues.Dispose();
+ }
+
+ //if (couldNotHaveAnalContraction
+ // && (rightMotor == null || totals.MostCaudalRightLevelWithMotorFunction.Ordinal - rightMotor.Ordinal <= 3)
+ // && (leftMotor == null || totals.MostCaudalLeftLevelWithMotorFunction.Ordinal - leftMotor.Ordinal <= 3))
+ // couldBeAsiaB = true;
+
+ // If the test is not motor incomplete at this level, do not continue to count motor levels, move to the next nli available.
+ if (!couldHaveAnalContraction
+ && totals.MostCaudalRightLevelWithMotorFunction.Ordinal - rightMotor.Ordinal <= 3
+ && totals.MostCaudalLeftLevelWithMotorFunction.Ordinal - leftMotor.Ordinal <= 3)
+ continue;
+
+ // When motor incomplete and the nli is S1 or more caudal, it is automatically D since there are no myotomes to count from.
+ // We can break the loop.
+ if (nliEnum.Current.Ordinal > 24) // Greater than L5 (24)
+ {
+ couldBeAsiaD = true;
+ break;
+ }
+
+ // If motor incomplete, count the motor levels with muscle grade greater than two
+ var levelsWithMuscleGradeGreaterThanTwo = 0;
+ var levelsWithMuscleGradeLessThanThree = 0;
+ var eligibleLevelCount = 0;
+ var currentLevel = nliEnum.Current.Next;
+
+ while (currentLevel != null)
+ {
+ if (currentLevel.IsKeyMuscle)
+ {
+ eligibleLevelCount += 2;
+
+ if (currentLevel.RightMotorValue > 2 || currentLevel.RightMotorImpairmentNotDueToSci || NtRegex.IsMatch(currentLevel.RightMotorName))
+ levelsWithMuscleGradeGreaterThanTwo++;
+
+ if ((currentLevel.RightMotorValue < 3 || NtRegex.IsMatch(currentLevel.RightMotorName)) && !currentLevel.RightMotorImpairmentNotDueToSci)
+ levelsWithMuscleGradeLessThanThree++;
+
+ if (currentLevel.LeftMotorValue > 2 || currentLevel.LeftMotorImpairmentNotDueToSci || NtRegex.IsMatch(currentLevel.LeftMotorName))
+ levelsWithMuscleGradeGreaterThanTwo++;
+
+ if ((currentLevel.LeftMotorValue < 3 || NtRegex.IsMatch(currentLevel.LeftMotorName)) && !currentLevel.LeftMotorImpairmentNotDueToSci)
+ levelsWithMuscleGradeLessThanThree++;
+ }
+
+ currentLevel = currentLevel.Next;
+ }
+
+ // If not more than half the myotomes contain values less to 3, this is an Asia C
+ if (levelsWithMuscleGradeLessThanThree > eligibleLevelCount / 2)
+ couldBeAsiaC = true;
+
+ // If at least half the myotomes below the current NLI containing values greater or equal to 3, hooray! it is ASIA D.
+ if (levelsWithMuscleGradeGreaterThanTwo >= eligibleLevelCount / 2)
+ couldBeAsiaD = true;
+ }
+
+ nliEnum.Dispose();
+ }
+ }
+}
diff --git a/Rhi.Isncsci.Core/BinaryObservation.cs b/Rhi.Isncsci.Core/BinaryObservation.cs
new file mode 100644
index 0000000..775b615
--- /dev/null
+++ b/Rhi.Isncsci.Core/BinaryObservation.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Rhi.Isncsci.Core
+{
+ [Flags]
+ public enum BinaryObservation
+ {
+ None = 0x0,
+ Yes = 0x1,
+ No = 0x2,
+ NT = 0x4
+ }
+}
diff --git a/Rhi.Isncsci.Core/NeurologyForm.cs b/Rhi.Isncsci.Core/NeurologyForm.cs
new file mode 100644
index 0000000..15af54e
--- /dev/null
+++ b/Rhi.Isncsci.Core/NeurologyForm.cs
@@ -0,0 +1,233 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Rhi.Isncsci.Core
+{
+ public class NeurologyForm
+ {
+ private static readonly string[] LevelNames = new[]
+ {
+ "C2", "C3", "C4", "C5", "C6", "C7", "C8", "T1", "T2", "T3"
+ , "T4", "T5", "T6", "T7", "T8", "T9", "T10", "T11", "T12",
+ "L1", "L2", "L3", "L4", "L5", "S1", "S2", "S3", "S4_5"
+ };
+
+ private readonly static string[] KeyMuscles = new[] { "C5", "C6", "C7", "C8", "T1", "L2", "L3", "L4", "L5", "S1" };
+ private readonly Dictionary _levels;
+ private static readonly Regex NtRegex = new Regex(@"\bNT\b", RegexOptions.IgnoreCase);
+ private static readonly Regex NonSciImpairmentRegex = new Regex(".+[!]");
+ private static readonly Regex NonSciImpairmentFlagsRegex = new Regex("[\\*!]");
+
+ private const int NormalMotorValue = 5;
+ private const int NormalSensoryValue = 2;
+
+ public NeurologyFormLevel C1 { get; protected set; }
+
+ public BinaryObservation AnalContraction { get; set; }
+ public BinaryObservation AnalSensation { get; set; }
+ public NeurologyFormLevel RightLowestNonKeyMuscleWithMotorFunction { get; protected set; }
+ public NeurologyFormLevel LeftLowestNonKeyMuscleWithMotorFunction { get; protected set; }
+
+ public NeurologyForm()
+ {
+ C1 = new NeurologyFormLevel
+ {
+ Ordinal = 0,
+ IsKeyMuscle = false,
+ IsLowerMuscle = false,
+ Name = "C1",
+ Previous = null,
+ RightMotorName = "5",
+ RightMotorValue = 5,
+ LeftMotorName = "5",
+ LeftMotorValue = 5,
+ RightTouchName = "2",
+ RightTouchValue = 2,
+ LeftTouchName = "2",
+ LeftTouchValue = 2,
+ RightPrickName = "2",
+ RightPrickValue = 2,
+ LeftPrickName = "2",
+ LeftPrickValue = 2
+ };
+
+ _levels = new Dictionary();
+
+ var previousLevel = C1;
+
+ for (var i = 0; i < LevelNames.Length; i++)
+ {
+ var name = LevelNames[i];
+
+ var currentLevel = new NeurologyFormLevel
+ {
+ IsKeyMuscle = KeyMuscles.Contains(name),
+ IsLowerMuscle = (i >= 20 && i <= 24),
+ Name = name,
+ Ordinal = i + 1,
+ Previous = previousLevel
+ };
+
+ previousLevel.Next = currentLevel;
+ previousLevel = currentLevel;
+ _levels.Add(name, currentLevel);
+ }
+ }
+
+ public void SetRightLowestNonKeyMuscleWithMotorFunction(string levelName)
+ {
+ if (string.IsNullOrEmpty(levelName))
+ return;
+
+ var key = levelName.ToUpper();
+
+ if (!_levels.ContainsKey(key))
+ return;
+
+ if (RightLowestNonKeyMuscleWithMotorFunction != null)
+ RightLowestNonKeyMuscleWithMotorFunction.HasOtherRightMotorFunction = false;
+
+ RightLowestNonKeyMuscleWithMotorFunction = _levels[key];
+ RightLowestNonKeyMuscleWithMotorFunction.HasOtherRightMotorFunction = true;
+ }
+
+ public void SetLeftLowestNonKeyMuscleWithMotorFunction(string levelName)
+ {
+ if (string.IsNullOrEmpty(levelName))
+ return;
+
+ var key = levelName.ToUpper();
+
+ if (!_levels.ContainsKey(key))
+ return;
+
+ if (LeftLowestNonKeyMuscleWithMotorFunction != null)
+ LeftLowestNonKeyMuscleWithMotorFunction.HasOtherLeftMotorFunction = false;
+
+ LeftLowestNonKeyMuscleWithMotorFunction = _levels[key];
+ LeftLowestNonKeyMuscleWithMotorFunction.HasOtherLeftMotorFunction = true;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// Neurology level at the specified index.
+ public NeurologyFormLevel GetLevelAt(int index)
+ {
+ return index >= 0 && index < _levels.Count ? _levels.ElementAt(index).Value : null;
+ }
+
+ ///
+ /// Returns the neurology level based on a level name. E.g. C2,C3,C4...S1,S2,S3,S4_5
+ ///
+ /// Lavel name being searched.
+ /// Neurology level that matches the specified level name.
+ public NeurologyFormLevel GetLevelWithName(string levelName)
+ {
+ var key = levelName.ToUpper();
+
+ return _levels.ContainsKey(key) ? _levels[key] : null;
+ }
+
+ ///
+ /// Updates the values of the neurology level with the specified name.
+ /// You can pass strings containing values between 0-2 for touch and prick and 0-5 for motor.
+ /// You can also use the exclamation mark and asterisk at the end of the string to indicate impairment not due to a spinal cord injury.
+ /// Finally, you can also pass NT to indicate that a value was not testable.
+ ///
+ /// Name of the respective neurology level.
+ /// Right touch
+ /// Left touch
+ /// Right Prick
+ /// Left prick
+ /// Right motor
+ /// Left motor
+ ///
+ public NeurologyForm UpdateLevelAt(string levelName, string rightTouchName, string leftTouchName,
+ string rightPrickName, string leftPrickName, string rightMotorName, string leftMotorName)
+ {
+ var level = GetLevelWithName(levelName);
+
+ if (level != null)
+ UpdateLevel(level, rightTouchName, leftTouchName, rightPrickName, leftPrickName, rightMotorName, leftMotorName);
+
+ return this;
+ }
+
+ private static int GetDermatomeValue(string text, int normalValue)
+ {
+ if (text.ToUpper() == "NT*")
+ return normalValue;
+
+ int value;
+ int.TryParse(NonSciImpairmentFlagsRegex.Replace(text, string.Empty), out value);
+
+ return value;
+ }
+
+ private static void UpdateLevel(NeurologyFormLevel level, string rightTouchName, string leftTouchName,
+ string rightPrickName, string leftPrickName, string rightMotorName, string leftMotorName)
+ {
+ // Right Touch
+ level.RightTouchName = rightTouchName;
+ level.RightTouchValue = GetDermatomeValue(rightTouchName, NormalSensoryValue);
+ level.RightTouchImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(rightTouchName);
+
+ // Left Touch
+ level.LeftTouchName = leftTouchName;
+ level.LeftTouchValue = GetDermatomeValue(leftTouchName, NormalSensoryValue);
+ level.LeftTouchImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(leftTouchName);
+
+ // Right Prick
+ level.RightPrickName = rightPrickName;
+ level.RightPrickValue = GetDermatomeValue(rightPrickName, NormalSensoryValue);
+ level.RightPrickImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(rightPrickName);
+
+ // Left Prick
+ level.LeftPrickName = leftPrickName;
+ level.LeftPrickValue = GetDermatomeValue(leftPrickName, NormalSensoryValue);
+ level.LeftPrickImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(leftPrickName);
+
+ // Right Motor
+ level.RightMotorName = rightMotorName;
+ level.RightMotorValue = GetDermatomeValue(rightMotorName, NormalMotorValue);
+ level.RightMotorImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(rightMotorName);
+
+ // Left Motor
+ level.LeftMotorName = leftMotorName;
+ level.LeftMotorValue = GetDermatomeValue(leftMotorName, NormalMotorValue);
+ level.LeftMotorImpairmentNotDueToSci = NonSciImpairmentRegex.IsMatch(leftMotorName);
+
+ if (!level.IsKeyMuscle)
+ {
+ if ((level.RightTouchValue == 2 || level.RightTouchImpairmentNotDueToSci)
+ && (level.RightPrickValue == 2 || level.RightPrickImpairmentNotDueToSci))
+ {
+ level.RightMotorName = "5";
+ level.RightMotorValue = 5;
+ }
+ else
+ {
+ level.RightMotorName = (NtRegex.IsMatch(level.RightTouchName) || level.RightTouchValue == 2)
+ && (NtRegex.IsMatch(level.RightPrickName) || level.RightPrickValue == 2) ? "NT" : "0";
+ level.RightMotorValue = 0;
+ }
+
+ if ((level.LeftTouchValue == 2 || level.LeftTouchImpairmentNotDueToSci)
+ && (level.LeftPrickValue == 2 || level.LeftPrickImpairmentNotDueToSci))
+ {
+ level.LeftMotorName = "5";
+ level.LeftMotorValue = 5;
+ }
+ else
+ {
+ level.LeftMotorName = (NtRegex.IsMatch(level.LeftTouchName) || level.LeftTouchValue == 2)
+ && (NtRegex.IsMatch(level.LeftPrickName) || level.LeftPrickValue == 2) ? "NT" : "0";
+ level.LeftMotorValue = 0;
+ }
+ }
+ }
+ }
+}
diff --git a/Rhi.Isncsci.Core/NeurologyFormLevel.cs b/Rhi.Isncsci.Core/NeurologyFormLevel.cs
new file mode 100644
index 0000000..f93b2c0
--- /dev/null
+++ b/Rhi.Isncsci.Core/NeurologyFormLevel.cs
@@ -0,0 +1,72 @@
+namespace Rhi.Isncsci.Core
+{
+ public class NeurologyFormLevel
+ {
+ public virtual bool IsKeyMuscle { get; set; }
+ public virtual bool IsLowerMuscle { get; set; }
+ public virtual bool LeftTouchImpairmentNotDueToSci { get; set; }
+ public virtual string LeftTouchName { get; set; }
+ public virtual int LeftTouchValue { get; set; }
+ public virtual bool LeftPrickImpairmentNotDueToSci { get; set; }
+ public virtual string LeftPrickName { get; set; }
+ public virtual int LeftPrickValue { get; set; }
+ public virtual bool LeftMotorImpairmentNotDueToSci { get; set; }
+ public virtual bool HasOtherLeftMotorFunction { get; set; }
+ public virtual string LeftMotorName { get; set; }
+ public virtual int LeftMotorValue { get; set; }
+ public virtual string Name { get; set; }
+ public virtual NeurologyFormLevel Next { get; set; }
+ //public virtual NeurologyTest NeurologyTest { get; set; }
+ public virtual int Ordinal { get; set; }
+ public virtual NeurologyFormLevel Previous { get; set; }
+ public virtual bool RightTouchImpairmentNotDueToSci { get; set; }
+ public virtual string RightTouchName { get; set; }
+ public virtual int RightTouchValue { get; set; }
+ public virtual bool RightPrickImpairmentNotDueToSci { get; set; }
+ public virtual string RightPrickName { get; set; }
+ public virtual int RightPrickValue { get; set; }
+ public virtual bool RightMotorImpairmentNotDueToSci { get; set; }
+ public bool HasOtherRightMotorFunction { get; set; }
+ public virtual string RightMotorName { get; set; }
+ public virtual int RightMotorValue { get; set; }
+
+ public virtual NeurologyFormLevel SetValues(int ordinal, bool isKeyMuscle, bool isLowerMuscle,
+ string rightTouchName, int rightTouchValue, bool rightTouchImpairmentNotDueToSci,
+ string leftTouchName, int leftTouchValue, bool leftTouchImpairmentNotDueToSci,
+ string rightPrickName, int rightPrickValue, bool rightPrickImpairmentNotDueToSci,
+ string leftPrickName, int leftPrickValue, bool leftPrickImpairmentNotDueToSci,
+ string rightMotorName, int rightMotorValue, bool rightMotorImpairmentNotDueToSci,
+ string leftMotorName, int leftMotorValue, bool leftMotorImpairmentNotDueToSci)
+ {
+ IsKeyMuscle = isKeyMuscle;
+ IsLowerMuscle = isLowerMuscle;
+ Ordinal = ordinal;
+
+ RightTouchName = rightPrickName;
+ RightTouchValue = rightPrickValue;
+ RightTouchImpairmentNotDueToSci = rightTouchImpairmentNotDueToSci;
+
+ LeftTouchName = leftTouchName;
+ LeftTouchValue = leftTouchValue;
+ LeftTouchImpairmentNotDueToSci = leftTouchImpairmentNotDueToSci;
+
+ RightPrickName = rightPrickName;
+ RightPrickValue = rightPrickValue;
+ RightPrickImpairmentNotDueToSci = rightPrickImpairmentNotDueToSci;
+
+ LeftPrickName = leftPrickName;
+ LeftPrickValue = leftPrickValue;
+ LeftPrickImpairmentNotDueToSci = leftPrickImpairmentNotDueToSci;
+
+ RightMotorName = rightMotorName;
+ RightMotorValue = rightMotorValue;
+ RightMotorImpairmentNotDueToSci = rightMotorImpairmentNotDueToSci;
+
+ LeftMotorName = leftMotorName;
+ LeftMotorValue = leftMotorValue;
+ LeftMotorImpairmentNotDueToSci = leftMotorImpairmentNotDueToSci;
+
+ return this;
+ }
+ }
+}
diff --git a/Rhi.Isncsci.Core/NeurologyFormTotals.cs b/Rhi.Isncsci.Core/NeurologyFormTotals.cs
new file mode 100644
index 0000000..12c4564
--- /dev/null
+++ b/Rhi.Isncsci.Core/NeurologyFormTotals.cs
@@ -0,0 +1,317 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rhi.Isncsci.Core
+{
+ public class NeurologyFormTotals
+ {
+ public int RightUpperMotorTotal;
+ public bool RightUpperMotorTotalHasImpairmentNotDueToSci;
+ public bool RightUpperMotorContainsNt;
+ public int LeftUpperMotorTotal;
+ public bool LeftUpperMotorTotalHasImpairmentNotDueToSci;
+ public bool LeftUpperMotorContainsNt;
+ public int RightLowerMotorTotal;
+ public bool RightLowerMotorTotalHasImpairmentNotDueToSci;
+ public bool RightLowerMotorContainsNt;
+ public int LeftLowerMotorTotal;
+ public bool LeftLowerMotorTotalHasImpairmentNotDueToSci;
+ public bool LeftLowerMotorContainsNt;
+ public int RightTouchTotal;
+ public bool RightTouchTotalHasImpairmentNotDueToSci;
+ public bool RightTouchContainsNt;
+ public int LeftTouchTotal;
+ public bool LeftTouchTotalHasImpairmentNotDueToSci;
+ public bool LeftTouchContainsNt;
+ public int RightPrickTotal;
+ public bool RightPrickTotalHasImpairmentNotDueToSci;
+ public bool RightPrickContainsNt;
+ public int LeftPrickTotal;
+ public bool LeftPrickTotalHasImpairmentNotDueToSci;
+ public bool LeftPrickContainsNt;
+
+ public int UpperMotorTotal;
+ public int LowerMotorTotal;
+ public int TouchTotal;
+ public int PrickTotal;
+
+ #region Right Sensory
+ private readonly List _rightSensoryValues = new List();
+ public bool RightSensoryHasOnlySoftValues = true;
+
+ public bool RightSensoryIsEmpty()
+ {
+ return !_rightSensoryValues.Any();
+ }
+
+ public void AddRightSensoryValue(NeurologyFormLevel level)
+ {
+ if (_rightSensoryValues.Any(l => l.Name.ToUpper() == level.Name))
+ return;
+
+ _rightSensoryValues.Add(level);
+ }
+
+ public List GetRightSensoryValues()
+ {
+ return new List(_rightSensoryValues);
+ }
+
+ public bool RightSensoryContains(string levelName)
+ {
+ return _rightSensoryValues.Any(l => l.Name.ToUpper() == levelName.ToUpper());
+ }
+ #endregion
+
+ #region Left Sensory
+ private readonly List _leftSensoryValues = new List();
+ public bool LeftSensoryHasOnlySoftValues = true;
+
+ public bool LeftSensoryIsEmpty()
+ {
+ return !_leftSensoryValues.Any();
+ }
+
+ public void AddLeftSensoryValue(NeurologyFormLevel level)
+ {
+ if (_leftSensoryValues.Any(l => l.Name.ToUpper() == level.Name))
+ return;
+
+ _leftSensoryValues.Add(level);
+ }
+
+ public List GetLeftSensoryValues()
+ {
+ return new List(_leftSensoryValues);
+ }
+
+ public bool LeftSensoryContains(string levelName)
+ {
+ return _leftSensoryValues.Any(l => l.Name.ToUpper() == levelName.ToUpper());
+ }
+ #endregion
+
+ #region Right Motor
+ private readonly List _rightMotorValues = new List();
+ public bool RightMotorHasOnlySoftValues = true;
+ public bool HasRightCollins = false;
+ public NeurologyFormLevel MostRostralRightMotor { get; protected set; }
+ public NeurologyFormLevel MostCaudalRightMotor { get; protected set; }
+
+ public bool RightMotorIsEmpty()
+ {
+ return !_rightMotorValues.Any();
+ }
+
+ public void AddRightMotorValue(NeurologyFormLevel level)
+ {
+ if (_rightMotorValues.Any(l => l.Name.ToUpper() == level.Name))
+ return;
+
+ if (MostRostralRightMotor == null || level.Ordinal < MostRostralRightMotor.Ordinal)
+ MostRostralRightMotor = level;
+
+ if (MostCaudalRightMotor == null || level.Ordinal > MostCaudalRightMotor.Ordinal)
+ MostCaudalRightMotor = level;
+
+ _rightMotorValues.Add(level);
+ }
+
+ public List GetRightMotorValues()
+ {
+ return new List(_rightMotorValues);
+ }
+
+ public bool RightMotorContains(string levelName)
+ {
+ return _rightMotorValues.Any(l => l.Name.ToUpper() == levelName.ToUpper());
+ }
+ #endregion
+
+ #region Left Motor
+ private readonly List _leftMotorValues = new List();
+ public bool LeftMotorHasOnlySoftValues = true;
+ public bool HasLeftCollins = false;
+
+ public bool LeftMotorIsEmpty()
+ {
+ return !_leftMotorValues.Any();
+ }
+
+ public void AddLeftMotorValue(NeurologyFormLevel level)
+ {
+ if (_leftMotorValues.Any(l => l.Name.ToUpper() == level.Name))
+ return;
+
+ if (MostRostralLeftMotor == null || level.Ordinal < MostRostralLeftMotor.Ordinal)
+ MostRostralLeftMotor = level;
+
+ if (MostCaudalLeftMotor == null || level.Ordinal > MostCaudalLeftMotor.Ordinal)
+ MostCaudalLeftMotor = level;
+
+ _leftMotorValues.Add(level);
+ }
+
+ public List GetLeftMotorValues()
+ {
+ return new List(_leftMotorValues);
+ }
+
+ public bool LeftMotorContains(string levelName)
+ {
+ return _leftMotorValues.Any(l => l.Name.ToUpper() == levelName.ToUpper());
+ }
+ #endregion
+
+ #region Neurological Level of Injury
+ private readonly List _neurologicalLevelsOfInjury = new List();
+ public bool NeurologicalLevelOfInjuryHasOnlySoftValues = true;
+
+ public void AddNeurologicalLevelOfInjury(NeurologyFormLevel level)
+ {
+ if (_neurologicalLevelsOfInjury.Any(l => l.Name.ToLower() == level.Name.ToLower()))
+ return;
+
+ if (MostRostralNeurologicalLevelOfInjury == null || level.Ordinal < MostRostralNeurologicalLevelOfInjury.Ordinal)
+ MostRostralNeurologicalLevelOfInjury = level;
+
+ if (MostCaudalNeurologicalLevelOfInjury == null || level.Ordinal > MostCaudalNeurologicalLevelOfInjury.Ordinal)
+ MostCaudalNeurologicalLevelOfInjury = level;
+
+ _neurologicalLevelsOfInjury.Add(level);
+ }
+
+ public List GetNeurologicalLevelsOfInjury()
+ {
+ return new List(_neurologicalLevelsOfInjury);
+ }
+ #endregion
+
+ #region Right Sensory ZPP
+ private readonly List _rightSensoryZppValues = new List();
+ public bool RightSensoryZppHasOnlySoftValues = true;
+
+ public bool RightSensoryZppIsEmpty()
+ {
+ return !_rightSensoryZppValues.Any();
+ }
+
+ public void AddRightSensoryZppValue(NeurologyFormLevel level)
+ {
+ if (_rightSensoryZppValues.Any(l => l.Name.ToUpper() == level.Name)
+ || "S4_5".Equals(level.Name))
+ return;
+
+ _rightSensoryZppValues.Add(level);
+ }
+
+ public List GetRightSensoryZppValues()
+ {
+ return new List(_rightSensoryZppValues);
+ }
+ #endregion
+
+ #region Left Sensory ZPP
+ private readonly List _leftSensoryZppValues = new List();
+ public bool LeftSensoryZppHasOnlySoftValues = true;
+ public bool LeftSensoryZppIsEmpty()
+ {
+ return !_leftSensoryZppValues.Any();
+ }
+
+ public void AddLeftSensoryZppValue(NeurologyFormLevel level)
+ {
+ if (_leftSensoryZppValues.Any(l => l.Name.ToUpper() == level.Name)
+ || "S4_5".Equals(level.Name))
+ return;
+
+ _leftSensoryZppValues.Add(level);
+ }
+
+ public List GetLeftSensoryZppValues()
+ {
+ return new List(_leftSensoryZppValues);
+ }
+ #endregion
+
+ #region Right Motor ZPP
+ private readonly List _rightMotorZppValues = new List();
+ public bool RightMotorZppHasOnlySoftValues = true;
+
+ public bool RightMotorZppIsEmpty()
+ {
+ return !_rightMotorZppValues.Any();
+ }
+
+ public void AddRightMotorZppValue(NeurologyFormLevel level)
+ {
+ if (_rightMotorZppValues.Any(l => l.Name.ToUpper() == level.Name)
+ || "S4_5".Equals(level.Name))
+ return;
+
+ _rightMotorZppValues.Add(level);
+ }
+
+ public List GetRightMotorZppValues()
+ {
+ return new List(_rightMotorZppValues);
+ }
+ #endregion
+
+ #region Left Motor ZPP
+ private readonly List _leftMotorZppValues = new List();
+ public bool LeftMotorZppHasOnlySoftValues = true;
+
+ public bool LeftMotorZppIsEmpty()
+ {
+ return !_leftMotorZppValues.Any();
+ }
+
+ public void AddLeftMotorZppValue(NeurologyFormLevel level)
+ {
+ if (_leftMotorZppValues.Any(l => l.Name.ToUpper() == level.Name)
+ || "S4_5".Equals(level.Name))
+ return;
+
+ _leftMotorZppValues.Add(level);
+ }
+
+ public List GetLeftMotorZppValues()
+ {
+ return new List(_leftMotorZppValues);
+ }
+ #endregion
+
+ #region Asia Impairment Scale
+ //public string Complete;
+ //public bool IsSensoryIncomplete;
+ //public bool IsMotorIncomplete;
+ private readonly List _asiaImpairmentScaleValues = new List();
+
+ public void AddAsiaImpairmentScaleValue(string value)
+ {
+ if (string.IsNullOrEmpty(value) || _asiaImpairmentScaleValues.Contains(value.ToUpper()))
+ return;
+
+ _asiaImpairmentScaleValues.Add(value.ToUpper());
+ }
+
+ public string GetAsiaImpairmentScaleValues()
+ {
+ return string.Join(",", _asiaImpairmentScaleValues.OrderBy(v => v));
+ }
+ #endregion
+
+
+ public NeurologyFormLevel MostRostralLeftMotor { get; protected set; }
+ public NeurologyFormLevel MostCaudalLeftMotor { get; protected set; }
+
+ public NeurologyFormLevel MostRostralNeurologicalLevelOfInjury { get; protected set; }
+ public NeurologyFormLevel MostCaudalNeurologicalLevelOfInjury { get; protected set; }
+
+ public NeurologyFormLevel MostRostralRightLevelWithMotorFunction { get; set; }
+ public NeurologyFormLevel MostCaudalRightLevelWithMotorFunction { get; set; }
+ public NeurologyFormLevel MostRostralLeftLevelWithMotorFunction { get; set; }
+ public NeurologyFormLevel MostCaudalLeftLevelWithMotorFunction { get; set; }
+ }
+}
diff --git a/Rhi.Isncsci.Core/NeurologyFormTotalsSummary.cs b/Rhi.Isncsci.Core/NeurologyFormTotalsSummary.cs
new file mode 100644
index 0000000..a5a7814
--- /dev/null
+++ b/Rhi.Isncsci.Core/NeurologyFormTotalsSummary.cs
@@ -0,0 +1,31 @@
+namespace Rhi.Isncsci.Core
+{
+ public class NeurologyFormTotalsSummary
+ {
+ public string AsiaImpairmentScale;
+ public string Completeness;
+ public string LeftLowerMotorTotal;
+ public string LeftMotor;
+ public string LeftMotorZpp;
+ public string LeftMotorTotal;
+ public string LeftPrickTotal;
+ public string LeftSensory;
+ public string LeftSensoryZpp;
+ public string LeftTouchTotal;
+ public string LeftUpperMotorTotal;
+ public string LowerMotorTotal;
+ public string NeurologicalLevelOfInjury;
+ public string PrickTotal;
+ public string RightLowerMotorTotal;
+ public string RightMotor;
+ public string RightMotorZpp;
+ public string RightMotorTotal;
+ public string RightPrickTotal;
+ public string RightSensory;
+ public string RightSensoryZpp;
+ public string RightTouchTotal;
+ public string RightUpperMotorTotal;
+ public string TouchTotal;
+ public string UpperMotorTotal;
+ }
+}
diff --git a/Rhi.Isncsci.Core/Rhi.Isncsci.Core.csproj b/Rhi.Isncsci.Core/Rhi.Isncsci.Core.csproj
new file mode 100644
index 0000000..d126cee
--- /dev/null
+++ b/Rhi.Isncsci.Core/Rhi.Isncsci.Core.csproj
@@ -0,0 +1,11 @@
+
+
+
+ netstandard2.0
+
+
+
+ E:\MarxGit\IsncsciAlgorithm\Rhi.Isncsci.Core\Rhi.Isncsci.Core.xml
+
+
+
diff --git a/Rhi.Isncsci.Core/Rhi.Isncsci.Core.xml b/Rhi.Isncsci.Core/Rhi.Isncsci.Core.xml
new file mode 100644
index 0000000..b3d645f
--- /dev/null
+++ b/Rhi.Isncsci.Core/Rhi.Isncsci.Core.xml
@@ -0,0 +1,120 @@
+
+
+
+ Rhi.Isncsci.Core
+
+
+
+
+ Produces a summarized version of a NeurologyFormTotals object which can be used to be displayed in an interface or be stored in a database.
+ The values in the summary return will have ranges instead of lists of values.
+
+ Neurology form that has been populated with the values to be used in the algorithm calculations.
+
+ Summarized version of the totals which presents the results as ranges, rather than lists containing every possible value for every field.
+
+
+
+
+ Produces a summarized version of a NeurologyFormTotals object which can be used to be displayed in an interface or be stored in a database.
+ The values in the summary return will have ranges instead of lists of values.
+
+ The form totals object to be used to generate the summary.
+
+ Summarized version of the totals where the enumerations get replaced by ranges.
+
+
+
+
+ Returns the results produced by the ISNCSCI Algorithm ir a raw values format.
+
+ Neurology form that has been populated with the values to be used in the algorithm calculations.
+
+ Totals in raw values format. The results contain lists with every prossible value for each field.
+ You can use the resulting object to obtained a summarized version, which uses ranges, by passing the result to the method GetTotalsSummaryFor
+
+
+
+
+ Formats a total value depending on the specified flags.
+
+ Raw total value.
+ Flag indicating if any value used in the calculation of this total presents impairment not due to a spinal cord injury.
+ Flag indicating if any value used in the calculation of this total is set to Not Testable.
+
+ The value, followed by an exclamation mark if the hasImpairmentNotDueToSci is set to true
+ or
+ UTD (Unable to determine) if the containsNt flag is set to true.
+
+
+
+
+ Returns a range string based on the specified list.
+
+ Neurological levels returned in a calculation.
+ Used in the Zone of Partial Preservation to indicate that one of the possible Asia Impairment Scale values is "A"
+ A representation of the list as a range string. E.g. NA,C4-C6,T1,T2
+
+
+
+
+ Recursive method which iterates through the values in a nuerology form while it updates the totals generating the results produced by the algorithm.
+
+ Form being evaluated.
+ Brand new totals object where the results are to be stored.
+ Current neurology level being evaluated
+ Flag used to evaluate the Kathy Collins condition on the right motor results.
+ Flag used to evaluate the Kathy Collins condition on the left motor results.
+
+
+
+ Evaluates the specified form and totals to determine if any of the different
+ return conditions could produce a case where there could be motor function more
+ than 3 levels below the injury level.
+
+ Form that was used to produce the totals.
+ Totals retunred by the algorithm.
+ Flag indicating if any combination in the totals could have a case with motor function more than 3 levels below the injury level.
+
+
+
+ Evaluates the specified form and totals to determine if any of the different
+ return conditions could produce a case where the Asia Impairment Scale is C o D.
+
+ Form that was used to produce the totals.
+ Totals retunred by the algorithm.
+ out variable use as flag indicating if this case is a possible ASIA C.
+ out variable use as flag indicating if this case is a possible ASIA D.
+
+
+
+
+
+
+ Neurology level at the specified index.
+
+
+
+ Returns the neurology level based on a level name. E.g. C2,C3,C4...S1,S2,S3,S4_5
+
+ Lavel name being searched.
+ Neurology level that matches the specified level name.
+
+
+
+ Updates the values of the neurology level with the specified name.
+ You can pass strings containing values between 0-2 for touch and prick and 0-5 for motor.
+ You can also use the exclamation mark and asterisk at the end of the string to indicate impairment not due to a spinal cord injury.
+ Finally, you can also pass NT to indicate that a value was not testable.
+
+ Name of the respective neurology level.
+ Right touch
+ Left touch
+ Right Prick
+ Left prick
+ Right motor
+ Left motor
+
+
+
+