
// readGeneticAnalysis provides function to read genetic analyses

package readGeneticAnalysis

import "seekia/internal/encoding"
import "seekia/internal/genetics/geneticReferences/monogenicDiseases"
import "seekia/internal/genetics/geneticReferences/polygenicDiseases"
import "seekia/internal/genetics/geneticReferences/traits"
import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/helpers"

import "errors"


// This converts a person genetic analysis MessagePack to a PersonAnalysis object 
func ReadPersonGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis.PersonAnalysis, error){

	inputAnalysisBytes := []byte(inputAnalysisString)

	var newAnalysisObject geneticAnalysis.PersonAnalysis

	err := encoding.DecodeMessagePackBytes(false, inputAnalysisBytes, &newAnalysisObject)
	if (err != nil) { return geneticAnalysis.PersonAnalysis{}, err }

	return newAnalysisObject, nil
}

// This converts a couple genetic analysis MessagePack to a CoupleAnalysis object 
func ReadCoupleGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis.CoupleAnalysis, error){

	inputAnalysisBytes := []byte(inputAnalysisString)

	var newAnalysisObject geneticAnalysis.CoupleAnalysis

	err := encoding.DecodeMessagePackBytes(false, inputAnalysisBytes, &newAnalysisObject)
	if (err != nil) { return geneticAnalysis.CoupleAnalysis{}, err }

	return newAnalysisObject, nil
}


//Outputs:
//	-[][16]byte: All raw genome identifiers list (excluding combined genomes)
//	-bool: Multiple genomes exist
//	-[16]byte: OnlyExcludeConflicts GenomeIdentifier
//	-[16]byte: OnlyIncludeShared GenomeIdentifier
//	-map[[16]byte]map[int64]locusValue.LocusValue: Genomes locus values map
//	-error
func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, map[[16]byte]map[int64]locusValue.LocusValue, error){

	analysisVersion := inputGeneticAnalysis.AnalysisVersion
	if (analysisVersion != 1){
		// This analysis must have been created by a newer version of Seekia
		// We cannot read it
		return nil, false, [16]byte{}, [16]byte{}, nil, errors.New("Cannot read analysis: Is a newer analysis version.")
	}

	allRawGenomeIdentifiersList := inputGeneticAnalysis.AllRawGenomeIdentifiersList

	genomesMap := inputGeneticAnalysis.GenomesMap

	combinedGenomesExist := inputGeneticAnalysis.CombinedGenomesExist
	if (combinedGenomesExist == false){
		return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, genomesMap, nil
	}

	onlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.OnlyExcludeConflictsGenomeIdentifier
	onlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.OnlyIncludeSharedGenomeIdentifier

	return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, genomesMap, nil
} 

