public static Tristate RetrieveInHHByYear( Int32 subjectTag, Bioparent bioparent, Int16 year, LinksDataSet.tblParentsOfGen1RetroDataTable dtRetro )
        {
            if ( dtRetro == null ) throw new ArgumentNullException("dsLinks");
            if ( dtRetro.Count <= 0 ) throw new ArgumentException("There should be at least one row in tblParentsOfGen1Retro.");

            string select = string.Format("{0}={1} AND {2}={3} AND {4}={5}",
                subjectTag, dtRetro.SubjectTagColumn.ColumnName,
                Convert.ToByte(bioparent), dtRetro.BioparentColumn.ColumnName,
                year, dtRetro.YearColumn.ColumnName);

            DataRow[] drs = dtRetro.Select(select);
            Trace.Assert(drs.Length <= 1, "There should be at most one row."); //The item asked only until they were 18.  The function could be requesting a year when they were older.
            if ( drs.Length == 0 ) return Tristate.DoNotKnow;

            LinksDataSet.tblParentsOfGen1RetroRow dr = (LinksDataSet.tblParentsOfGen1RetroRow)drs[0];
            if ( dr.IsInHHNull() )
                return Tristate.DoNotKnow;
            else if ( dr.InHH )
                return Tristate.Yes;
            else if ( !dr.InHH )
                return Tristate.No;
            else
                throw new InvalidOperationException();
        }
        private Int32 FromLiveWithBioparent( Int16 year, Bioparent bioparent, LinksDataSet.tblRelatedStructureRow drRelated, LinksDataSet.tblParentsOfGen1RetroDataTable dtRetro )
        {
            Tristate subject1 = ParentsOfGen1Retro.RetrieveInHHByYear(drRelated.SubjectTag_S1, bioparent, year, dtRetro);
            Tristate subject2 = ParentsOfGen1Retro.RetrieveInHHByYear(drRelated.SubjectTag_S2, bioparent, year, dtRetro);
            MarkerEvidence shareBioparent = MarkerEvidence.Missing;

            if( (subject1 == Tristate.DoNotKnow) || (subject2 == Tristate.DoNotKnow) )
                shareBioparent = MarkerEvidence.Missing;
            else if( subject1 != subject2 )
                shareBioparent = MarkerEvidence.Unlikely;
            else if( (subject1 == Tristate.Yes) || (subject2 == Tristate.Yes) )
                shareBioparent = MarkerEvidence.StronglySupports;
            else if( (subject1 == Tristate.No) || (subject2 == Tristate.No) )
                shareBioparent = MarkerEvidence.Consistent;
            else
                throw new InvalidOperationException("All the conditions should have been caught.");

            MarkerEvidence mzEvidence = MarkerEvidence.Missing;
            MarkerEvidence shareBiomom = MarkerEvidence.Irrelevant;
            MarkerEvidence shareBiodad = MarkerEvidence.Irrelevant;

            switch( shareBioparent ) {
                case MarkerEvidence.StronglySupports:
                //case MarkerEvidence.Supports:
                case MarkerEvidence.Consistent:
                    mzEvidence = MarkerEvidence.Consistent;
                    break;
                case MarkerEvidence.Unlikely:
                    mzEvidence = MarkerEvidence.Unlikely;
                    break;
                //case MarkerEvidence.Disconfirms:
                //   mzEvidence = MarkerEvidence.Disconfirms;
                //   break;
                case MarkerEvidence.Missing:
                    mzEvidence = MarkerEvidence.Missing;
                    break;
                default:
                    throw new InvalidOperationException("The switch should not have gotten here.");
            }

            MarkerType markerType;
            switch( bioparent ) {
                case Bioparent.Dad:
                    markerType = MarkerType.Gen1BiodadInHH;
                    shareBiodad = shareBioparent;
                    break;
                case Bioparent.Mom:
                    markerType = MarkerType.Gen1BiomomInHH;
                    shareBiomom = shareBioparent;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("bioparent", bioparent, "The 'bioparent' value wasn't recognized.");
            }
            AddMarkerRow(drRelated.ExtendedID, drRelated.ID, markerType, year, mzEvidence: mzEvidence, sameGenerationEvidence: MarkerEvidence.Irrelevant,
                biomomEvidence: shareBiomom, biodadEvidence: shareBiodad, biograndparentEvidence: MarkerEvidence.Ambiguous);
            const Int32 recordsAdded = 1;
            return recordsAdded;
        }
        private Int32 FromBioparentDeathAge(Bioparent bioparent , LinksDataSet.tblRelatedStructureRow drRelated, LinksDataSet.tblParentsOfGen1CurrentDataTable dtParentsOfGen1Current )
        {
            const Int16 surveyYear = ItemYears.Gen1BioparentDeathAge;
            byte? deathAge1 = null;
            byte? deathAge2 = null;

            MarkerType markerType;
            switch ( bioparent ) {
                case Bioparent.Mom: markerType = MarkerType.Gen1BiomomDeathAge; break;
                case Bioparent.Dad: markerType = MarkerType.Gen1BiodadDeathAge; break;
                default: throw new ArgumentOutOfRangeException("bioparent", bioparent, "The 'FromShareBioparent' function does not accommodate this bioparent value.");
            }

            deathAge1 = ParentsOfGen1Current.RetrieveDeathAge(drRelated.SubjectTag_S1, bioparent, dtParentsOfGen1Current);
            deathAge2 = ParentsOfGen1Current.RetrieveDeathAge(drRelated.SubjectTag_S2, bioparent, dtParentsOfGen1Current);

            if ( !deathAge1.HasValue || !deathAge2.HasValue )
                return 0;

            Int32 gap = Convert.ToInt32(Math.Abs(deathAge2.Value - deathAge1.Value));

            MarkerEvidence shareBioparent = MarkerEvidence.Missing;
            if ( (gap == 0) && (deathAge1.Value < 55) ) shareBioparent = MarkerEvidence.StronglySupports;
            else if ( gap == 0 ) shareBioparent = MarkerEvidence.Supports;
            else if ( gap <= 5 ) shareBioparent = MarkerEvidence.Consistent;
            else if ( gap <= 25 ) shareBioparent = MarkerEvidence.Unlikely;
            else shareBioparent = MarkerEvidence.Disconfirms;

            MarkerEvidence mzEvidence = MarkerEvidence.Missing;
            MarkerEvidence shareBiomom = MarkerEvidence.Irrelevant;
            MarkerEvidence shareBiodad = MarkerEvidence.Irrelevant;

            switch ( shareBioparent ) {
                case MarkerEvidence.StronglySupports:
                case MarkerEvidence.Supports:
                case MarkerEvidence.Consistent:
                    mzEvidence = MarkerEvidence.Consistent;
                    break;
                case MarkerEvidence.Unlikely:
                    mzEvidence = MarkerEvidence.Unlikely;
                    break;
                case MarkerEvidence.Disconfirms:
                    mzEvidence = MarkerEvidence.Disconfirms;
                    break;
                default:
                    throw new InvalidOperationException("The switch should not have gotten here.");
            }

            switch ( bioparent ) {
                case Bioparent.Mom: shareBiomom = shareBioparent; break;
                case Bioparent.Dad: shareBiodad = shareBioparent; break;
                default: throw new ArgumentOutOfRangeException("bioparent", bioparent, "The 'FromShareBioparent' function does not accommodate this bioparent value.");
            }
            AddMarkerRow(drRelated.ExtendedID, drRelated.ID, markerType, surveyYear, mzEvidence: mzEvidence, sameGenerationEvidence: MarkerEvidence.Irrelevant,
                biomomEvidence: shareBiomom, biodadEvidence: shareBiodad, biograndparentEvidence: MarkerEvidence.Ambiguous);
            return 1;
        }
        private Int32 FromBioparentBirthState( Bioparent bioparent, LinksDataSet.tblRelatedStructureRow drRelated, ImportDataSet.tblGeocodeSanitizedDataTable dtGeocode )
        {
            const Int16 year = ItemYears.Gen1BioparentBirthState;
            Int32 subjectSmaller = Math.Min(drRelated.SubjectTag_S1, drRelated.SubjectTag_S2);
            Int32 subjectLarger = Math.Max(drRelated.SubjectTag_S1, drRelated.SubjectTag_S2);
            ImportDataSet.tblGeocodeSanitizedRow drGeo = dtGeocode.FindBySubjectTag_S1SubjectTag_S2(subjectSmaller, subjectLarger);

            MarkerType markerType;

            MarkerEvidence mzEvidence = MarkerEvidence.Irrelevant;
            MarkerEvidence shareBiomom = MarkerEvidence.Irrelevant;
            MarkerEvidence shareBiodad = MarkerEvidence.Irrelevant;

            switch ( bioparent ) {
                case Bioparent.Mom:
                    markerType = MarkerType.Gen1BiomomBirthState;
                    if ( drGeo.BirthMotherStateMissing_1 || drGeo.BirthMotherStateMissing_2 )
                        shareBiomom = MarkerEvidence.Missing;
                    else if ( drGeo.BirthMotherStateEqual )
                        shareBiomom = MarkerEvidence.Consistent;
                    else if ( !drGeo.BirthMotherStateEqual )
                        shareBiomom = MarkerEvidence.Disconfirms;
                    else
                        throw new InvalidOperationException("The execution should not have gotten here.");
                    mzEvidence = shareBiomom;
                    break;
                case Bioparent.Dad:
                    markerType = MarkerType.Gen1BiodadBirthState;
                    if ( drGeo.BirthFatherStateMissing_1 || drGeo.BirthFatherStateMissing_2 )
                        shareBiodad = MarkerEvidence.Missing;
                    else if ( drGeo.BirthFatherStateEqual )
                        shareBiodad = MarkerEvidence.Consistent;
                    else if ( !drGeo.BirthFatherStateEqual )
                        shareBiodad = MarkerEvidence.Disconfirms;
                    else
                        throw new InvalidOperationException("The execution should not have gotten here.");
                    mzEvidence = shareBiodad;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("bioparent", bioparent, "The 'FromShareBioparent' function does not accommodate this bioparent value.");
            }

            AddMarkerRow(drRelated.ExtendedID, drRelated.ID, markerType, year, mzEvidence: mzEvidence, sameGenerationEvidence: MarkerEvidence.Irrelevant,
                biomomEvidence: shareBiomom, biodadEvidence: shareBiodad, biograndparentEvidence: MarkerEvidence.Ambiguous);
            const Int32 recordsAdded = 1;
            return recordsAdded;
        }
 internal static MarkerEvidence RetrieveParentMarkerSingleYear( Int64 relatedIDLeft, MarkerType markerType, Bioparent bioparent, LinksDataSet.tblMarkerGen1DataTable dtMarker )
 {
     if ( dtMarker == null ) throw new ArgumentNullException("dtMarker");
     string select = string.Format("{0}={1} AND {2}={3}",
         relatedIDLeft, dtMarker.RelatedIDColumn.ColumnName,
         (byte)markerType, dtMarker.MarkerTypeColumn.ColumnName);
     string sort = dtMarker.SurveyYearColumn.ColumnName;
     LinksDataSet.tblMarkerGen1Row[] drs = (LinksDataSet.tblMarkerGen1Row[])dtMarker.Select(select, sort);
     Trace.Assert(drs.Length <= 1, "The number of returns markers should not exceed 1.");
     if ( drs.Length == 0 )
         return MarkerEvidence.Missing;
     else if ( bioparent == Bioparent.Dad )
         return (MarkerEvidence)drs[0].ShareBiodadEvidence;
     else if ( bioparent == Bioparent.Mom )
         return (MarkerEvidence)drs[0].ShareBiomomEvidence;
     else
         throw new ArgumentOutOfRangeException("markerType", markerType, "The 'bioparent' value is not accepted by this function.");
 }
        public static Int16? RetrieveBirthYear( Int32 subjectTag, Bioparent bioparent, LinksDataSet.tblParentsOfGen1CurrentDataTable dtInput )
        {
            if( dtInput == null ) throw new ArgumentNullException("dtInput");
            if( dtInput.Count <= 0 ) throw new ArgumentException("There should be at least one row in tblParentsOfGen1Current.");

            LinksDataSet.tblParentsOfGen1CurrentRow dr = dtInput.FindBySubjectTag(subjectTag);
            switch( bioparent ) {
                case Bioparent.Mom:
                    if( dr.IsBiomomBirthYearEstimatedNull() ) return null;
                    else return dr.BiomomBirthYearEstimated;
                case Bioparent.Dad:
                    if( dr.IsBiodadBirthYearEstimatedNull() ) return null;
                    else return dr.BiodadBirthYearEstimated;
                default:
                    throw new ArgumentOutOfRangeException("bioparent", bioparent, "The function does not accommodate that bioparent value.");
            }
        }
        public static TrendLineGen0InHH RetrieveTrend( Bioparent bioparent, Int32 subjectTag, LinksDataSet.tblParentsOfGen1RetroDataTable dtRetro )
        {
            //, LinksDataSet.tblSubjectDetailsDataTable dtDetail ) {
            if ( dtRetro == null )
                return new TrendLineGen0InHH(yob: 0, hasAnyRecords: false, everAtHome: false, years: null, values: null, ages: null);
            else if ( dtRetro.Count <= 0 )
                throw new ArgumentException("There should be at least one row in tblParentsOfGen1Retro.");

            string selectYears = string.Format("{0}={1} AND {2}={3}",
                subjectTag, dtRetro.SubjectTagColumn.ColumnName,
                (byte)bioparent, dtRetro.BioparentColumn.ColumnName);
            LinksDataSet.tblParentsOfGen1RetroRow[] drs = (LinksDataSet.tblParentsOfGen1RetroRow[])dtRetro.Select(selectYears);
            Trace.Assert(drs.Length >= 0, "At least zero records should be retrieved from tblParentsOfGen1Retro.");
            Int16 yob = (from dr in drs where dr.Age == 0 select dr.Year).First();

            bool? everInHH;
            if ( drs[0].IsInHHNull() ) everInHH = null;
            else everInHH = drs[0].InHH;

            Int16[] years = new Int16[drs.Length];
            byte[] ages = new byte[drs.Length];
            bool?[] inHHs = new bool?[drs.Length];

            //values = (from dr in drsYes select (YesNo)dr.BiodadInHH).ToArray();
            for ( Int32 i = 0; i < drs.Length; i++ ) {
                years[i] = drs[i].Year;
                ages[i] = drs[i].Age;

                if ( drs[i].IsInHHNull() ) inHHs[i] = null;
                else inHHs[i] = drs[i].InHH;
            }
            //switch ( everInHH ) {
            //   case YesNo.No:
            //      Trace.Assert(drsYes.Length == 0, "If the subject says the bioparent has never lived in the HH, then there shouldn't be any more records.");
            //      break;
            //   case YesNo.Yes:
            //      //This is ok.  Execute the statements following the switch statement.
            //      break;
            //   default:
            //      throw new ArgumentOutOfRangeException("bioparent");
            //}
            return new TrendLineGen0InHH(yob: yob, hasAnyRecords: true, everAtHome: everInHH, years: years, values: inHHs, ages: ages);
        }
        private Int32 ProcessForOneParent( Tristate bothParentsAlways, Bioparent parent,Int16 yob, LinksDataSet.tblSubjectRow drSubject, LinksDataSet.tblResponseDataTable dtExtendedResponse )
        {
            const byte loopIndexForNever = 255;
            byte[] loopIndicesAndAges = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
            Int32 recordsAdded = 0;
            Item item;
            switch ( parent ) {
                case Bioparent.Dad: item = Item.Gen1LivedWithFatherAtAgeX; break;
                case Bioparent.Mom: item = Item.Gen1LivedWithMotherAtAgeX; break;
                default: throw new ArgumentOutOfRangeException("parent");
            }
            Tristate responseEver = DetermineOneParentEverInHH(item, _surveyYear, drSubject.SubjectTag, loopIndexForNever, dtExtendedResponse);

            foreach ( byte loopIndexAndAge in loopIndicesAndAges ) {
                Tristate inHH;
                if ( bothParentsAlways == Tristate.Yes )
                    inHH =	Tristate.Yes;
                else if ( responseEver == Tristate.DoNotKnow )
                    inHH = Tristate.DoNotKnow;
                else if ( responseEver == Tristate.Yes )
                    inHH = DetermineParentInHH(Item.Gen1LivedWithFatherAtAgeX, _surveyYear, drSubject.SubjectTag, loopIndexAndAge, dtExtendedResponse);
                else if ( responseEver == Tristate.No )
                    inHH = Tristate.No;
                else
                    throw new InvalidOperationException("The execution shouldn't have gotten to here in the 'ProcessForOneParent' function.");

                Int16 yearInHH = Convert.ToInt16(yob + loopIndexAndAge);
                AddRow(drSubject.SubjectTag, drSubject.ExtendedID, parent, inHH: inHH, age: loopIndexAndAge, yearInHH: yearInHH);
                recordsAdded += 1; ;
            }
            return recordsAdded;
        }
        private void AddRow( Int32 subjectTag, Int16 extendedID, Bioparent parent, Tristate inHH,  byte age, Int16 yearInHH )
        {
            //lock ( _ds.tblFatherOfGen2 ) {
            LinksDataSet.tblParentsOfGen1RetroRow drNew = _ds.tblParentsOfGen1Retro.NewtblParentsOfGen1RetroRow();
            drNew.SubjectTag = subjectTag;
            drNew.ExtendedID = extendedID;
            drNew.Bioparent = (byte)parent;

            if ( inHH==Tristate.DoNotKnow ) drNew.SetInHHNull();
            else drNew.InHH = (inHH==Tristate.Yes);

            drNew.Age = age;
            drNew.Year = yearInHH;
            _ds.tblParentsOfGen1Retro.AddtblParentsOfGen1RetroRow(drNew);
            //}
        }