// helpers provides miscellaneous functions package helpers import "seekia/internal/encoding" import "seekia/internal/identity" import "seekia/internal/translation" import "errors" import "math" import "strconv" import "strings" import "time" import "slices" import "maps" import cryptoRand "crypto/rand" import mathRand "math/rand/v2" //Outputs: // -int: Feet // -float64: Inches // -error func ConvertCentimetersToFeetInches(centimeters float64) (int, float64, error) { if (centimeters < 0){ return 0, 0, errors.New("ConvertCentimetersToFeetInches called with negative centimeters.") } totalInches := centimeters / 2.54 feetFloored := math.Floor(totalInches/12) inches := totalInches - (feetFloored * 12) feetInt := int(feetFloored) return feetInt, inches, nil } //Outputs: // -string: Example: 1 foot, 10 centimeters // -error func ConvertCentimetersToFeetInchesTranslatedString(centimeters float64)(string, error){ inputFeet, inputInches, err := ConvertCentimetersToFeetInches(centimeters) if (err != nil) { return "", errors.New("ConvertCentimetersToFeetInchesTranslatedString called with invalid centimeters.") } getInchUnits := func()string{ if (inputInches == 1){ result := translation.TranslateTextFromEnglishToMyLanguage("inch") return result } inchesTranslated := translation.TranslateTextFromEnglishToMyLanguage("inches") return inchesTranslated } inchUnits := getInchUnits() inputInchesString := ConvertFloat64ToStringRounded(inputInches, 1) if (inputFeet == 0){ formattedResult := inputInchesString + " " + inchUnits return formattedResult, nil } getFeetUnits := func()string{ if (inputFeet <= 1){ result := translation.TranslateTextFromEnglishToMyLanguage("foot") return result } feetTranslated := translation.TranslateTextFromEnglishToMyLanguage("feet") return feetTranslated } feetUnits := getFeetUnits() inputFeetString := ConvertIntToString(inputFeet) formattedResult := inputFeetString + " " + feetUnits + ", " + inputInchesString + " " + inchUnits return formattedResult, nil } func ConvertFeetInchesToCentimeters(feet int, inches float64)(float64, error) { if (feet < 0 || inches < 0){ return 0, errors.New("ConvertFeetInchesToCentimeters called with negative feet/inches.") } totalInches := float64(feet*12) + inches centimeters := totalInches * 2.54 return centimeters, nil } func ConvertKilometersToMiles(kilometers float64)(float64, error){ if (kilometers < 0){ return 0, errors.New("ConvertKilometersToMiles called with negative kilometers.") } miles := kilometers * 0.621371 return miles, nil } func ConvertMilesToKilometers(miles float64)(float64, error){ if (miles < 0){ return 0, errors.New("ConvertMilesToKilometers called with negative miles.") } kilometers := miles * 1.609344 return kilometers, nil } func VerifyLatitude(latitude float64)bool{ if (latitude >= -90 && latitude <= 90){ return true } return false } func VerifyLongitude(longitude float64)bool{ if (longitude >= -180 && longitude <= 180){ return true } return false } func VerifyStringIsFloat(input string)bool{ _, err := ConvertStringToFloat64(input) if (err != nil){ return false } return true } func VerifyStringIsIntWithinRange(inputString string, targetMin int64, targetMax int64)(bool, error){ if (targetMin >= targetMax){ return false, errors.New("VerifyStringIsIntWithinRange called with min greater than or equal to max") } num, err := ConvertStringToInt64(inputString) if (err != nil) { return false, nil } if (num < targetMin || num > targetMax){ return false, nil } return true, nil } func VerifyStringIsFloatWithinRange(inputString string, targetMin float64, targetMax float64)(bool, error){ if (targetMin >= targetMax){ return false, errors.New("VerifyStringIsFloatWithinRange called with min greater than or equal to max") } num, err := ConvertStringToFloat64(inputString) if (err != nil) { return false, nil } if (num < targetMin || num > targetMax){ return false, nil } return true, nil } func CheckIfFloat64IsInteger(float float64)bool{ floatRounded := float64(int64(float)) if (float == floatRounded){ return true } return false } func FloorFloat64ToInt(input float64)(int, error){ flooredFloat := math.Floor(input) if (flooredFloat < -2147483648 || flooredFloat > 2147483647){ return 0, errors.New("FloorFloat64ToInt called with float out of range.") } newInt := int(flooredFloat) return newInt, nil } func FloorFloat64ToInt64(input float64)(int64, error){ flooredFloat := math.Floor(input) if (flooredFloat < -9223372036854775808 || flooredFloat > 9223372036854775807){ return 0, errors.New("FloorFloat64ToInt64 called with float out of range.") } newInt := int64(flooredFloat) return newInt, nil } func CeilFloat64ToInt64(input float64)(int64, error){ ceiledFloat := math.Ceil(input) if (ceiledFloat < -9223372036854775808 || ceiledFloat > 9223372036854775807){ return 0, errors.New("CeilFloat64ToInt64 called with float out of range.") } ceiledInt := int64(ceiledFloat) return ceiledInt, nil } func CeilFloat64ToInt(input float64)(int, error){ ceiledFloat := math.Ceil(input) if (ceiledFloat < -2147483648 || ceiledFloat > 2147483647){ return 0, errors.New("CeilFloat64ToInt called with float out of range.") } ceiledInt := int(ceiledFloat) return ceiledInt, nil } func ConvertFloat32ToString(input float32) string{ result := strconv.FormatFloat(float64(input), 'f', 5, 32) return result } func ConvertFloat64ToString(input float64) string{ result := strconv.FormatFloat(input, 'f', 5, 64) return result } func ConvertFloat64ToStringRounded(input float64, outputPrecision int)string{ if (outputPrecision < 0){ outputPrecision = 0 } converted := strconv.FormatFloat(input, 'f', outputPrecision, 64) if (outputPrecision == 0){ return converted } // If all numbers after the decimal are zero, we will remove them // Example: "5.000" -> "5" beforeDecimal, afterDecimal, decimalFound := strings.Cut(converted, ".") if (decimalFound == false){ return converted } for _, number := range afterDecimal{ if (number != '0'){ return converted } } return beforeDecimal } func ConvertStringToFloat64(input string)(float64, error){ num, err := strconv.ParseFloat(input, 64) if (err != nil){ return 0, errors.New("ConvertStringToFloat64 called with invalid input: " + input) } return num, nil } func ConvertInt64ToInt(input int64)(int, error){ if (input < -2147483648 || input > 2147483647){ return 0, errors.New("ConvertInt64ToInt called with input out of range.") } result := int(input) return result, nil } func ConvertByteToString(input byte) string{ result := ConvertIntToString(int(input)) return result } func ConvertStringToByte(input string)(byte, error){ resultInt, err := strconv.Atoi(input) if (err != nil) { return 0, errors.New("ConvertStringToByte called with invalid input: " + input) } if (resultInt < 0 || resultInt > 255){ return 0, errors.New("ConvertStringToByte called with invalid input: " + input) } result := byte(resultInt) return result, nil } func ConvertNetworkTypeStringToByte(input string)(byte, error){ if (input == "1"){ return 1, nil } if (input == "2"){ return 2, nil } return 0, errors.New("ConvertNetworkTypeStringToByte called with invalid input: " + input) } func VerifyNetworkType(input byte)bool{ if (input != 1 && input != 2){ return false } return true } func ConvertIntToString(input int) string{ str := strconv.Itoa(input) return str } func ConvertInt64ToString(input int64)string{ str := strconv.FormatInt(input, 10) return str } func ConvertStringToInt(input string)(int, error){ result, err := strconv.Atoi(input) if (err != nil) { return 0, errors.New("ConvertStringToInt called with invalid input: " + input) } return result, nil } func ConvertStringToInt64(input string) (int64, error) { result, err := strconv.ParseInt(input, 10, 64) if (err != nil){ return 0, errors.New("ConvertStringToInt64 called with invalid input: " + input) } return result, nil } func ConvertCreationTimeStringToInt64(input string)(int64, error){ inputInt64, err := ConvertStringToInt64(input) if (err != nil) { return 0, errors.New("ConvertCreationTimeStringToInt64 called with invalid input: " + input) } isValid := VerifyCreationTime(inputInt64) if (isValid == false){ return 0, errors.New("ConvertCreationTimeStringToInt64 called with invalid input. Too early: " + input) } return inputInt64, nil } func VerifyCreationTime(input int64)bool{ if (input < 1717000000){ return false } //TODO: Add upper limit return true } func ConvertIntToStringWithCommas(num int)string{ numAsString := ConvertIntToString(num) if (num <= 999){ return numAsString } resultWithCommas := "" finalIndex := len(numAsString)-1 counter := 0 for i := finalIndex; i >= 0; i--{ currentCharacter := string(numAsString[i]) if (counter == 3){ resultWithCommas = currentCharacter + "," + resultWithCommas counter = 1 continue } resultWithCommas = currentCharacter + resultWithCommas counter += 1 } return resultWithCommas } //Outputs: // -string: Content Type ("Profile"/"Attribute"/"Message"/"Review"/"Report"/"Parameters") // -error func GetContentTypeFromContentHash(contentHash []byte)(string, error){ hashLength := len(contentHash) switch hashLength{ case 26:{ return "Message", nil } case 27:{ metadataByte := contentHash[26] if (metadataByte >= 1 && metadataByte <= 6){ return "Attribute", nil } } case 28:{ metadataByte := contentHash[27] if (metadataByte >= 1 && metadataByte <= 6){ return "Profile", nil } } case 29:{ metadataByte := contentHash[28] if (metadataByte >= 1 && metadataByte <= 4){ return "Review", nil } } case 30:{ metadataByte := contentHash[29] if (metadataByte >= 1 && metadataByte <= 4){ return "Report", nil } } case 31:{ return "Parameters", nil } } contentHashHex := encoding.EncodeBytesToHexString(contentHash) return "", errors.New("GetContentTypeFromContentHash called with invalid contentHash: " + contentHashHex) } //Outputs: // -[]byte: Reported hash bytes // -string: Reported hash type (Identity, Profile, Message, or Attribute) // -error func ReadReportedHashString(reportedHash string)([]byte, string, error){ reportedHashBytes, reportedHashType, err := ReadReviewedHashString(reportedHash) if (err != nil){ return nil, "", errors.New("ReadReportedHashString called with invalid reportedHash: " + err.Error()) } return reportedHashBytes, reportedHashType, nil } //Outputs: // -[]byte: Reviewed hash bytes // -string: Reviewed hash type (Identity, Profile, Message, or Attribute) // -error func ReadReviewedHashString(reviewedHash string)([]byte, string, error){ if (len(reviewedHash) == 27){ // Must be identity hash identityHashBytes, _, err := identity.ReadIdentityHashString(reviewedHash) if (err != nil) { return nil, "", errors.New("ReadReviewedHashString called with invalid reviewedHash: " + reviewedHash) } return identityHashBytes[:], "Identity", nil } // All other reviewedHash types are encoded in Hex reviewedHashBytes, err := encoding.DecodeHexStringToBytes(reviewedHash) if (err != nil) { return nil, "", errors.New("ReadReviewedHashString called with invalid reviewedHash: " + reviewedHash) } reviewedType, err := GetReviewedTypeFromReviewedHash(reviewedHashBytes) if (err != nil){ return nil, "", errors.New("ReadReviewedHashString called with invalid reviewedHash: " + reviewedHash) } return reviewedHashBytes, reviewedType, nil } func EncodeReportedHashBytesToString(reportedHash []byte)(string, error){ result, err := EncodeReviewedHashBytesToString(reportedHash) return result, err } func EncodeReviewedHashBytesToString(reviewedHash []byte)(string, error){ reviewedType, err := GetReviewedTypeFromReviewedHash(reviewedHash) if (err != nil){ return "", errors.New("EncodeReviewedHashToString called with invalid reviewedHashBytes: " + err.Error()) } if (reviewedType == "Identity"){ if (len(reviewedHash) != 16){ return "", errors.New("GetReviewedTypeFromReviewedHash returning Identity reviewedType when bytes length is not 16.") } reviewedHashArray := [16]byte(reviewedHash) identityHash, _, err := identity.EncodeIdentityHashBytesToString(reviewedHashArray) if (err != nil) { return "", err } return identityHash, nil } reviewedHashString := encoding.EncodeBytesToHexString(reviewedHash) return reviewedHashString, nil } func GetReportedTypeFromReportedHash(reportedHash []byte)(string, error){ reportedType, err := GetReviewedTypeFromReviewedHash(reportedHash) if (err != nil) { return "", err } return reportedType, nil } func GetReviewedTypeFromReviewedHash(reviewedHash []byte)(string, error){ hashLength := len(reviewedHash) if (hashLength == 16){ metadataByte := reviewedHash[15] if (metadataByte >= 1 && metadataByte <= 3){ return "Identity", nil } } else if (hashLength == 26){ return "Message", nil } else if (hashLength == 27){ metadataByte := reviewedHash[26] if (metadataByte >= 1 && metadataByte <= 6){ return "Attribute", nil } } else if (hashLength == 28){ metadataByte := reviewedHash[27] // We must make sure profile is not disabled // Disabled profiles cannot be reviewed if (metadataByte == 2 || metadataByte == 4 || metadataByte == 6){ return "Profile", nil } } reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash) return "", errors.New("GetReviewedTypeFromReviewedHashBytes called with invalid reviewedHash: " + reviewedHashHex) } func GetNewRandomProfileHash(profileTypeProvided bool, profileType string, isDisabledProvided bool, isDisabled bool)([28]byte, error){ if (profileTypeProvided == true){ if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){ return [28]byte{}, errors.New("GetNewRandomProfileHash called with invalid profileType: " + profileType) } } getMetadataByte := func()(byte, error){ if (profileTypeProvided == false && isDisabledProvided == false){ randomByte, err := GetRandomByteWithinRange(1, 6) if (err != nil) { return 0, err } return randomByte, nil } if (profileTypeProvided == true && isDisabledProvided == false){ randomByte, err := GetRandomByteWithinRange(0, 1) if (err != nil) { return 0, err } if (profileType == "Mate"){ result := 1 + randomByte return result, nil } if (profileType == "Host"){ result := 3 + randomByte return result, nil } if (profileType == "Moderator"){ result := 5 + randomByte return result, nil } } if (profileTypeProvided == false && isDisabledProvided == true){ randomInt := GetRandomIntWithinRange(1, 3) if (randomInt == 1){ // ProfileType == "Mate" if (isDisabled == true){ return 1, nil } return 2, nil } if (randomInt == 2){ // ProfileType == "Host" if (isDisabled == true){ return 3, nil } return 4, nil } // ProfileType == "Moderator" // randomInt == 3 if (isDisabled == true){ return 5, nil } return 6, nil } //profileTypeProvided == true && isDisabledProvided == true if (profileType == "Mate"){ if (isDisabled == true){ return 1, nil } return 2, nil } if (profileType == "Host"){ if (isDisabled == true){ return 3, nil } return 4, nil } // profileType == "Moderator" if (isDisabled == true){ return 5, nil } return 6, nil } metadataByte, err := getMetadataByte() if (err != nil){ return [28]byte{}, err } var profileHash [28]byte _, err = cryptoRand.Read(profileHash[:]) if (err != nil) { return [28]byte{}, err } profileHash[27] = metadataByte return profileHash, nil } func GetNewRandomAttributeHash(profileTypeProvided bool, profileType string, isCanonicalProvided bool, isCanonical bool)([27]byte, error){ if (profileTypeProvided == true){ if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){ return [27]byte{}, errors.New("GetNewRandomAttributeHash called with invalid profileType: " + profileType) } } getMetadataByte := func()(byte, error){ if (profileTypeProvided == false && isCanonicalProvided == false){ randomByte, err := GetRandomByteWithinRange(1, 6) if (err != nil) { return 0, err } return randomByte, nil } if (profileTypeProvided == true && isCanonicalProvided == false){ randomByte, err := GetRandomByteWithinRange(0, 1) if (err != nil) { return 0, err } if (profileType == "Mate"){ result := 1 + randomByte return result, nil } if (profileType == "Host"){ result := 3 + randomByte return result, nil } if (profileType == "Moderator"){ result := 5 + randomByte return result, nil } } if (profileTypeProvided == false && isCanonicalProvided == true){ randomInt := GetRandomIntWithinRange(1, 3) if (randomInt == 1){ // ProfileType == "Mate" if (isCanonical == true){ return 1, nil } return 2, nil } if (randomInt == 2){ // ProfileType == "Host" if (isCanonical == true){ return 3, nil } return 4, nil } // ProfileType == "Moderator" // randomInt == 3 if (isCanonical == true){ return 5, nil } return 6, nil } //profileTypeProvided == true && isCanonicalProvided == true if (profileType == "Mate"){ if (isCanonical == true){ return 1, nil } return 2, nil } if (profileType == "Host"){ if (isCanonical == true){ return 3, nil } return 4, nil } // profileType == "Moderator" if (isCanonical == true){ return 5, nil } return 6, nil } metadataByte, err := getMetadataByte() if (err != nil) { return [27]byte{}, err } var attributeHash [27]byte _, err = cryptoRand.Read(attributeHash[:]) if (err != nil) { return [27]byte{}, err } attributeHash[26] = metadataByte return attributeHash, nil } func GetNewRandomMessageHash()([26]byte, error){ var messageHash [26]byte _, err := cryptoRand.Read(messageHash[:]) if (err != nil) { return [26]byte{}, err } return messageHash, nil } func GetNewRandomReviewHash(reviewTypeProvided bool, reviewType string)([29]byte, error){ getMetadataByte := func()(byte, error){ if (reviewTypeProvided == false){ randomByte, err := GetRandomByteWithinRange(1, 4) if (err != nil) { return 0, err } return randomByte, nil } if (reviewType == "Identity"){ return 1, nil } if (reviewType == "Profile"){ return 2, nil } if (reviewType == "Attribute"){ return 3, nil } if (reviewType == "Message"){ return 4, nil } return 0, errors.New("GetNewRandomReviewHash called with invalid reviewType: " + reviewType) } metadataByte, err := getMetadataByte() if (err != nil){ return [29]byte{}, err } var reviewHash [29]byte _, err = cryptoRand.Read(reviewHash[:]) if (err != nil) { return [29]byte{}, err } reviewHash[28] = metadataByte return reviewHash, nil } func GetNewRandomReportHash(reportTypeProvided bool, reportType string)([30]byte, error){ getMetadataByte := func()(byte, error){ if (reportTypeProvided == false){ randomByte, err := GetRandomByteWithinRange(1, 4) if (err != nil) { return 0, err } return randomByte, nil } if (reportType == "Identity"){ return 1, nil } if (reportType == "Profile"){ return 2, nil } if (reportType == "Attribute"){ return 3, nil } if (reportType == "Message"){ return 4, nil } return 0, errors.New("GetNewRandomReportHash called with invalid reportType: " + reportType) } metadataByte, err := getMetadataByte() if (err != nil){ return [30]byte{}, err } var reportHash [30]byte _, err = cryptoRand.Read(reportHash[:]) if (err != nil) { return [30]byte{}, err } reportHash[29] = metadataByte return reportHash, nil } func GetNewRandomParametersHash()([31]byte, error){ var parametersHash [31]byte _, err := cryptoRand.Read(parametersHash[:]) if (err != nil) { return [31]byte{}, err } return parametersHash, nil } func GetNewRandomBytes(lengthInBytes int)([]byte, error){ if (lengthInBytes == 0){ return nil, errors.New("GetNewRandomBytes called with 0 lengthInBytes.") } randomBytes := make([]byte, lengthInBytes) _, err := cryptoRand.Read(randomBytes[:]) if (err != nil) { return nil, err } return randomBytes, nil } func GetNewRandomDeviceIdentifier()([11]byte, error){ var newArray [11]byte _, err := cryptoRand.Read(newArray[:]) if (err != nil) { return [11]byte{}, err } return newArray, nil } func GetNewRandom16ByteArray()([16]byte, error){ var newArray [16]byte _, err := cryptoRand.Read(newArray[:]) if (err != nil) { return [16]byte{}, err } return newArray, nil } func GetNewRandom32ByteArray()([32]byte, error){ var newArray [32]byte _, err := cryptoRand.Read(newArray[:]) if (err != nil) { return [32]byte{}, err } return newArray, nil } func GetNewRandom24ByteArray()([24]byte, error){ var newArray [24]byte _, err := cryptoRand.Read(newArray[:]) if (err != nil) { return [24]byte{}, err } return newArray, nil } func GetNewRandomHexString(lengthInBytes int)(string, error){ if (lengthInBytes == 0){ return "", errors.New("GetNewRandomHexString called with 0 lengthInBytes.") } randomBytes := make([]byte, lengthInBytes) _, err := cryptoRand.Read(randomBytes[:]) if (err != nil) { return "", err } randomString := encoding.EncodeBytesToHexString(randomBytes) return randomString, nil } func VerifyHexString(expectedLengthInBytes uint32, stringToVerify string)bool{ inputStringBytes, err := encoding.DecodeHexStringToBytes(stringToVerify) if (err != nil) { return false } if (len(inputStringBytes) != int(expectedLengthInBytes)){ return false } return true } func ConvertBoolToYesOrNoString(input bool)string{ if (input == true){ return "Yes" } return "No" } func ConvertYesOrNoStringToBool(input string)(bool, error){ if (input == "Yes"){ return true, nil } if (input == "No"){ return false, nil } return false, errors.New("ConvertYesOrNoStringToBool called with invalid yes/no: " + input) } func DeleteIndexFromStringList(inputList []string, indexToDelete int)([]string, error){ if (len(inputList) == 0){ return nil, errors.New("DeleteIndexFromStringList called with empty list.") } if (indexToDelete < 0){ return nil, errors.New("DeleteIndexFromStringList called with negative indexToDelete.") } finalIndex := len(inputList) - 1 if (indexToDelete > finalIndex){ return nil, errors.New("DeleteIndexFromStringList called with indexToDelete which is too large.") } newList := slices.Delete(inputList, indexToDelete, indexToDelete+1) return newList, nil } //Outputs: // -[]string: New list // -bool: Deleted any items func DeleteAllMatchingItemsFromList[E comparable](inputList []E, itemToDelete E)([]E, bool){ listCopy := slices.Clone(inputList) deletionFunction := func(input E)bool{ if (input == itemToDelete){ return true } return false } newList := slices.DeleteFunc(listCopy, deletionFunction) if (len(newList) == len(inputList)){ return newList, false } return newList, true } func GetSharedItemsOfTwoStringLists(listA []string, listB []string)[]string{ sharedItemsList := make([]string, 0) for _, element := range listA{ containsItem := slices.Contains(listB, element) if (containsItem == true){ sharedItemsList = append(sharedItemsList, element) } } return sharedItemsList } // This function will compare two lists. It does not care about item order. func CheckIfTwoListsContainIdenticalItems[E comparable](listA []E, listB []E)bool{ if (len(listA) != len(listB)){ return false } itemCountMapA := make(map[E]int) for _, item := range listA{ itemCountMapA[item] += 1 } itemCountMapB := make(map[E]int) for _, item := range listB{ itemCountMapB[item] += 1 } areEqual := maps.Equal(itemCountMapA, itemCountMapB) if (areEqual == false){ return false } return true } // This function will split a list into sublists func SplitListIntoSublists[E any](inputList []E, maximumItemsPerSublist int)([][]E, error){ if (maximumItemsPerSublist <= 0){ return nil, errors.New("maximumItemsPerSublist is <= 0") } lengthOfInputList := len(inputList) if (lengthOfInputList <= maximumItemsPerSublist){ result := [][]E{inputList} return result, nil } numberOfSublists := float64(lengthOfInputList)/float64(maximumItemsPerSublist) numberOfSublistsCeil := math.Ceil(numberOfSublists) itemsPerSublist := float64(lengthOfInputList)/numberOfSublistsCeil itemsPerSublistCeiled := math.Ceil(itemsPerSublist) if (itemsPerSublistCeiled > 2147483647){ return nil, errors.New("Items per sublist is out of range.") } sizeOfEachSublist := int(itemsPerSublistCeiled) maximumIndex := lengthOfInputList-1 listOfSublists := make([][]E, 0) currentSublist := make([]E, 0) for index, element := range inputList{ currentSublist = append(currentSublist, element) currentSublistLength := len(currentSublist) if (currentSublistLength == sizeOfEachSublist || index == maximumIndex){ listOfSublists = append(listOfSublists, currentSublist) currentSublist = make([]E, 0) } } return listOfSublists, nil } func AddItemToStringListAndAvoidDuplicate(inputList []string, newItem string)[]string{ for _, element := range inputList{ if (element == newItem){ return inputList } } inputList = append(inputList, newItem) return inputList } // Will combine two lists, and avoid any duplicate entries func CombineTwoListsAndAvoidDuplicates[E comparable](listA []E, listB []E)[]E{ combinedListsMap := make(map[E]struct{}) for _, element := range listA{ combinedListsMap[element] = struct{}{} } for _, element := range listB{ combinedListsMap[element] = struct{}{} } combinedList := GetListOfMapKeys(combinedListsMap) return combinedList } func RemoveDuplicatesFromStringList(inputList []string)[]string{ if (len(inputList) <= 1){ listCopy := slices.Clone(inputList) return listCopy } listAsMap := make(map[string]struct{}) for _, element := range inputList{ listAsMap[element] = struct{}{} } newList := GetListOfMapKeys(listAsMap) return newList } // Outputs: // -bool: Duplicate exists // -E: The first duplicate we found func CheckIfListContainsDuplicates[E comparable](inputList []E)(bool, E){ if (len(inputList) <= 1){ var emptyItem E return false, emptyItem } listMap := make(map[E]struct{}) for index, element := range inputList{ if (index != 0){ _, exists := listMap[element] if (exists == true){ return true, element } } listMap[element] = struct{}{} } var emptyItem E return false, emptyItem } // Uses weak randomness func GetRandomItemFromList[E any](inputList []E)(E, error){ lengthOfList := len(inputList) if (lengthOfList == 0){ var emptyItem E return emptyItem, errors.New("GetRandomItemFromList called with empty list.") } if (lengthOfList == 1){ result := inputList[0] return result, nil } randomIndex := mathRand.IntN(lengthOfList) result := inputList[randomIndex] return result, nil } // This function will return a list containing a map's keys func GetListOfMapKeys[M map[K]V, K comparable, V any](inputMap M)[]K{ newList := make([]K, 0, len(inputMap)) for key, _ := range inputMap{ newList = append(newList, key) } return newList } // This function will return a list containing a map's values func GetListOfMapValues[M map[K]V, K comparable, V any](inputMap M)[]V{ newList := make([]V, 0, len(inputMap)) for _, value := range inputMap{ newList = append(newList, value) } return newList } // This function will return a deep copy of a list of string->string maps func DeepCopyStringToStringMapList(inputMapList []map[string]string)[]map[string]string{ mapListCopy := make([]map[string]string, 0, len(inputMapList)) for _, element := range inputMapList{ elementCopy := maps.Clone(element) mapListCopy = append(mapListCopy, elementCopy) } return mapListCopy } //Uses weak randomness func RandomizeListOrder[E any](inputList []E){ mathRand.Shuffle(len(inputList), func(i int, j int){ inputList[i], inputList[j] = inputList[j], inputList[i] }) } func SortIdentityHashListToUnicodeOrder(inputList [][16]byte)error{ // Map Structure: Identity hash -> Identity hash string identityHashStringsMap := make(map[[16]byte]string) for _, identityHash := range inputList{ identityHashString, _, err := identity.EncodeIdentityHashBytesToString(identityHash) if (err != nil){ identityHashHex := encoding.EncodeBytesToHexString(identityHash[:]) return errors.New("SortIdentityHashListToUnicodeOrder called with invalid identityHash: " + identityHashHex) } identityHashStringsMap[identityHash] = identityHashString } compareFunction := func(identityHash1 [16]byte, identityHash2 [16]byte)int{ if (identityHash1 == identityHash2){ return 0 } identityHash1String, exists := identityHashStringsMap[identityHash1] if (exists == false){ panic("identityHash1 is missing from identityHashStringsMap.") } identityHash2String, exists := identityHashStringsMap[identityHash2] if (exists == false){ panic("identityHash2 is missing from identityHashStringsMap.") } if (identityHash1String < identityHash2String){ return -1 } return 1 } slices.SortFunc(inputList, compareFunction) return nil } func CopyAndSortStringListToUnicodeOrder(inputList []string)[]string{ listCopy := slices.Clone(inputList) slices.Sort(listCopy) return listCopy } func SortStringListToUnicodeOrder(inputList []string){ slices.Sort(inputList) } func GetRandomByteWithinRange(minimum byte, maximum byte)(byte, error){ if (maximum < minimum){ return 0, errors.New("GetRandomByteWithinRange called with maximum < minimum") } randomInt := GetRandomIntWithinRange(int(minimum), int(maximum)) if (randomInt < 0 || randomInt > 255){ return 0, errors.New("GetRandomIntWithinRange returning out of bounds result.") } randomByte := byte(randomInt) return randomByte, nil } // Will return random int between two input numbers // Returns weak randomness func GetRandomIntWithinRange(numA int, numB int)int{ if (numA == numB){ return numA } lesserValue := min(numA, numB) greaterValue := max(numA, numB) differenceBetweenValues := greaterValue - lesserValue randomValue := mathRand.IntN(differenceBetweenValues + 1) resultValue := lesserValue + randomValue return resultValue } // This function will return a bool with the provided probabilityOfTrue // For example, if the probabilityOfTrue is 0.6, then 60% of the time, this function will return true // func GetRandomBoolWithProbability(probabilityOfTrue float64)(bool, error){ if (probabilityOfTrue < 0 || probabilityOfTrue > 1){ probabilityOfTrueString := ConvertFloat64ToString(probabilityOfTrue) return false, errors.New("GetRandomBoolWithProbability called with invalid probabilityOfTrue: " + probabilityOfTrueString) } if (probabilityOfTrue == 0){ return false, nil } if (probabilityOfTrue == 1){ return true, nil } // This function returns a random float between 0-1 randomFloat := mathRand.Float64() if (randomFloat <= probabilityOfTrue){ return true, nil } return false, nil } // Will return random int64 between two input numbers // Returns weak randomness func GetRandomInt64WithinRange(numA int64, numB int64)int64{ if (numA == numB){ return numA } lesserValue := min(numA, numB) greaterValue := max(numA, numB) differenceBetweenValues := greaterValue - lesserValue randomValue := mathRand.Int64N(differenceBetweenValues + 1) resultValue := lesserValue + randomValue return resultValue } func GetRandomBool()bool{ randomNumber := mathRand.IntN(2) if (randomNumber == 1){ return true } return false } // This will replace newline characters with the "⁋" character // It also counts tabs as being 5 characters long //Outputs: // -string: Trimmed/flattened string (or original string if no changes were made) // -bool: Any trimming/flattening occurred // -error func TrimAndFlattenString(inputString string, maximumCharacters int)(string, bool, error){ if (maximumCharacters < 1) { return "", false, errors.New("TrimAndFlattenString called with maximumCharacters which is less than 1.") } // Note that some characters are multiple runes in length. // This is not a perfect estimate, but it will only overestimate the length // All english characters are 1 byte long, so they are also 1 rune long // We treat tabs as being 5 characters long tabsReplacedString := strings.ReplaceAll(inputString, "\t", " ") getFlattenedString := func()string{ containsNewlines := strings.Contains(tabsReplacedString, "\n") if (containsNewlines == true){ // String must be flattened newlinesReplacedString := strings.ReplaceAll(tabsReplacedString, "\n", "⁋") return newlinesReplacedString } return tabsReplacedString } flattenedString := getFlattenedString() numberOfCharacters := len([]rune(flattenedString)) if (numberOfCharacters <= maximumCharacters){ // No trimming is needed. return flattenedString, false, nil } stringCharactersList := []rune(flattenedString) outputString := string(stringCharactersList[:maximumCharacters]) outputStringWithEllipsis := outputString + "..." return outputStringWithEllipsis, true, nil } func TranslateAndJoinStringListItems(inputList []string, delimiter string)string{ if (len(inputList) == 0){ return "" } // We use this to build the output var outputBuilder strings.Builder finalIndex := len(inputList) - 1 for index, element := range inputList{ elementTranslated := translation.TranslateTextFromEnglishToMyLanguage(element) outputBuilder.WriteString(elementTranslated) if (index != finalIndex){ outputBuilder.WriteString(delimiter) } } result := outputBuilder.String() return result } // Show remainder will include two units, such as "5 days 10 hours" or "3 months 2 weeks" func ConvertUnixTimeDurationToUnitsTimeTranslated(unixDuration int64, showRemainder bool)(string, error){ // TODO: Add translation getDurationWithRemainder := func()(string, int64){ if (unixDuration <= 0) { return "0 seconds", 0 } minuteUnix := int64(60) if (unixDuration < minuteUnix){ numSeconds := unixDuration durationSecondsString := ConvertInt64ToString(numSeconds) if (durationSecondsString == "1"){ return durationSecondsString + " Second", 0 } return durationSecondsString + " Seconds", 0 } hourUnix := int64(3600) if (unixDuration < hourUnix){ numMinutes := unixDuration/minuteUnix remainderTime := unixDuration - (numMinutes * minuteUnix) durationMinutesString := ConvertInt64ToString(numMinutes) if (durationMinutesString == "1"){ return durationMinutesString + " Minute", remainderTime } return durationMinutesString + " Minutes", remainderTime } dayUnix := int64(86400) if (unixDuration < dayUnix){ numHours := unixDuration/hourUnix remainderTime := unixDuration - (numHours * hourUnix) if (remainderTime < minuteUnix){ remainderTime = 0 } durationHoursString := ConvertInt64ToString(numHours) if (durationHoursString == "1"){ return durationHoursString + " Hour", remainderTime } return durationHoursString + " Hours", remainderTime } weekUnix := int64(604800) if (unixDuration < weekUnix){ numDays := unixDuration/dayUnix remainderTime := unixDuration - (numDays * dayUnix) if (remainderTime < hourUnix){ remainderTime = 0 } durationDaysString := ConvertInt64ToString(numDays) if (durationDaysString == "1"){ return durationDaysString + " Day", remainderTime } return durationDaysString + " Days", remainderTime } monthUnix := int64(2629743) if (unixDuration < monthUnix){ numWeeks := unixDuration/weekUnix remainderTime := unixDuration - (numWeeks * weekUnix) if (remainderTime < dayUnix){ remainderTime = 0 } durationWeeksString := ConvertInt64ToString(numWeeks) if (durationWeeksString == "1"){ return durationWeeksString + " Week", remainderTime } return durationWeeksString + " Weeks", remainderTime } yearUnix := int64(31556926) if (unixDuration < yearUnix) { numMonths := unixDuration/monthUnix remainderTime := unixDuration - (numMonths * monthUnix) if (remainderTime < dayUnix){ remainderTime = 0 } durationMonthsString := ConvertInt64ToString(numMonths) if (durationMonthsString == "1"){ return durationMonthsString + " Month", remainderTime } return durationMonthsString + " Months", remainderTime } numYears := unixDuration/yearUnix remainderTime := unixDuration - (numYears * yearUnix) if (remainderTime < dayUnix || numYears > 1){ remainderTime = 0 } durationYearsString := ConvertInt64ToString(numYears) if (durationYearsString == "1"){ return durationYearsString + " Year", remainderTime } return durationYearsString + " Years", remainderTime } timeTranslated, remainder := getDurationWithRemainder() if (showRemainder == false){ return timeTranslated, nil } if (remainder == 0){ return timeTranslated, nil } remainderString, err := ConvertUnixTimeDurationToUnitsTimeTranslated(remainder, false) if (err != nil) { return "", err } andTranslated := translation.TranslateTextFromEnglishToMyLanguage("and") // Example of resultWithRemainder: "1 week and 5 days" resultWithRemainder := timeTranslated + " " + andTranslated + " " + remainderString return resultWithRemainder, nil } func ConvertUnixTimeToTimeAgoTranslated(inputUnixTime int64, showRemainderTime bool)(string, error){ currentTime := time.Now().Unix() if (inputUnixTime > currentTime){ return "", errors.New("ConvertUnixTimeToTimeAgoTranslated called with invalid date: Time is not in the past.") } durationUnix := currentTime - inputUnixTime durationString, err := ConvertUnixTimeDurationToUnitsTimeTranslated(durationUnix, showRemainderTime) if (err != nil) { return "", err } agoTranslated := translation.TranslateTextFromEnglishToMyLanguage("Ago") durationStringAgo := durationString + " " + agoTranslated return durationStringAgo, nil } func ConvertUnixTimeToTimeFromNowTranslated(inputUnixTime int64, showRemainderTime bool)(string, error){ currentTime := time.Now().Unix() getDurationUnixAndSuffix := func()(int64, string){ if (currentTime >= inputUnixTime){ // inputUnixTime is in the past durationUnix := currentTime - inputUnixTime return durationUnix, "Ago" } // inputUnixTime is in the future durationUnix := inputUnixTime - currentTime return durationUnix, "In The Future" } durationUnix, suffix := getDurationUnixAndSuffix() durationString, err := ConvertUnixTimeDurationToUnitsTimeTranslated(durationUnix, showRemainderTime) if (err != nil) { return "", err } suffixTranslated := translation.TranslateTextFromEnglishToMyLanguage(suffix) timeFromNowTranslated := durationString + " " + suffixTranslated return timeFromNowTranslated, nil } // This will return time in the the following format: February 14, 2023 at 2:14. // It returns 24 hour time func ConvertUnixTimeToTranslatedTime(inputTime int64)string{ timeObject := time.Unix(inputTime, 0) timeYear, timeMonth, timeDay := timeObject.Date() timeYearString := ConvertIntToString(timeYear) timeMonthString := translation.TranslateTextFromEnglishToMyLanguage(timeMonth.String()) timeDayString := ConvertIntToString(timeDay) atTranslated := translation.TranslateTextFromEnglishToMyLanguage("at") timeHour, timeMinute, _ := timeObject.Clock() timeHourString := ConvertIntToString(timeHour) getTimeMinuteStringFormatted := func()string{ timeMinuteString := ConvertIntToString(timeMinute) // We have to convert times such as 11:0 -> 11:00 if (timeMinute < 10){ result := "0" + timeMinuteString return result } return timeMinuteString } timeMinuteStringFormatted := getTimeMinuteStringFormatted() timeString := timeMonthString + " " + timeDayString + ", " + timeYearString + " " + atTranslated + " " + timeHourString + ":" + timeMinuteStringFormatted return timeString } func ConvertFloat64ToRoundedStringWithTranslatedUnits(inputFloat float64)(string, error){ if (inputFloat < 0){ return "", errors.New("ConvertFloat64ToRoundedStringWithTranslatedUnits called with negative input.") } if (inputFloat < 1000){ result := ConvertInt64ToString(int64(inputFloat)) return result, nil } getUnits := func()(float64, string){ millionFloat := float64(1000000) if (inputFloat < millionFloat){ divided := inputFloat / 1000 return divided, "thousand" } billionFloat := float64(1000000000) if (inputFloat < billionFloat){ divided := inputFloat / millionFloat return divided, "million" } trillionFloat := float64(1000000000000) if (inputFloat < trillionFloat){ divided := inputFloat / billionFloat return divided, "billion" } quadrillionFloat := float64(1000000000000000) if (inputFloat < quadrillionFloat){ divided := inputFloat / trillionFloat return divided, "trillion" } quintillionFloat := float64(1000000000000000000) if (inputFloat < quintillionFloat){ divided := inputFloat / quadrillionFloat return divided, "quadrillion" } sextillionFloat := float64(1000000000000000000000) if (inputFloat < sextillionFloat){ divided := inputFloat / quintillionFloat return divided, "quintillion" } septillionFloat := float64(1000000000000000000000000) if (inputFloat < septillionFloat){ divided := inputFloat / sextillionFloat return divided, "sextillion" } divided := inputFloat / septillionFloat return divided, "septillion" } dividedResult, unitsString := getUnits() unitsTranslated := translation.TranslateTextFromEnglishToMyLanguage(unitsString) resultString := ConvertFloat64ToStringRounded(dividedResult, 1) hasSuffix := strings.HasSuffix(resultString, ".0") if (hasSuffix == true){ resultTrimmed := strings.TrimSuffix(resultString, ".0") resultWithUnits := resultTrimmed + " " + unitsTranslated return resultWithUnits, nil } resultWithUnits := resultString + " " + unitsTranslated return resultWithUnits, nil } // This function takes an int and the min and max range of that int // It returns an int scaled between a new min and max func ScaleIntProportionally(ascending bool, input int, inputMin int, inputMax int, newMin int, newMax int)(int, error){ if (inputMin == inputMax) { return inputMin, nil } if (inputMin > inputMax) { return 0, errors.New("ScaleIntProportionally error: InputMin is greater than inputMax") } if (input < inputMin) { return 0, errors.New("ScaleIntProportionally error: Input is less than inputMin") } if (input > inputMax) { return 0, errors.New("ScaleIntProportionally error: Input is greater than inputMax") } if (newMin == newMax) { return newMin, nil } if (newMin > newMin){ return 0, errors.New("ScaleIntProportionally error: newMin is greater than newMin.") } inputRangePortionLength := input - inputMin inputRangeDistance := inputMax - inputMin inputRangePortion := float64(inputRangePortionLength)/float64(inputRangeDistance) // This represents the portion of our output range that we want to travel across getOutputRangePortion := func()float64{ if (ascending == true){ return inputRangePortion } outputRangePortion := 1 - inputRangePortion return outputRangePortion } outputRangePortion := getOutputRangePortion() outputRangeDistance := newMax - newMin outputRangePortionLength := float64(outputRangeDistance) * outputRangePortion resultFloat64 := float64(newMin) + outputRangePortionLength result := int(math.Floor(resultFloat64)) return result, nil } // This function takes an float64 and the min and max range of that float64 // It returns a float64 scaled between a new min and max func ScaleFloat64Proportionally(ascending bool, input float64, inputMin float64, inputMax float64, newMin float64, newMax float64)(float64, error){ if (inputMin == inputMax) { return inputMin, nil } if (inputMin > inputMax) { return 0, errors.New("ScaleFloat64Proportionally error: InputMin is greater than inputMax") } if (input < inputMin) { return 0, errors.New("ScaleFloat64Proportionally error: Input is less than inputMin") } if (input > inputMax) { return 0, errors.New("ScaleFloat64Proportionally error: Input is greater than inputMax") } if (newMin == newMax) { return newMin, nil } if (newMin > newMin){ return 0, errors.New("ScaleFloat64Proportionally error: newMin is greater than newMin.") } inputRangePortionLength := input - inputMin inputRangeDistance := inputMax - inputMin inputRangePortion := inputRangePortionLength/inputRangeDistance // This represents the portion of our output range that we want to travel across getOutputRangePortion := func()float64{ if (ascending == true){ return inputRangePortion } outputRangePortion := 1 - inputRangePortion return outputRangePortion } outputRangePortion := getOutputRangePortion() outputRangeDistance := newMax - newMin outputRangePortionLength := outputRangeDistance * outputRangePortion result := newMin + outputRangePortionLength return result, nil } func XORTwo32ByteArrays(array1 [32]byte, array2 [32]byte)[32]byte{ var newArray [32]byte for i := 0; i < 32; i++ { newArray[i] = array1[i] ^ array2[i] } return newArray } func CheckIfStringContainsTabsOrNewlines(inputString string)bool{ for _, element := range inputString{ if (element == '\r' || element == '\n' || element == '\t') { return true } } return false } func JoinTwo16ByteArrays(inputArray1 [16]byte, inputArray2 [16]byte)[32]byte{ arraysJoinedSlice := slices.Concat(inputArray1[:], inputArray2[:]) arraysJoined := [32]byte(arraysJoinedSlice) return arraysJoined } func Split32ByteArrayInHalf(inputArray [32]byte)([16]byte, [16]byte){ piece1 := [16]byte(inputArray[:16]) piece2 := [16]byte(inputArray[16:]) return piece1, piece2 } // This function takes a list of ints and a target value, and returns the int in the list that is the closest to that value // If there is a tie, the function returns the earliest item in the list of the tied elements func GetClosestIntInList(inputList []int, targetValue int)(int, error){ if (len(inputList) == 0){ return 0, errors.New("GetClosestIntInList called with empty inputList.") } closestValue := 0 closestValueDistance := float64(0) for index, element := range inputList{ if (element == targetValue){ return element, nil } distance := math.Abs(float64(element - targetValue)) if (index == 0 || distance < closestValueDistance){ closestValue = element closestValueDistance = distance } } return closestValue, nil } func CountMatchingElementsInSlice[E comparable](inputSlice []E, inputElement E)int{ counter := 0 for _, element := range inputSlice{ if (element == inputElement){ counter += 1 } } return counter } func CheckIfAllItemsInSliceAreIdentical[E comparable](inputSlice []E)bool{ if (len(inputSlice) <= 1){ return true } initialElement := inputSlice[0] for index, element := range inputSlice{ if (index == 0){ continue } if (element != initialElement){ return false } } return true }