//Outputs:
//	-[16]byte: Pair 1 Person 1 Genome Identifier
//	-[16]byte: Pair 1 Person 2 Genome Identifier
//	-bool: Second Genome pair exists
//	-[16]byte: Pair 2 Person 1 Genome Identifier
//	-[16]byte: Pair 2 Person 2 Genome Identifier
//	-bool: Person 1 Has Multiple Genomes
//	-[16]byte: Person 1 OnlyExcludeConflicts Genome Identifier
//	-[16]byte: Person 1 OnlyIncludeShared Genome Identifier
//	-bool: Person 2 Has Multiple Genomes
//	-[16]byte: Person 2 OnlyExcludeConflicts Genome Identifier
//	-[16]byte: Person 2 OnlyIncludeShared Genome Identifier
//	-error
func GetMetadataFromCoupleGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.CoupleAnalysis)([16]byte, [16]byte, bool, [16]byte, [16]byte, bool, [16]byte, [16]byte, bool, [16]byte, [16]byte, error){

	analysisVersion := inputGeneticAnalysis.AnalysisVersion
	if (analysisVersion != 1){
		// This analysis must have been created by a newer version of Seekia
		// We cannot read it
		return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, errors.New("Cannot read analysis: Is a newer analysis version.")
	}

	pair1Person1GenomeIdentifier := inputGeneticAnalysis.Pair1Person1GenomeIdentifier
	pair1Person2GenomeIdentifier := inputGeneticAnalysis.Pair1Person2GenomeIdentifier

	secondPairExists := inputGeneticAnalysis.SecondPairExists
	if (secondPairExists == false){

		return pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, nil
	}

	pair2Person1GenomeIdentifier := inputGeneticAnalysis.Pair2Person1GenomeIdentifier
	pair2Person2GenomeIdentifier := inputGeneticAnalysis.Pair2Person2GenomeIdentifier

	getPerson1MultipleGenomesInfo := func()(bool, [16]byte, [16]byte, error){

		person1HasMultipleGenomes := inputGeneticAnalysis.Person1HasMultipleGenomes
		if (person1HasMultipleGenomes == false){
			return false, [16]byte{}, [16]byte{}, nil
		}

		person1OnlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.Person1OnlyExcludeConflictsGenomeIdentifier

		person1OnlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.Person1OnlyIncludeSharedGenomeIdentifier

		return true, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, nil
	}

	getPerson2MultipleGenomesInfo := func()(bool, [16]byte, [16]byte, error){

		person2HasMultipleGenomes := inputGeneticAnalysis.Person2HasMultipleGenomes
		if (person2HasMultipleGenomes == false){
			return false, [16]byte{}, [16]byte{}, nil
		}

		person2OnlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.Person2OnlyExcludeConflictsGenomeIdentifier

		person2OnlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.Person2OnlyIncludeSharedGenomeIdentifier

		return true, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil
	}

	person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, err := getPerson1MultipleGenomesInfo()
	if (err != nil) { return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, err }

	person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := getPerson2MultipleGenomesInfo()
	if (err != nil) { return [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, false, [16]byte{}, [16]byte{}, err }

	return pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, true, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, nil
}


// This function will take a couple and person analysis
// It takes in a genome identifier from the couple analysis
// It returns the equivalent genome identifier from the person analysis
// This is needed for combined genomes, because their identifiers are generated randomly for each analysis
//Inputs:
//	-bool: Is person 1
//	-geneticAnalysis.PersonAnalysis: Person 1 Analysis
//	-geneticAnalysis.PersonAnalysis: Person 2 Analysis
//	-geneticAnalysis.CoupleAnalysis: Couple Analysis
//	-[16]byte: Input Genome Identifier (Should be from Couple identifier)
//Outputs:
//	-[16]byte: Genome identifier from person analysis
//	-bool: Person analysis has multiple genomes
//	-bool: Genome is a combined genome
//	-string: Genome combined type ("Only Exclude Conflicts"/"Only Include Shared")
//	-error
func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool, person1AnalysisObject geneticAnalysis.PersonAnalysis, person2AnalysisObject geneticAnalysis.PersonAnalysis, coupleAnalysisObject geneticAnalysis.CoupleAnalysis, inputGenomeIdentifier [16]byte)([16]byte, bool, bool, string, error){

	// We need to figure out which genome identifier the current genome identifier corresponds to within the person analysis
	// If the genome is not a combined genome, then the genome identifier should be identical between each analysis
	// If it is a combined genome, we need to determine which genome it corresponds to

	_, _, secondGenomePairExists, _, _, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject)
	if (err != nil) { return [16]byte{}, false, false, "", err }

	if (isPerson1 == true){

		if (person1HasMultipleGenomes == false){
			// This person does not have multiple genomes. The genome identifier is the same between both analyses
			return inputGenomeIdentifier, false, false, "", nil
		}

		if (secondGenomePairExists == false){
			return inputGenomeIdentifier, true, false, "", nil
		}

		_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject)
		if (err != nil) { return [16]byte{}, false, false, "", err }
		if (multipleGenomesExist == false){
			return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
		}

		if (inputGenomeIdentifier == person1OnlyExcludeConflictsGenomeIdentifier){
			return onlyExcludeConflictsGenomeIdentifier, true, true, "Only Exclude Conflicts", nil
		}
		if (inputGenomeIdentifier == person1OnlyIncludeSharedGenomeIdentifier){
			return onlyIncludeSharedGenomeIdentifier, true, true, "Only Include Shared", nil
		}
		return [16]byte{}, false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.")
	}

	// isPerson1 == false

	if (person2HasMultipleGenomes == false){
		// This person does not have multiple genomes. The genome identifier is the same between both analyses
		return inputGenomeIdentifier, false, false, "", nil
	}

	if (secondGenomePairExists == false){
		return inputGenomeIdentifier, true, false, "", nil
	}

	_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject)
	if (err != nil) { return [16]byte{}, false, false, "", err }
	if (multipleGenomesExist == false){
		return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
	}

	if (inputGenomeIdentifier == person2OnlyExcludeConflictsGenomeIdentifier){
		return onlyExcludeConflictsGenomeIdentifier, true, true, "Only Exclude Conflicts", nil
	}
	if (inputGenomeIdentifier == person2OnlyIncludeSharedGenomeIdentifier){
		return onlyIncludeSharedGenomeIdentifier, true, true, "Only Include Shared", nil
	}

	return [16]byte{}, false, false, "", errors.New("Combined genome identifier from couple analysis does not correspond to either combined genome from person analysis.")
}


//Outputs:
//	-bool: Disease info known
//	-bool: Person has disease
//	-int: Probability of passing a disease variant
//	-string: Probability of passing a disease variant formatted (with % suffix)
//	-int: Quantity of variants tested
//	-int: Quantity of loci tested
//	-int: Quantity of phased loci
//	-bool: Conflict exists
//	-error
func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, bool, int, string, int, int, int, bool, error){

	personMonogenicDiseasesMap := personAnalysisObject.MonogenicDiseasesMap

	personMonogenicDiseaseInfo, exists := personMonogenicDiseasesMap[diseaseName]
	if (exists == false){
		return false, false, 0, "", 0, 0, 0, false, nil
	}

	personMonogenicDiseaseInfoMap := personMonogenicDiseaseInfo.MonogenicDiseaseInfoMap

	genomeMonogenicDiseaseInfo, exists := personMonogenicDiseaseInfoMap[genomeIdentifier]
	if (exists == false){
		return false, false, 0, "", 0, 0, 0, false, nil
	}

	conflictExists := personMonogenicDiseaseInfo.ConflictExists

	personHasDisease := genomeMonogenicDiseaseInfo.PersonHasDisease
	quantityOfVariantsTested := genomeMonogenicDiseaseInfo.QuantityOfVariantsTested
	quantityOfLociTested := genomeMonogenicDiseaseInfo.QuantityOfLociTested
	quantityOfPhasedLoci := genomeMonogenicDiseaseInfo.QuantityOfPhasedLoci
	probabilityOfPassingAVariant := genomeMonogenicDiseaseInfo.ProbabilityOfPassingADiseaseVariant

	probabilityOfPassingAVariantString := helpers.ConvertIntToString(probabilityOfPassingAVariant)

	probabilityOfPassingAVariantFormatted := probabilityOfPassingAVariantString + "%"

	return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, quantityOfVariantsTested, quantityOfLociTested, quantityOfPhasedLoci, conflictExists, nil
}


