From f92c90e296211fb3c36df1026597b3387dbe8c48 Mon Sep 17 00:00:00 2001 From: timersha Date: Mon, 11 Nov 2024 08:56:09 +0300 Subject: [PATCH] HW2 (cherry picked from commit f59425ac23dea20b40c10dee09b627a1e1068b77) --- hw02_unpack_string/.sync | 0 hw02_unpack_string/go.mod | 2 +- hw02_unpack_string/unpack.go | 93 ++++++++++++++++++++++++++++++- hw02_unpack_string/unpack_test.go | 18 +++--- 4 files changed, 102 insertions(+), 11 deletions(-) delete mode 100644 hw02_unpack_string/.sync diff --git a/hw02_unpack_string/.sync b/hw02_unpack_string/.sync deleted file mode 100644 index e69de29..0000000 diff --git a/hw02_unpack_string/go.mod b/hw02_unpack_string/go.mod index 3b20310..9d8651b 100644 --- a/hw02_unpack_string/go.mod +++ b/hw02_unpack_string/go.mod @@ -1,4 +1,4 @@ -module github.com/fixme_my_friend/hw02_unpack_string +module github.com/timersha/golang-tests/hw02_unpack_string go 1.22 diff --git a/hw02_unpack_string/unpack.go b/hw02_unpack_string/unpack.go index 41f003d..7b3f3fb 100644 --- a/hw02_unpack_string/unpack.go +++ b/hw02_unpack_string/unpack.go @@ -2,11 +2,98 @@ package hw02unpackstring import ( "errors" + "math" + "strconv" + "unicode" ) var ErrInvalidString = errors.New("invalid string") -func Unpack(_ string) (string, error) { - // Place your code here. - return "", nil +func Unpack(input *[]rune, result *[]rune) error { + inputIndex := 0 + resultIndex := 0 + backslash := '\\' + inputLen := len(*input) - 1 + + // validate length + if inputLen == 0 { + return nil + } + + for inputIndex <= inputLen { + r := (*input)[inputIndex] + + // starts with digit + if unicode.IsDigit(r) && inputIndex == 0 { + return ErrInvalidString + } + + inputNextIndex := nextIndex(input, inputIndex) + nextR := (*input)[inputNextIndex] + // finds number + if inputLen != inputNextIndex && unicode.IsDigit(r) && unicode.IsDigit(nextR) { + return ErrInvalidString + } + + // escaped symbol is valid + isValidForEscape := unicode.IsDigit(nextR) || nextR == backslash + if r == backslash && isValidForEscape { + *result = append(*result, nextR) + resultIndex++ + inputIndex += 2 + continue + } + + // escaped symbol is not valid + if r == backslash && !isValidForEscape { + return ErrInvalidString + } + + // repeatedly add rune + isDigit := unicode.IsDigit(r) + if isDigit { + shift := appendRune(result, (*result)[previousIndex(result, resultIndex)], r) + resultIndex += shift + inputIndex++ + continue + } + + // don't append current rune + vl, err := strconv.Atoi(string(nextR)) + if err == nil && vl == 0 { + inputIndex++ + continue + } + + // simple append + *result = append(*result, r) + resultIndex++ + inputIndex++ + } + + return nil +} + +func appendRune(res *[]rune, rn rune, count rune) int { + value, err := strconv.Atoi(string(count)) + if err != nil { + return 0 + } + + for range value - 1 { + *res = append(*res, rn) + } + return value +} + +func nextIndex(sl *[]rune, index int) int { + slLen := math.Max(0, float64(len(*sl)-1)) + nextIndex := index + 1 + return int(math.Min(float64(slLen), float64(nextIndex))) +} + +func previousIndex(sl *[]rune, index int) int { + slLen := math.Max(0, float64(len(*sl)-1)) + previousIndex := index - 1 + return int(math.Min(float64(slLen), math.Max(0, float64(previousIndex)))) } diff --git a/hw02_unpack_string/unpack_test.go b/hw02_unpack_string/unpack_test.go index 9799e18..910c546 100644 --- a/hw02_unpack_string/unpack_test.go +++ b/hw02_unpack_string/unpack_test.go @@ -17,28 +17,32 @@ func TestUnpack(t *testing.T) { {input: "", expected: ""}, {input: "aaa0b", expected: "aab"}, // uncomment if task with asterisk completed - // {input: `qwe\4\5`, expected: `qwe45`}, - // {input: `qwe\45`, expected: `qwe44444`}, - // {input: `qwe\\5`, expected: `qwe\\\\\`}, - // {input: `qwe\\\3`, expected: `qwe\3`}, + {input: `qwe\4\5`, expected: `qwe45`}, + {input: `qwe\45`, expected: `qwe44444`}, + {input: `qwe\\5`, expected: `qwe\\\\\`}, + {input: `qwe\\\3`, expected: `qwe\3`}, } for _, tc := range tests { + var res = make([]rune, 0, 100) tc := tc t.Run(tc.input, func(t *testing.T) { - result, err := Unpack(tc.input) + runes := []rune(tc.input) + err := Unpack(&runes, &res) require.NoError(t, err) - require.Equal(t, tc.expected, result) + require.Equal(t, tc.expected, string(res)) }) } } func TestUnpackInvalidString(t *testing.T) { invalidStrings := []string{"3abc", "45", "aaa10b"} + var res = make([]rune, 0, 100) for _, tc := range invalidStrings { tc := tc t.Run(tc, func(t *testing.T) { - _, err := Unpack(tc) + runes := []rune(tc) + err := Unpack(&runes, &res) require.Truef(t, errors.Is(err, ErrInvalidString), "actual error %q", err) }) }