2008 lines
44 KiB
Go
2008 lines
44 KiB
Go
|
|
// 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.")
|
|
}
|
|
|
|
getFeetUnits := func()string{
|
|
|
|
if (inputFeet <= 1){
|
|
result := translation.TranslateTextFromEnglishToMyLanguage("foot")
|
|
|
|
return result
|
|
}
|
|
|
|
feetTranslated := translation.TranslateTextFromEnglishToMyLanguage("feet")
|
|
|
|
return feetTranslated
|
|
}
|
|
|
|
feetUnits := getFeetUnits()
|
|
|
|
getInchUnits := func()string{
|
|
|
|
if (inputInches == 1){
|
|
result := translation.TranslateTextFromEnglishToMyLanguage("inch")
|
|
return result
|
|
}
|
|
|
|
inchesTranslated := translation.TranslateTextFromEnglishToMyLanguage("inches")
|
|
|
|
return inchesTranslated
|
|
}
|
|
|
|
inchUnits := getInchUnits()
|
|
|
|
inputFeetString := ConvertIntToString(inputFeet)
|
|
inputInchesString := ConvertFloat64ToStringRounded(inputInches, 1)
|
|
|
|
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 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 ConvertBroadcastTimeStringToInt64(input string)(int64, error){
|
|
|
|
inputInt64, err := ConvertStringToInt64(input)
|
|
if (err != nil) {
|
|
return 0, errors.New("ConvertBroadcastTimeStringToInt64 called with invalid input: " + input)
|
|
}
|
|
|
|
isValid := VerifyBroadcastTime(inputInt64)
|
|
if (isValid == false){
|
|
return 0, errors.New("ConvertBroadcastTimeStringToInt64 called with invalid input. Too early: " + input)
|
|
}
|
|
|
|
return inputInt64, nil
|
|
}
|
|
|
|
func VerifyBroadcastTime(input int64)bool{
|
|
|
|
if (input < 1680000000){
|
|
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)
|
|
|
|
if (hashLength == 26){
|
|
|
|
return "Message", nil
|
|
|
|
} else if (hashLength == 27){
|
|
|
|
metadataByte := contentHash[26]
|
|
|
|
if (metadataByte >= 1 && metadataByte <= 6){
|
|
|
|
return "Attribute", nil
|
|
}
|
|
|
|
} else if (hashLength == 28){
|
|
|
|
metadataByte := contentHash[27]
|
|
|
|
if (metadataByte >= 1 && metadataByte <= 6){
|
|
|
|
return "Profile", nil
|
|
}
|
|
|
|
} else if (hashLength == 29){
|
|
|
|
metadataByte := contentHash[28]
|
|
|
|
if (metadataByte >= 1 && metadataByte <= 4){
|
|
|
|
return "Review", nil
|
|
}
|
|
|
|
} else if (hashLength == 30){
|
|
|
|
metadataByte := contentHash[29]
|
|
|
|
if (metadataByte >= 1 && metadataByte <= 4){
|
|
|
|
return "Report", nil
|
|
}
|
|
|
|
} else if (hashLength == 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 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 DeleteAllMatchingItemsFromStringList(inputList []string, itemToDelete string)([]string, bool){
|
|
|
|
listCopy := slices.Clone(inputList)
|
|
|
|
deletionFunction := func(input string)bool{
|
|
if (input == itemToDelete){
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
newList := slices.DeleteFunc(listCopy, deletionFunction)
|
|
|
|
if (len(newList) == len(inputList)){
|
|
return newList, false
|
|
}
|
|
|
|
return newList, true
|
|
}
|
|
|
|
//Outputs:
|
|
// -[]string: New list
|
|
// -bool: Deleted any items
|
|
func DeleteAllMatchingItemsFromProfileHashList(inputList [][28]byte, itemToDelete [28]byte)([][28]byte, bool){
|
|
|
|
listCopy := slices.Clone(inputList)
|
|
|
|
deletionFunction := func(input [28]byte)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(identityHashA [16]byte, identityHashB [16]byte)int{
|
|
|
|
if (identityHashA == identityHashB){
|
|
return 0
|
|
}
|
|
|
|
identityHashAString, exists := identityHashStringsMap[identityHashA]
|
|
if (exists == false){
|
|
panic("identityHashA is missing from identityHashStringsMap.")
|
|
}
|
|
|
|
identityHashBString, exists := identityHashStringsMap[identityHashB]
|
|
if (exists == false){
|
|
panic("identityHashB is missing from identityHashStringsMap.")
|
|
}
|
|
|
|
if (identityHashAString < identityHashBString){
|
|
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 a number and the min and max range of that number
|
|
// It returns a number scaled between a new min and max
|
|
func ScaleNumberProportionally(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("ScaleNumberProportionally error: InputMin is greater than inputMax")
|
|
}
|
|
|
|
if (input < inputMin) {
|
|
return 0, errors.New("ScaleNumberProportionally error: Input is less than inputMin")
|
|
}
|
|
if (input > inputMax) {
|
|
return 0, errors.New("ScaleNumberProportionally error: Input is greater than inputMax")
|
|
}
|
|
|
|
if (newMin == newMax) {
|
|
return newMin, nil
|
|
}
|
|
if (newMin > newMin){
|
|
return 0, errors.New("ScaleNumberProportionally 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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|