//Outputs:
//	-bool: Probability Offspring Has Disease Is Known
//	-int: Percentage Probability of offspring having disease
//	-string: Probability of offspring having disease formatted (with % suffix)
//	-bool: Probability Offspring Has Variant Is Known
//	-int: Percentage probability of offspring having a disease variant
//	-string: Percentage probability of offspring having a disease variant formatted (with % suffix)
//	-bool: Conflict exists between genome pairs
//	-error
func GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, bool, int, string, bool, error){

	offspringMonogenicDiseasesMap := coupleAnalysisObject.MonogenicDiseasesMap

	offspringMonogenicDiseaseInfoObject, exists := offspringMonogenicDiseasesMap[diseaseName]
	if (exists == false){
		return false, 0, "", false, 0, "", false, nil
	}

	monogenicDiseaseInfoMap := offspringMonogenicDiseaseInfoObject.MonogenicDiseaseInfoMap

	offspringGenomePairMonogenicDiseaseInfo, exists := monogenicDiseaseInfoMap[genomePairIdentifier]
	if (exists == false){
		return false, 0, "", false, 0, "", false, nil
	}

	conflictExists := offspringMonogenicDiseaseInfoObject.ConflictExists

	probabilityOffspringHasDiseaseIsKnown := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasDiseaseIsKnown
	probabilityOffspringHasDisease := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasDisease
	probabilityOffspringHasVariantIsKnown := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasVariantIsKnown
	probabilityOffspringHasVariant := offspringGenomePairMonogenicDiseaseInfo.ProbabilityOffspringHasVariant

	getProbabilityOffspringHasDiseaseFormatted := func()string{
		if (probabilityOffspringHasDiseaseIsKnown == false){
			return ""
		}

		probabilityOffspringHasDiseaseString := helpers.ConvertIntToString(probabilityOffspringHasDisease)

		probabilityOffspringHasDiseaseFormatted := probabilityOffspringHasDiseaseString + "%"

		return probabilityOffspringHasDiseaseFormatted
	}

	probabilityOffspringHasDiseaseFormatted := getProbabilityOffspringHasDiseaseFormatted()

	getProbabilityOffspringHasVariantFormatted := func()string{
		if (probabilityOffspringHasVariantIsKnown == false){
			return ""
		}

		probabilityOffspringHasVariantString := helpers.ConvertIntToString(probabilityOffspringHasVariant)

		probabilityOffspringHasVariantFormatted := probabilityOffspringHasVariantString + "%"

		return probabilityOffspringHasVariantFormatted
	}

	probabilityOffspringHasVariantFormatted := getProbabilityOffspringHasVariantFormatted()

	return probabilityOffspringHasDiseaseIsKnown, probabilityOffspringHasDisease, probabilityOffspringHasDiseaseFormatted, probabilityOffspringHasVariantIsKnown, probabilityOffspringHasVariant, probabilityOffspringHasVariantFormatted, conflictExists, nil
}


//Outputs:
//	-bool: Number of mutations is known
//	-int: Number of mutations
//	-error
func GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, variantIdentifier [3]byte, genomeIdentifier [16]byte)(bool, int, error){

	personMonogenicDiseasesMap := personAnalysisObject.MonogenicDiseasesMap

	personMonogenicDiseaseInfo, exists := personMonogenicDiseasesMap[diseaseName]
	if (exists == false){
		return false, 0, nil
	}

	personMonogenicDiseaseInfoMap := personMonogenicDiseaseInfo.MonogenicDiseaseInfoMap

	genomeMonogenicDiseaseInfo, exists := personMonogenicDiseaseInfoMap[genomeIdentifier]
	if (exists == false){
		return false, 0, nil
	}

	variantsInfoMap := genomeMonogenicDiseaseInfo.VariantsInfoMap

	variantInfoObject, exists := variantsInfoMap[variantIdentifier]
	if (exists == false){
		// The genome's variant info is unknown for this variant
		return false, 0, nil
	}

	base1HasVariant := variantInfoObject.Base1HasVariant
	base2HasVariant := variantInfoObject.Base2HasVariant

	numberOfMutations := 0

	if (base1HasVariant == true){
		numberOfMutations += 1
	}
	if (base2HasVariant == true){
		numberOfMutations += 1
	}

	return true, numberOfMutations, nil
}

//Outputs:
//	-bool: Offspring Probabilities Known 
//	-int: Lower Bound Percentage Probability of 0 mutations
//	-int: Upper Bound Percentage Probability of 0 mutations
//	-string: Percentage probability of 0 mutations formatted (has % suffix and - between non-identical bounds)
//	-int: Lower Bound Percentage probability of only 1 mutation
//	-int: Upper Bound Percentage probability of only 1 mutation
//	-string: Percentage probability of 1 mutation formatted (has % suffix and - between non-identical bounds)
//	-int: Lower Bound Percentage probability of 2 mutations
//	-int: Upper Bound Percentage probability of 2 mutations
//	-string: Percentage probability of 2 mutations formatted (has % suffix and - between non-identical bounds)
//	-error
func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, variantIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, int, string, int, int, string, int, int, string, error){

	coupleMonogenicDiseasesMap := coupleAnalysisObject.MonogenicDiseasesMap

	coupleMonogenicDiseaseInfo, exists := coupleMonogenicDiseasesMap[diseaseName]
	if (exists == false){
		return false, 0, 0, "", 0, 0, "", 0, 0, "", nil
	}

	coupleMonogenicDiseaseInfoMap := coupleMonogenicDiseaseInfo.MonogenicDiseaseInfoMap

	genomePairMonogenicDiseaseInfo, exists := coupleMonogenicDiseaseInfoMap[genomePairIdentifier]
	if (exists == false){
		return false, 0, 0, "", 0, 0, "", 0, 0, "", nil
	}

	genomePairVariantsInfoMap := genomePairMonogenicDiseaseInfo.VariantsInfoMap

	variantInfoObject, exists := genomePairVariantsInfoMap[variantIdentifier]
	if (exists == false){
		return false, 0, 0, "", 0, 0, "", 0, 0, "", nil
	}

	probabilityOf0MutationsLowerBound := variantInfoObject.ProbabilityOf0MutationsLowerBound
	probabilityOf0MutationsUpperBound := variantInfoObject.ProbabilityOf0MutationsUpperBound

	probabilityOf1MutationLowerBound := variantInfoObject.ProbabilityOf1MutationLowerBound
	probabilityOf1MutationUpperBound := variantInfoObject.ProbabilityOf1MutationUpperBound

	probabilityOf2MutationsLowerBound := variantInfoObject.ProbabilityOf2MutationsLowerBound
	probabilityOf2MutationsUpperBound := variantInfoObject.ProbabilityOf2MutationsUpperBound

	getOffspringProbabilityOf0MutationsFormatted := func()string{

		probabilityOf0MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf0MutationsLowerBound)

		probabilityOf0MutationsLowerBoundFormatted := probabilityOf0MutationsLowerBoundString + "%"	

		if (probabilityOf0MutationsLowerBound == probabilityOf0MutationsUpperBound){
			return probabilityOf0MutationsLowerBoundFormatted
		}

		probabilityOf0MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf0MutationsUpperBound)

		probabilityOf0MutationsUpperBoundFormatted := probabilityOf0MutationsUpperBoundString + "%"

		formattedResult := probabilityOf0MutationsLowerBoundFormatted + " - " + probabilityOf0MutationsUpperBoundFormatted

		return formattedResult
	}

	probabilityOf0MutationsFormatted := getOffspringProbabilityOf0MutationsFormatted()

	getOffspringProbabilityOf1MutationFormatted := func()string{

		probabilityOf1MutationLowerBoundString := helpers.ConvertIntToString(probabilityOf1MutationLowerBound)

		probabilityOf1MutationLowerBoundFormatted := probabilityOf1MutationLowerBoundString + "%"	

		if (probabilityOf1MutationLowerBound == probabilityOf1MutationUpperBound){
			return probabilityOf1MutationLowerBoundFormatted
		}

		probabilityOf1MutationUpperBoundString := helpers.ConvertIntToString(probabilityOf1MutationUpperBound)

		probabilityOf1MutationUpperBoundFormatted := probabilityOf1MutationUpperBoundString + "%"

		formattedResult := probabilityOf1MutationLowerBoundFormatted + " - " + probabilityOf1MutationUpperBoundFormatted

		return formattedResult
	}

	probabilityOf1MutationFormatted := getOffspringProbabilityOf1MutationFormatted()

	getOffspringProbabilityOf2MutationsFormatted := func()string{

		probabilityOf2MutationsLowerBoundString := helpers.ConvertIntToString(probabilityOf2MutationsLowerBound)

		probabilityOf2MutationsLowerBoundFormatted := probabilityOf2MutationsLowerBoundString + "%"	

		if (probabilityOf2MutationsLowerBound == probabilityOf2MutationsUpperBound){
			return probabilityOf2MutationsLowerBoundFormatted
		}

		probabilityOf2MutationsUpperBoundString := helpers.ConvertIntToString(probabilityOf2MutationsUpperBound)

		probabilityOf2MutationsUpperBoundFormatted := probabilityOf2MutationsUpperBoundString + "%"

		formattedResult := probabilityOf2MutationsLowerBoundFormatted + " - " + probabilityOf2MutationsUpperBoundFormatted

		return formattedResult
	}

	probabilityOf2MutationsFormatted := getOffspringProbabilityOf2MutationsFormatted()

	return true, probabilityOf0MutationsLowerBound, probabilityOf0MutationsUpperBound, probabilityOf0MutationsFormatted, probabilityOf1MutationLowerBound, probabilityOf1MutationUpperBound, probabilityOf1MutationFormatted, probabilityOf2MutationsLowerBound, probabilityOf2MutationsUpperBound, probabilityOf2MutationsFormatted, nil
}

//Outputs:
//	-bool: Any analysis exists
//	-int: Predicted risk score (0-10)
//	-map[int]float64: Prediction confidence ranges map
//		-Map Structure: Percentage probability of accurate prediction -> distance of range in both directions from prediction
//	-int: Quantity of loci known
//	-int: Quantity of phased loci
//	-bool: Conflict exists (between any of these results for each genome)
//	-error
func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, map[int]float64, int, int, bool, error){

	personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap

	personDiseaseInfoObject, exists := personPolygenicDiseasesMap[diseaseName]
	if (exists == false){
		return false, 0, nil, 0, 0, false, nil
	}

	personDiseaseInfoMap := personDiseaseInfoObject.PolygenicDiseaseInfoMap
	conflictExists := personDiseaseInfoObject.ConflictExists

	personGenomeDiseaseInfoObject, exists := personDiseaseInfoMap[genomeIdentifier]
	if (exists == false){
		return false, 0, nil, 0, 0, false, nil
	}

	predictedRiskScore := personGenomeDiseaseInfoObject.RiskScore
	confidenceRangesMap := personGenomeDiseaseInfoObject.ConfidenceRangesMap
	quantityOfLociKnown := personGenomeDiseaseInfoObject.QuantityOfLociKnown
	quantityOfPhasedLoci := personGenomeDiseaseInfoObject.QuantityOfPhasedLoci

	return true, predictedRiskScore, confidenceRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, conflictExists, nil
}


//Outputs:
//	-bool: Analysis exists
//	-int: Average offspring risk score (0-10)
//	-map[int]float64: Prediction confidence ranges map
//	-int: Quantity of loci known
//	-int: Quantity of Parental phased loci
//	-[]int: 100 Sample offspring risk scores
//	-bool: Conflict exists (Between this genome pair and other genome pairs)
//	-error
func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, map[int]float64, int, int, []int, bool, error){

	offspringDiseasesMap := coupleAnalysisObject.PolygenicDiseasesMap

	diseaseInfoObject, exists := offspringDiseasesMap[diseaseName]
	if (exists == false){
		return false, 0, nil, 0, 0, nil, false, nil
	}

	diseaseInfoMap := diseaseInfoObject.PolygenicDiseaseInfoMap
	conflictExists := diseaseInfoObject.ConflictExists

	genomePairDiseaseInfoObject, exists := diseaseInfoMap[genomePairIdentifier]
	if (exists == false){
		return false, 0, nil, 0, 0, nil, false, nil
	}

	offspringAverageRiskScore := genomePairDiseaseInfoObject.OffspringAverageRiskScore
	predictionConfidenceRangesMap := genomePairDiseaseInfoObject.PredictionConfidenceRangesMap
	quantityOfLociKnown := genomePairDiseaseInfoObject.QuantityOfLociKnown
	quantityOfParentalPhasedLoci := genomePairDiseaseInfoObject.QuantityOfParentalPhasedLoci
	sampleOffspringRiskScoresList := genomePairDiseaseInfoObject.SampleOffspringRiskScoresList

	return true, offspringAverageRiskScore, predictionConfidenceRangesMap, quantityOfLociKnown, quantityOfParentalPhasedLoci, sampleOffspringRiskScoresList, conflictExists, nil
}

//Outputs:
//	-bool: Neural network exists
//	-bool: Any neural network analysis exists
//	-string: Neural network predicted outcome
//	-int: Prediction confidence
//	-int: Quantity of loci known (Neural network)
//	-int: Quantity of phased loci (Neural network)
//	-bool: Any trait rules exist (Rule-based analysis is possible)
//	-bool: Any Trait Rule known/tested
//	-map[[3]byte]bool: Rule Identifier -> Person passes rule
//	-bool: Predicted outcome exists
//	-string: Predicted outcome
//	-int: Quantity of rules tested
//	-int: Quantity Of Loci Known (Rules)
//	-bool: Conflict exists (between any of these results for each genome)
//	-error
func GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(bool, bool, string, int, int, int, bool, bool, map[[3]byte]bool, bool, string, int, int, bool, error){

	personTraitsMap := personAnalysisObject.DiscreteTraitsMap

	personTraitInfoObject, exists := personTraitsMap[traitName]
	if (exists == false){
		return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("Person trait analysis is missing trait: " + traitName)
	}

	personTraitInfoMap := personTraitInfoObject.TraitInfoMap
	conflictExists := personTraitInfoObject.ConflictExists

	personGenomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
	if (exists == false){
		return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("personTraitInfoMap in Person analysis is missing map for genome identifier.")
	}

	neuralNetworkExists := personGenomeTraitInfoObject.NeuralNetworkExists
	neuralNetworkAnalysisExists := personGenomeTraitInfoObject.NeuralNetworkAnalysisExists

	getNeuralNetworkAnalysisInfo := func()(string, int, int, int){

		if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
			return "", 0, 0, 0
		}

		neuralNetworkAnalysis := personGenomeTraitInfoObject.NeuralNetworkAnalysis

		predictedOutcome := neuralNetworkAnalysis.PredictedOutcome
		predictionConfidence := neuralNetworkAnalysis.PredictionConfidence
		quantityOfLociKnown := neuralNetworkAnalysis.QuantityOfLociKnown
		quantityOfPhasedLoci := neuralNetworkAnalysis.QuantityOfPhasedLoci

		return predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci
	}

	neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork := 
getNeuralNetworkAnalysisInfo()

	anyRulesExist := personGenomeTraitInfoObject.AnyRulesExist
	rulesAnalysisExists := personGenomeTraitInfoObject.RulesAnalysisExists

	getTraitAnalysisInfo := func()(map[[3]byte]bool, bool, string, int, int){

		if (anyRulesExist == false || rulesAnalysisExists == false){
			return nil, false, "", 0, 0
		}

		rulesAnalysisObject := personGenomeTraitInfoObject.RulesAnalysis

		genomePassesRulesMap := rulesAnalysisObject.GenomePassesRulesMap

		predictedOutcomeExists := rulesAnalysisObject.PredictedOutcomeExists

		predictedOutcome := rulesAnalysisObject.PredictedOutcome

		quantityOfRulesTested := rulesAnalysisObject.QuantityOfRulesTested

		quantityOfLociKnown := rulesAnalysisObject.QuantityOfLociKnown

		return genomePassesRulesMap, predictedOutcomeExists, predictedOutcome, quantityOfRulesTested, quantityOfLociKnown
	}

	genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules := getTraitAnalysisInfo()

	return neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
}

//Outputs:
//	-bool: Neural network exists
//	-bool: Neural network analysis exists
//	-map[string]int: Offspring outcome probabilities map for neural network prediction
//		-Map Structure: Outcome name -> Probability of outcome (0-100)
//	-int: Average confidence (for neural network prediction)
//	-int: Quantity of loci known (for neural network)
//	-int: Quantity of Parental phased loci
//	-bool: Any Rules exist
//	-bool: Rules analysis exists
//	-map[string]int: Offspring outcome probabilities map for rules-based prediction
//		-Map Structure: Outcome name -> Probability of outcome (0-100)
//	-map[[3]byte]int: Offspring probability of passing rules map
//		-Map Structure: Rule Identifier -> Probability of passing rule (0-100)
//	-int: Quantity of rules tested
//	-int: Quantity of loci known (For Rules)
//	-bool: Conflict exists (Between this genome pair and other genome pairs)
//	-error
func GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, bool, map[string]int, int, int, int, bool, bool, map[string]int, map[[3]byte]int, int, int, bool, error){

	offspringTraitsMap := coupleAnalysisObject.DiscreteTraitsMap

	traitInfoObject, exists := offspringTraitsMap[traitName]
	if (exists == false){
		return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("offspringTraitsMap missing trait when reading couple genetic analysis: " + traitName)
	}

	traitInfoMap := traitInfoObject.TraitInfoMap
	conflictExists := traitInfoObject.ConflictExists

	genomePairTraitInfoObject, exists := traitInfoMap[genomePairIdentifier]
	if (exists == false){
		return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("traitInfoMap missing trait info for genome pair when reading from genetic analysis.")
	}

	neuralNetworkExists := genomePairTraitInfoObject.NeuralNetworkExists

	neuralNetworkAnalysisExists := genomePairTraitInfoObject.NeuralNetworkAnalysisExists

	getGenomePairTraitNeuralNetworkAnalysisInfo := func()(map[string]int, int, int, int){

		if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
			return nil, 0, 0, 0
		}

		genomePairTraitNeuralNetworkInfo := genomePairTraitInfoObject.NeuralNetworkAnalysis 

		offspringOutcomeProbabilitiesMap := genomePairTraitNeuralNetworkInfo.OffspringOutcomeProbabilitiesMap
		averageConfidence := genomePairTraitNeuralNetworkInfo.AverageConfidence
		quantityOfLociKnown := genomePairTraitNeuralNetworkInfo.QuantityOfLociKnown
		quantityOfParentalPhasedLoci := genomePairTraitNeuralNetworkInfo.QuantityOfParentalPhasedLoci

		return offspringOutcomeProbabilitiesMap, averageConfidence, quantityOfLociKnown, quantityOfParentalPhasedLoci
	}

	offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork := getGenomePairTraitNeuralNetworkAnalysisInfo()

	anyRulesExist := genomePairTraitInfoObject.RulesExist

	rulesAnalysisExists := genomePairTraitInfoObject.RulesAnalysisExists

	getGenomePairTraitRulesAnalysisInfo := func()(map[string]int, map[[3]byte]int, int, int){

		if (anyRulesExist == false || rulesAnalysisExists == false){
			return nil, nil, 0, 0
		}

		genomePairTraitInfo_Rules := genomePairTraitInfoObject.RulesAnalysis 

		offspringOutcomeProbabilitiesMap := genomePairTraitInfo_Rules.OffspringOutcomeProbabilitiesMap
		probabilityOfPassingRulesMap := genomePairTraitInfo_Rules.ProbabilityOfPassingRulesMap
		quantityOfRulesTested := genomePairTraitInfo_Rules.QuantityOfRulesTested
		quantityOfLociKnown := genomePairTraitInfo_Rules.QuantityOfLociKnown

		return offspringOutcomeProbabilitiesMap, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown
	}

	offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules := getGenomePairTraitRulesAnalysisInfo()

	return neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
}


//Outputs:
//	-bool: Rule status is known (we know if the rule is passed or not)
//	-bool: Genome passes rule
//	-error
func GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){

	_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
	if (err != nil) { return false, false, err }
	if (anyRulesExist == false){
		return false, false, errors.New("GetPersonTraitRuleInfoFromGeneticAnalysis called when no trait rules exist.")
	}
	if (rulesAnalysisExists == false){
		return false, false, nil
	}

	genomePassesRule, statusIsKnown := genomePassesRulesMap[ruleIdentifier]
	if (statusIsKnown == false){
		return false, false, nil
	}

	return true, genomePassesRule, nil
}


//Outputs:
//	-bool: Offspring trait rule probability known
//	-int: Offspring probability of passing rule (0 - 100)
//	-string: Offspring probability of passing rule formatted (with % suffix)
//	-error
func GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){

	_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, _, offspringProbabilityOfPassingRulesMap, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
	if (err != nil) { return false, 0, "", err }
	if (anyRulesExist == false){
		return false, 0, "", errors.New("GetOffspringTraitRuleInfoFromGeneticAnalysis called for trait which has no rules: " + traitName)
	}
	if (rulesAnalysisExists == false){
		return false, 0, "", nil
	}

	offspringProbabilityOfPassingRule, exists := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
	if (exists == false){
		return false, 0, "", nil
	}

	offspringProbabilityOfPassingRuleString := helpers.ConvertIntToString(offspringProbabilityOfPassingRule)

	offspringProbabilityOfPassingRuleFormatted := offspringProbabilityOfPassingRuleString + "%"

	return true, offspringProbabilityOfPassingRule, offspringProbabilityOfPassingRuleFormatted, nil
}


//Outputs:
//	-bool: Any analysis exists
//	-float64: Predicted outcome (Example: Height in centimeters)
//	-map[int]float64: Prediction confidence ranges map
//		-Map Structure: Percentage probability of accurate prediction -> distance of range in both directions from prediction
//	-int: Quantity of loci known
//	-int: Quantity of phased loci
//	-bool: Conflict exists (between any of these results for each genome)
//	-error
func GetPersonNumericTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(bool, float64, map[int]float64, int, int, bool, error){

	personTraitsMap := personAnalysisObject.NumericTraitsMap

	personTraitInfoObject, exists := personTraitsMap[traitName]
	if (exists == false){
		return false, 0, nil, 0, 0, false, nil
	}

	personTraitInfoMap := personTraitInfoObject.TraitInfoMap
	conflictExists := personTraitInfoObject.ConflictExists

	personGenomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
	if (exists == false){
		return false, 0, nil, 0, 0, false, nil
	}

	predictedOutcome := personGenomeTraitInfoObject.PredictedOutcome
	confidenceRangesMap := personGenomeTraitInfoObject.ConfidenceRangesMap
	quantityOfLociKnown := personGenomeTraitInfoObject.QuantityOfLociKnown
	quantityOfPhasedLoci := personGenomeTraitInfoObject.QuantityOfPhasedLoci

	return true, predictedOutcome, confidenceRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, conflictExists, nil
}



//Outputs:
//	-bool: Analysis exists
//	-float64: Average offspring outcome
//	-map[int]float64: Prediction confidence ranges map
//	-int: Quantity of loci known
//	-int: Quantity of Parental phased loci
//	-[]float64: 100 Sample offspring outcomes
//	-bool: Conflict exists (Between this genome pair and other genome pairs)
//	-error
func GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, float64, map[int]float64, int, int, []float64, bool, error){

	offspringTraitsMap := coupleAnalysisObject.NumericTraitsMap

	traitInfoObject, exists := offspringTraitsMap[traitName]
	if (exists == false){
		return false, 0, nil, 0, 0, nil, false, nil
	}

	traitInfoMap := traitInfoObject.TraitInfoMap
	conflictExists := traitInfoObject.ConflictExists

	genomePairTraitInfoObject, exists := traitInfoMap[genomePairIdentifier]
	if (exists == false){
		return false, 0, nil, 0, 0, nil, false, nil
	}

	offspringAverageOutcome := genomePairTraitInfoObject.OffspringAverageOutcome
	predictionConfidenceRangesMap := genomePairTraitInfoObject.PredictionConfidenceRangesMap
	quantityOfLociKnown := genomePairTraitInfoObject.QuantityOfLociKnown
	quantityOfParentalPhasedLoci := genomePairTraitInfoObject.QuantityOfParentalPhasedLoci
	sampleOffspringOutcomesList := genomePairTraitInfoObject.SampleOffspringOutcomesList

	return true, offspringAverageOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, quantityOfParentalPhasedLoci, sampleOffspringOutcomesList, conflictExists, nil
}



// We use this function to verify a person genetic analysis is well formed
//TODO: Perform sanity checks on data
func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis)error{

	allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject)
	if (err != nil) { return err }

	allGenomeIdentifiersList := allRawGenomeIdentifiersList
	if (personHasMultipleGenomes == true){
		allGenomeIdentifiersList = append(allGenomeIdentifiersList, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier)
	}

	monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList()
	if (err != nil) { return err }

	for _, monogenicDiseaseObject := range monogenicDiseaseObjectsList{

		diseaseName := monogenicDiseaseObject.DiseaseName

		for _, genomeIdentifier := range allGenomeIdentifiersList{

			_, _, _, _, _, _, _, _, err := GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
			if (err != nil) { return err }
		}

		diseaseVariantObjectsList := monogenicDiseaseObject.VariantsList

		for _, diseaseVariantObject := range diseaseVariantObjectsList{

			variantIdentifierHex := diseaseVariantObject.VariantIdentifier

			variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex)
			if (err != nil) { return err }

			for _, genomeIdentifier := range allGenomeIdentifiersList{

				_, _, err := GetPersonMonogenicDiseaseVariantInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, variantIdentifier, genomeIdentifier)
				if (err != nil) { return err }
			}
		}
	}

	polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
	if (err != nil) { return err }

	for _, diseaseObject := range polygenicDiseaseObjectsList{

		diseaseName := diseaseObject.DiseaseName

		for _, genomeIdentifier := range allGenomeIdentifiersList{

			_, _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
			if (err != nil) { return err }
		}
	}

	traitObjectsList, err := traits.GetTraitObjectsList()
	if (err != nil) { return err }

	for _, traitObject := range traitObjectsList{

		traitName := traitObject.TraitName
		traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric

		if (traitIsDiscreteOrNumeric == "Discrete"){

			for _, genomeIdentifier := range allGenomeIdentifiersList{

				_, _, _, _, _, _, _, _, _, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
				if (err != nil) { return err }
			}

			traitRulesList := traitObject.RulesList

			for _, traitRuleObject := range traitRulesList{

				ruleIdentifierHex := traitRuleObject.RuleIdentifier

				ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
				if (err != nil) { return err }

				for _, genomeIdentifier := range allGenomeIdentifiersList{

					_, _, err := GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
					if (err != nil) { return err }
				}
			}
		} else {

			for _, genomeIdentifier := range allGenomeIdentifiersList{

				_, _, _, _, _, _, err := GetPersonNumericTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
				if (err != nil) { return err }
			}
		}
	}

	return nil
}


// We use this function to verify a couple genetic analysis is well formed
//TODO: Perform sanity checks on data
func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis)error{

	pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier, secondGenomePairExists, pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier, _, _, _, _, _, _, err := GetMetadataFromCoupleGeneticAnalysis(coupleAnalysisObject)
	if (err != nil) { return err }

	pair1GenomeIdentifier := helpers.JoinTwo16ByteArrays(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)

	allGenomePairIdentifiersList := [][32]byte{pair1GenomeIdentifier}

	if (secondGenomePairExists == true){

		pair2GenomeIdentifier := helpers.JoinTwo16ByteArrays(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier)

		allGenomePairIdentifiersList = append(allGenomePairIdentifiersList, pair2GenomeIdentifier)
	}

	monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList()
	if (err != nil) { return err }

	for _, monogenicDiseaseObject := range monogenicDiseaseObjectsList{

		diseaseName := monogenicDiseaseObject.DiseaseName

		for _, genomePairIdentifier := range allGenomePairIdentifiersList{

			_, _, _, _, _, _, _, err = GetOffspringMonogenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier)
			if (err != nil) { return err }
		}

		diseaseVariantObjectsList := monogenicDiseaseObject.VariantsList

		for _, diseaseVariantObject := range diseaseVariantObjectsList{

			variantIdentifierHex := diseaseVariantObject.VariantIdentifier

			variantIdentifier, err := encoding.DecodeHexStringTo3ByteArray(variantIdentifierHex)
			if (err != nil) { return err }

			for _, genomePairIdentifier := range allGenomePairIdentifiersList{

				_, _, _, _, _, _, _, _, _, _, err := GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, variantIdentifier, genomePairIdentifier)
				if (err != nil) { return err }
			}
		}
	}

	polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
	if (err != nil) { return err }

	for _, diseaseObject := range polygenicDiseaseObjectsList{

		diseaseName := diseaseObject.DiseaseName

		for _, genomePairIdentifier := range allGenomePairIdentifiersList{

			_, _, _, _, _, _, _, err := GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject, diseaseName, genomePairIdentifier)
			if (err != nil) { return err }
		}
	}

	traitObjectsList, err := traits.GetTraitObjectsList()
	if (err != nil) { return err }

	for _, traitObject := range traitObjectsList{

		traitName := traitObject.TraitName
		traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric

		if (traitIsDiscreteOrNumeric == "Discrete"){

			for _, genomePairIdentifier := range allGenomePairIdentifiersList{

				_, _, _, _, _, _, _, _, _, _, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
				if (err != nil) { return err }
			}

			traitRulesList := traitObject.RulesList

			for _, traitRuleObject := range traitRulesList{

				ruleIdentifierHex := traitRuleObject.RuleIdentifier

				ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
				if (err != nil) { return err }

				for _, genomePairIdentifier := range allGenomePairIdentifiersList{		

					_, _, _, err := GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
					if (err != nil) { return err }
				}
			}
		} else {

			for _, genomePairIdentifier := range allGenomePairIdentifiersList{

				_, _, _, _, _, _, _, err := GetOffspringNumericTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
				if (err != nil) { return err }
			}

		}
	}

	return nil	
}


