├── .github └── workflows │ ├── directory.jule │ ├── directory.yml │ └── tests.yml ├── .gitignore ├── DIRECTORY.md ├── LICENSE ├── README.md ├── jule.mod ├── math ├── abs.jule ├── abs_test.jule ├── fact.jule ├── fact_test.jule ├── fib.jule ├── fib_test.jule ├── max.jule ├── max_test.jule ├── median.jule ├── median_test.jule ├── min.jule ├── min_test.jule ├── q_rsqrt.jule ├── q_rsqrt_test.jule ├── sum.jule └── sum_test.jule ├── search ├── binary.jule ├── binary_test.jule ├── error.jule ├── interpolation.jule ├── interpolation_test.jule ├── jump.jule ├── jump2.jule ├── jump2_test.jule ├── jump_test.jule ├── linear.jule ├── linear_test.jule └── testcases_test.jule ├── sort ├── binaryinsertion.jule ├── bubble.jule ├── bucket.jule ├── circle.jule ├── cocktail.jule ├── comb.jule ├── count.jule ├── cycle.jule ├── exchange.jule ├── insertion.jule ├── merge.jule ├── pancake.jule ├── quicksort.jule ├── shell.jule └── testcases_test.jule └── strings ├── issubsequence.jule ├── issubsequence_test.jule ├── reverse.jule └── reverse_test.jule /.github/workflows/directory.jule: -------------------------------------------------------------------------------- 1 | use "std/os" 2 | use "std/os/filepath" 3 | use "std/slices" 4 | use "std/strings" 5 | 6 | // Buffer for the table. 7 | static mut Table = strings::Builder{} 8 | 9 | // Path strings should be formatted by markdown URLs. 10 | static IgnorePaths: [...]str = [ 11 | "search/error.jule", 12 | ] 13 | 14 | static IgnorePackages: [...]str = [ 15 | ".", 16 | "..", 17 | ".github", 18 | ".git", 19 | ] 20 | 21 | fn workflowFailed(message: str) { 22 | const FailExitCode = 1 23 | println(message) 24 | os::Exit(FailExitCode) 25 | } 26 | 27 | fn write(text: str) { Table.WriteStr(text)! } 28 | 29 | fn writeLine(text: str) { 30 | Table.WriteStr(text)! 31 | Table.WriteByte('\n')! 32 | } 33 | 34 | // Subdirectories comes first and ordered by the strings::Compare function. 35 | // Implementation files comes after subdirectories and ordered by the strings::Compare function. 36 | fn sortDirents(mut &dirents: []os::DirEntry) { 37 | slices::SortFunc(dirents, fn(a: os::DirEntry, b: os::DirEntry): int { 38 | if a.Stat.IsDir() && !b.Stat.IsDir() { 39 | ret -1 40 | } 41 | if !a.Stat.IsDir() && b.Stat.IsDir() { 42 | ret +1 43 | } 44 | ret strings::Compare(a.Name, b.Name) 45 | }) 46 | } 47 | 48 | fn makeName(mut s: str): str { 49 | // Remove all cutset substrings. 50 | let cutsetSubs = [ 51 | ".jule", 52 | ".", 53 | "/", 54 | "\\", 55 | ] 56 | for _, subs in cutsetSubs { 57 | s = strings::Replace(s, subs, "", -1) 58 | } 59 | 60 | // Replace all underlines with space. 61 | s = strings::Replace(s, "_", " ", -1) 62 | 63 | // Make uppercase first character of all words. 64 | let mut i = 0 65 | let mut waitSpace = false 66 | let mut sb = []byte(s) 67 | for i < len(sb); i++ { 68 | let b = sb[i] 69 | if waitSpace { 70 | if b == ' ' { 71 | waitSpace = false 72 | } 73 | continue 74 | } 75 | sb[i] = 'Z' - ('z' - b) 76 | waitSpace = true 77 | } 78 | ret str(sb) 79 | } 80 | 81 | fn isIgnorePackage(path: str): bool { 82 | for _, ip in IgnorePackages { 83 | if ip == path { 84 | ret true 85 | } 86 | } 87 | ret false 88 | } 89 | 90 | fn isIgnorePath(path: str): bool { 91 | for _, ip in IgnorePaths { 92 | if ip == path { 93 | ret true 94 | } 95 | } 96 | ret false 97 | } 98 | 99 | fn createDirectoryMd() { 100 | const MdPath = "./DIRECTORY.md" 101 | os::WriteFile(MdPath, unsafe { Table.Buf() }, 0o660) else { 102 | workflowFailed("a problem occurs when creating " + MdPath) 103 | } 104 | } 105 | 106 | fn walkPackage(package: str, path: str, indent: int) { 107 | let mut dirents = os::ReadDir(path) else { 108 | workflowFailed("directory did not readed") 109 | ret // Avoid error. 110 | } 111 | sortDirents(dirents) 112 | for _, d in dirents { 113 | if isIgnorePackage(d.Name) { 114 | continue 115 | } 116 | if strings::HasSuffix(d.Name, "_test.jule") { 117 | // ignore test files 118 | continue 119 | } 120 | 121 | // add file or directory as table entry 122 | url := path + "/" + d.Name 123 | tableName := makeName(d.Name) 124 | if isIgnorePath(url) { 125 | continue 126 | } 127 | 128 | write(strings::Repeat(" ", indent)) 129 | write("- [") 130 | write(tableName) 131 | write("](") 132 | write(url) 133 | writeLine(")") 134 | 135 | if d.Stat.IsDir() { 136 | // append entries of subdirectory 137 | walkPackage(package, filepath::Join(path, d.Name), indent+1) 138 | } 139 | } 140 | } 141 | 142 | fn main() { 143 | writeLine("# Table of Contents") 144 | let mut dirents = os::ReadDir(".") else { 145 | workflowFailed("directory did not readed") 146 | ret // Avoid error. 147 | } 148 | sortDirents(dirents) 149 | for _, d in dirents { 150 | if isIgnorePackage(d.Name) { 151 | continue 152 | } 153 | if d.Stat.IsDir() { 154 | let packageTableName = makeName(d.Name) 155 | write("\n## ") 156 | writeLine(packageTableName) 157 | walkPackage(d.Name, filepath::Join(".", d.Name), 0) 158 | } 159 | } 160 | createDirectoryMd() 161 | } 162 | -------------------------------------------------------------------------------- /.github/workflows/directory.yml: -------------------------------------------------------------------------------- 1 | name: Directory writer 2 | on: 3 | schedule: 4 | # ┌───────────── minute (0 - 59) 5 | # │ ┌───────────── hour (0 - 23) 6 | # │ │ ┌───────────── day of the month (1 - 31) 7 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 8 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 9 | # │ │ │ │ │ 10 | # │ │ │ │ │ 11 | # │ │ │ │ │ 12 | # * * * * * 13 | - cron: '0 0 * * *' 14 | workflow_dispatch: 15 | 16 | jobs: 17 | build: 18 | if: github.repository == 'TheAlgorithms/Jule' # We only need this to run in our repository. 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: Panquesito7/setup-jule@v1.1.3 23 | with: 24 | version: dev 25 | directory: .. 26 | add-to-path: true 27 | - name: Compile Directory Writer 28 | run: | 29 | julec -o ./directory_writer ./.github/workflows 30 | git update-index --add --chmod=-x ./directory_writer 31 | chmod +x ./directory_writer 32 | - name: Generate Directory 33 | run: | 34 | ./directory_writer 35 | - name: Cleanup 36 | run: | 37 | rm -rf ./jule 38 | rm -f ./directory_writer 39 | - name: Setup Git configurations 40 | run: | 41 | git config --global user.name github-actions[bot] 42 | git config --global user.email 'github-actions[bot]@users.noreply.github.com' 43 | - name: Commit and push changes 44 | run: | 45 | if [[ `git status --porcelain` ]]; 46 | then 47 | git checkout -b directory_update-${{ github.sha }} 48 | git add . 49 | 50 | git commit -m 'Update `DIRECTORY.md`' || true 51 | git push origin directory_update-${{ github.sha }}:directory_update-${{ github.sha }} || true 52 | 53 | gh pr create --base ${GITHUB_REF##*/} --head directory_update-${{ github.sha }} --title 'Update `DIRECTORY.md`' --body 'Updated the `DIRECTORY.md` file (see the diff. for changes).' || true 54 | fi 55 | env: 56 | GH_TOKEN: ${{ github.token }} 57 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macOS-latest] 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: Panquesito7/setup-jule@v1.1.3 13 | with: 14 | version: dev 15 | directory: . 16 | add-to-path: true 17 | extra-command: version 18 | 19 | - name: Test - math 20 | run: | 21 | julec test -o test math 22 | git update-index --add --chmod=-x test 23 | chmod +x test 24 | ./test 25 | 26 | - name: Test - search 27 | run: | 28 | julec test -o test search 29 | git update-index --add --chmod=-x test 30 | chmod +x test 31 | ./test 32 | 33 | - name: Test - sort 34 | run: | 35 | julec test -o test sort 36 | git update-index --add --chmod=-x test 37 | chmod +x test 38 | ./test 39 | 40 | - name: Test - strings 41 | run: | 42 | julec test -o test strings 43 | git update-index --add --chmod=-x test 44 | chmod +x test 45 | ./test 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | main 8 | julec 9 | !julec/ 10 | 11 | # Dist 12 | dist/ 13 | 14 | # Bin 15 | bin/ 16 | 17 | # Should ignore. 18 | __* 19 | 20 | # macOS's DS_Store files. 21 | .DS_Store 22 | 23 | # Test binary, built with `go test -c` 24 | *.test 25 | 26 | # Output of the go coverage tool, specifically when used with LiteIDE 27 | *.out 28 | 29 | # Dependency directories (remove the comment below to include it) 30 | # vendor/ 31 | 32 | # Hide Visual Studio Code Directory 33 | .vscode 34 | 35 | # Vim swap files 36 | *.swp 37 | 38 | -------------------------------------------------------------------------------- /DIRECTORY.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | ## Math 4 | - [Abs](math/abs.jule) 5 | - [Fact](math/fact.jule) 6 | - [Fib](math/fib.jule) 7 | - [Max](math/max.jule) 8 | - [Median](math/median.jule) 9 | - [Min](math/min.jule) 10 | - [Q Rsqrt](math/q_rsqrt.jule) 11 | - [Sum](math/sum.jule) 12 | 13 | ## Search 14 | - [Binary](search/binary.jule) 15 | - [Interpolation](search/interpolation.jule) 16 | - [Jump](search/jump.jule) 17 | - [Jump2](search/jump2.jule) 18 | - [Linear](search/linear.jule) 19 | 20 | ## Sort 21 | - [Binaryinsertion](sort/binaryinsertion.jule) 22 | - [Bubble](sort/bubble.jule) 23 | - [Bucket](sort/bucket.jule) 24 | - [Circle](sort/circle.jule) 25 | - [Cocktail](sort/cocktail.jule) 26 | - [Comb](sort/comb.jule) 27 | - [Count](sort/count.jule) 28 | - [Cycle](sort/cycle.jule) 29 | - [Exchange](sort/exchange.jule) 30 | - [Insertion](sort/insertion.jule) 31 | - [Merge](sort/merge.jule) 32 | - [Pancake](sort/pancake.jule) 33 | - [Quicksort](sort/quicksort.jule) 34 | - [Shell](sort/shell.jule) 35 | 36 | ## Strings 37 | - [Issubsequence](strings/issubsequence.jule) 38 | - [Reverse](strings/reverse.jule) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 The Algorithms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
8 | 9 | Algorithms and data structures implemented in the Jule programming language. 10 | 11 | All contents are listed in [`DIRECTORY.md`](./DIRECTORY.md) file. 12 | 13 | ## Contributing 14 | 15 | Please feel free to contribute. 16 | Any contribution is appreciated. 17 | 18 | Fork repository, add your algorithm, write your tests, add the README to the algorithm tree and open the PR. 19 | Make sure your code follows the current repository layout and all CIs are passed successfully. 20 | 21 | ## License 22 | The all Jule implementations are distributed under the terms of the MIT license. \ 23 | [See License Details](./LICENSE) 24 | -------------------------------------------------------------------------------- /jule.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlgorithms/Jule/0291bd4296be84b22160e1fa51b92aa4f4c819e6/jule.mod -------------------------------------------------------------------------------- /math/abs.jule: -------------------------------------------------------------------------------- 1 | fn Abs(mut n: f64): f64 { 2 | if n < 0 { 3 | n = -n 4 | } 5 | ret n 6 | } -------------------------------------------------------------------------------- /math/abs_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testAbs(t: &testing::T) { 7 | t.Assert(Abs(-12.2) == 12.2, "-12.2 should equal to 12.2") 8 | t.Assert(Abs(-12) == 12, "-12 should equal to 12") 9 | t.Assert(Abs(-0.35) == 0.35, "-0.35 should equal to 0.35") 10 | } -------------------------------------------------------------------------------- /math/fact.jule: -------------------------------------------------------------------------------- 1 | fn Fact(n: f64): f64 { 2 | if n == 0 { 3 | ret 1 4 | } 5 | ret n * Fact(n-1) 6 | } -------------------------------------------------------------------------------- /math/fact_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testFact(t: &testing::T) { 7 | t.Assert(Fact(10) == 3628800, "10 fact should equal to 3628800") 8 | } -------------------------------------------------------------------------------- /math/fib.jule: -------------------------------------------------------------------------------- 1 | // O(2^n) 2 | fn Fib(i: int): int { 3 | if i == 0 || i == 1 { 4 | ret i 5 | } 6 | ret Fib(i-1) + Fib(i-2) 7 | } 8 | 9 | // O(n * 2^n) 10 | fn FibSeq(mut n: int): []int { 11 | let mut s: []int 12 | let mut i = n 13 | for i > 0 { 14 | s = append(s, Fib(n-i+1)) 15 | i-- 16 | } 17 | ret s 18 | } -------------------------------------------------------------------------------- /math/fib_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/slices" 4 | use "std/testing" 5 | 6 | #test 7 | fn testFib(t: &testing::T) { 8 | t.Assert(Fib(6) == 8, "index 6 of fib. should be 8") 9 | t.Assert(slices::Equal(FibSeq(6), [1, 1, 2, 3, 5, 8]), "a fib. sequence up to index 6 should be 1, 1, 2, 3, 5, 8") 10 | } -------------------------------------------------------------------------------- /math/max.jule: -------------------------------------------------------------------------------- 1 | fn Max(values: ...int): int { 2 | if len(values) == 0 { 3 | ret 0 4 | } 5 | let mut max = values[0] 6 | for _, x in values[1:] { 7 | if max < x { 8 | max = x 9 | } 10 | } 11 | ret max 12 | } -------------------------------------------------------------------------------- /math/max_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testMax(t: &testing::T) { 7 | t.Assert(Max(0, -9024, 1, 894, -34) == 894, "max value should be 894") 8 | } -------------------------------------------------------------------------------- /math/median.jule: -------------------------------------------------------------------------------- 1 | use "sort" 2 | 3 | fn Median(mut slice: []int): f64 { 4 | slice = sort::Quicksort(slice) 5 | let l = len(slice) 6 | match { 7 | | l == 0: 8 | ret 0 9 | | l%2 == 0: 10 | ret f64(slice[l/2-1]+slice[l/2]) / 2 11 | |: 12 | ret f64(slice[l/2]) 13 | } 14 | } -------------------------------------------------------------------------------- /math/median_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testMedian(t: &testing::T) { 7 | t.Assert(Median([2, 5, 2, 6, -4, -15, 1, -3]) == 1.5, "median should be 1.5") 8 | } -------------------------------------------------------------------------------- /math/min.jule: -------------------------------------------------------------------------------- 1 | fn Min(values: ...int): int { 2 | if len(values) == 0 { 3 | ret 0 4 | } 5 | let mut min = values[0] 6 | for _, x in values[1:] { 7 | if min > x { 8 | min = x 9 | } 10 | } 11 | ret min 12 | } -------------------------------------------------------------------------------- /math/min_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testMin(t: &testing::T) { 7 | t.Assert(Min(0, -9024, 1, 894, -34) == -9024, "min value should be -9024") 8 | } -------------------------------------------------------------------------------- /math/q_rsqrt.jule: -------------------------------------------------------------------------------- 1 | use "std/math" 2 | 3 | // Fast inverse square root. 4 | // See: https://en.wikipedia.org/wiki/Fast_inverse_square_root 5 | fn QRsqrt(mut f: f64): f64 { 6 | const ThreeHalfs = 1.5 7 | const Magic = 0x5FE6EB50C7B537A9 8 | 9 | let n2 = f * 0.5 10 | let b = Magic - (math::F64Bits(f) >> 1) 11 | f = math::F64FromBits(b) 12 | f *= ThreeHalfs - (n2 * f * f) 13 | ret f 14 | } -------------------------------------------------------------------------------- /math/q_rsqrt_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | static casesQRsqrt: [][]f64 = [ 6 | [90, 0.10530667845276515], 7 | [987743, 0.001004453602645671], 8 | [2, 0.706929650795464], 9 | [224, 0.06680356325907384], 10 | ] 11 | 12 | #test 13 | fn testQRsqrt(t: &testing::T) { 14 | for _, c in casesQRsqrt { 15 | if QRsqrt(c[0]) != c[1] { 16 | t.Errorf("q_rsqrt({}) should be {}", c[0], c[1]) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /math/sum.jule: -------------------------------------------------------------------------------- 1 | fn Sum(slice: []int): int { 2 | let mut total: int = 0 3 | for _, i in slice { 4 | total += i 5 | } 6 | ret total 7 | } -------------------------------------------------------------------------------- /math/sum_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testSum(t: &testing::T) { 7 | t.Assert(Sum([-9, 0, 1, 99, 54, 12]) == 157, "sum should return 157") 8 | } -------------------------------------------------------------------------------- /search/binary.jule: -------------------------------------------------------------------------------- 1 | // Search for target within a sorted array by repeatedly dividing the array in half and comparing the midpoint with the target. 2 | // This function uses recursive call to itself. 3 | // If a target is found, the index of the target is returned. Else the function throws exceptional with Error.NotFound. 4 | fn Binary(array: []int, target: int, lowIndex: int, highIndex: int)!: int { 5 | if highIndex < lowIndex || len(array) == 0 { 6 | error(Error.NotFound) 7 | } 8 | mid := int(lowIndex + (highIndex-lowIndex)/2) 9 | if array[mid] > target { 10 | ret Binary(array, target, lowIndex, mid-1) else { error(error) } 11 | } else if array[mid] < target { 12 | ret Binary(array, target, mid+1, highIndex) else { error(error) } 13 | } else { 14 | ret mid 15 | } 16 | } 17 | 18 | // Search for target within a sorted array by repeatedly dividing the array in half and comparing the midpoint with the target. 19 | // Unlike Binary, this function uses iterative method and not recursive. 20 | // If a target is found, the index of the target is returned. Else the function throws exceptional with Error.NotFound. 21 | fn BinaryIterative(array: []int, target: int)!: int { 22 | mut startIndex := 0 23 | mut endIndex := len(array) - 1 24 | mut mid := 0 25 | for startIndex <= endIndex { 26 | mid = int(startIndex + (endIndex-startIndex)/2) 27 | if array[mid] > target { 28 | endIndex = mid - 1 29 | } else if array[mid] < target { 30 | startIndex = mid + 1 31 | } else { 32 | ret mid 33 | } 34 | } 35 | error(Error.NotFound) 36 | } 37 | 38 | // Returns index to the first element in the range [0, len(array)-1] that is not less than (i.e. greater or equal to) target. 39 | // Throws exceptional with Error.NotFound if no such element is found. 40 | fn LowerBound(array: []int, target: int)!: int { 41 | mut startIndex := 0 42 | mut endIndex := len(array) - 1 43 | mut mid := 0 44 | for startIndex <= endIndex { 45 | mid = int(startIndex + (endIndex-startIndex)/2) 46 | if array[mid] < target { 47 | startIndex = mid + 1 48 | } else { 49 | endIndex = mid - 1 50 | } 51 | } 52 | 53 | // when target greater than every element in array, startIndex will out of bounds 54 | if startIndex >= len(array) { 55 | error(Error.NotFound) 56 | } 57 | ret startIndex 58 | } 59 | 60 | // Returns index to the first element in the range [lowIndex, len(array)-1] that is greater than target. 61 | // Throws exceptional with Error.NotFound if no such element is found. 62 | fn UpperBound(array: []int, target: int)!: int { 63 | mut startIndex := 0 64 | mut endIndex := len(array) - 1 65 | mut mid := 0 66 | for startIndex <= endIndex { 67 | mid = int(startIndex + (endIndex-startIndex)/2) 68 | if array[mid] > target { 69 | endIndex = mid - 1 70 | } else { 71 | startIndex = mid + 1 72 | } 73 | } 74 | 75 | // when target greater or equal than every element in array, startIndex will out of bounds 76 | if startIndex >= len(array) { 77 | error(Error.NotFound) 78 | } 79 | ret startIndex 80 | } -------------------------------------------------------------------------------- /search/binary_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testBinary(t: &testing::T) { 7 | for _, test in searchTests { 8 | actualValue := Binary(test.data, test.key, 0, len(test.data)-1) else { 9 | if error != test.expectedError { 10 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 11 | } 12 | continue 13 | } 14 | if test.expectedError != Error.NA { 15 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 16 | } 17 | if actualValue != test.expected { 18 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 19 | } 20 | } 21 | } 22 | 23 | #test 24 | fn testBinaryIterative(t: &testing::T) { 25 | for _, test in searchTests { 26 | actualValue := BinaryIterative(test.data, test.key) else { 27 | if error != test.expectedError { 28 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 29 | } 30 | continue 31 | } 32 | if test.expectedError != Error.NA { 33 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 34 | } 35 | if actualValue != test.expected { 36 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 37 | } 38 | } 39 | } 40 | 41 | #test 42 | fn testLowerBound(t: &testing::T) { 43 | for _, test in lowerBoundTests { 44 | actualValue := LowerBound(test.data, test.key) else { 45 | if error != test.expectedError { 46 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 47 | } 48 | continue 49 | } 50 | if test.expectedError != Error.NA { 51 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 52 | } 53 | if actualValue != test.expected { 54 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 55 | } 56 | } 57 | } 58 | 59 | #test 60 | fn testUpperBound(t: &testing::T) { 61 | for _, test in upperBoundTests { 62 | actualValue := UpperBound(test.data, test.key) else { 63 | if error != test.expectedError { 64 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 65 | } 66 | continue 67 | } 68 | if test.expectedError != Error.NA { 69 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 70 | } 71 | if actualValue != test.expected { 72 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /search/error.jule: -------------------------------------------------------------------------------- 1 | // Error codes for search algorithms. 2 | enum Error { 3 | NA, 4 | NotFound, 5 | } -------------------------------------------------------------------------------- /search/interpolation.jule: -------------------------------------------------------------------------------- 1 | // Searches for the entity in the given sortedData. 2 | // if the entity is present, it will return the index of the entity, if not throws exceptional with Error.NotFound. 3 | // see: https://en.wikipedia.org/wiki/Interpolation_search 4 | // Complexity 5 | // 6 | // Worst: O(N) 7 | // Average: O(log(log(N)) if the elements are uniformly distributed 8 | // Best: O(1) 9 | // 10 | // Example 11 | // 12 | // fmt::Println(Interpolation([1, 2, 9, 20, 31, 45, 63, 70, 100],100)) 13 | fn Interpolation(sortedData: []int, guess: int)!: int { 14 | if len(sortedData) == 0 { 15 | error(Error.NotFound) 16 | } 17 | 18 | mut low, mut high := 0, len(sortedData) - 1 19 | mut lowVal, mut highVal := sortedData[low], sortedData[high] 20 | 21 | for lowVal != highVal && (lowVal <= guess) && (guess <= highVal) { 22 | mut mid := low + int(f64(f64((guess-lowVal)*(high-low))/f64(highVal-lowVal))) 23 | 24 | // if guess is found, array can also have duplicate values, so scan backwards and find the first index 25 | if sortedData[mid] == guess { 26 | for mid > 0 && sortedData[mid-1] == guess { 27 | mid-- 28 | } 29 | ret mid 30 | } 31 | 32 | // adjust our guess and continue 33 | if sortedData[mid] > guess { 34 | high, highVal = mid - 1, sortedData[high] 35 | } else { 36 | low, lowVal = mid + 1, sortedData[low] 37 | } 38 | } 39 | 40 | if guess == lowVal { 41 | ret low 42 | } 43 | error(Error.NotFound) 44 | } -------------------------------------------------------------------------------- /search/interpolation_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testInterpolation(t: &testing::T) { 7 | for _, test in searchTests { 8 | actualValue := Interpolation(test.data, test.key) else { 9 | if error != test.expectedError { 10 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 11 | } 12 | continue 13 | } 14 | if test.expectedError != Error.NA { 15 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 16 | } 17 | if actualValue != test.expected { 18 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /search/jump.jule: -------------------------------------------------------------------------------- 1 | // description: Implementation of jump search 2 | // details: 3 | // A search algorithm for ordered list that jump through the list to narrow down the range 4 | // before performing a linear search 5 | // reference: https://en.wikipedia.org/wiki/Jump_search 6 | // see jump_test.go for a test implementation, test function TestJump 7 | 8 | use "std/math" 9 | 10 | // Search works by jumping multiple steps ahead in sorted list until it find an item larger than target, 11 | // then create a sublist of item from the last searched item up to the current item and perform a linear search. 12 | fn Jump(array: []int, target: int)!: int { 13 | n := len(array) 14 | if n == 0 { 15 | error(Error.NotFound) 16 | } 17 | 18 | // the optimal value of step is square root of the length of list 19 | step := int(math::Round(math::Sqrt(f64(n)))) 20 | 21 | mut prev := 0 // previous index 22 | mut curr := step // current index 23 | for array[curr-1] < target { 24 | prev = curr 25 | if prev >= len(array) { 26 | error(Error.NotFound) 27 | } 28 | 29 | curr += step 30 | 31 | // prevent jumping over list range 32 | if curr > n { 33 | curr = n 34 | } 35 | } 36 | 37 | // perform linear search from index prev to index curr 38 | for array[prev] < target { 39 | prev++ 40 | 41 | // if reach end of range, indicate target not found 42 | if prev == curr { 43 | error(Error.NotFound) 44 | } 45 | } 46 | if array[prev] == target { 47 | ret prev 48 | } 49 | 50 | error(Error.NotFound) 51 | } -------------------------------------------------------------------------------- /search/jump2.jule: -------------------------------------------------------------------------------- 1 | use "std/math" 2 | 3 | fn Jump2(arr: []int, target: int)!: int { 4 | step := int(math::Round(math::Sqrt(f64(len(arr))))) 5 | mut rbound := len(arr) 6 | mut i := step 7 | for i < len(arr); i += step { 8 | if arr[i] > target { 9 | rbound = i 10 | break 11 | } 12 | } 13 | 14 | i = rbound - step 15 | for i < rbound; i++ { 16 | if arr[i] == target { 17 | ret i 18 | } 19 | if arr[i] > target { 20 | break 21 | } 22 | } 23 | error(Error.NotFound) 24 | } -------------------------------------------------------------------------------- /search/jump2_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testJump2(t: &testing::T) { 7 | for _, test in searchTests { 8 | actualValue := Jump2(test.data, test.key) else { 9 | if error != test.expectedError { 10 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 11 | } 12 | continue 13 | } 14 | if test.expectedError != Error.NA { 15 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 16 | } 17 | if actualValue != test.expected { 18 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /search/jump_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testJump(t: &testing::T) { 7 | for _, test in searchTests { 8 | actualValue := Jump(test.data, test.key) else { 9 | if error != test.expectedError { 10 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 11 | } 12 | continue 13 | } 14 | if test.expectedError != Error.NA { 15 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 16 | } 17 | if actualValue != test.expected { 18 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /search/linear.jule: -------------------------------------------------------------------------------- 1 | // Simple linear search algorithm that iterates over all elements of an array in the worst case scenario 2 | fn Linear(array: []int, query: int)!: int { 3 | for i, item in array { 4 | if item == query { 5 | ret i 6 | } 7 | } 8 | error(Error.NotFound) 9 | } -------------------------------------------------------------------------------- /search/linear_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | #test 6 | fn testLinear(t: &testing::T) { 7 | for _, test in searchTests { 8 | actualValue := Linear(test.data, test.key) else { 9 | if error != test.expectedError { 10 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, error) 11 | } 12 | continue 13 | } 14 | if test.expectedError != Error.NA { 15 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected error '{}', get error '{}'", test.name, test.data, test.key, test.expectedError, Error.NA) 16 | } 17 | if actualValue != test.expected { 18 | t.Errorf("test '{}' failed: input array '{}' with key '{}', expected '{}', get '{}'", test.name, test.data, test.key, test.expected, actualValue) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /search/testcases_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | struct searchTest { 4 | data: []int 5 | key: int 6 | expected: int 7 | expectedError: Error 8 | name: str 9 | } 10 | 11 | // Note that these are immutable therefore they are shared among all the search tests. 12 | // If your algorithm is mutating these then it is advisable to create separate test cases. 13 | static searchTests: []searchTest = [ 14 | //Sanity 15 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10, 9, Error.NA, "Sanity"}, 16 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 9, 8, Error.NA, "Sanity"}, 17 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 8, 7, Error.NA, "Sanity"}, 18 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 7, 6, Error.NA, "Sanity"}, 19 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 6, 5, Error.NA, "Sanity"}, 20 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, 4, Error.NA, "Sanity"}, 21 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4, 3, Error.NA, "Sanity"}, 22 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3, 2, Error.NA, "Sanity"}, 23 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2, 1, Error.NA, "Sanity"}, 24 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1, 0, Error.NA, "Sanity"}, 25 | //Absent 26 | {[1, 4, 5, 6, 7, 10], -25, -1, Error.NotFound, "Absent"}, 27 | {[1, 4, 5, 6, 7, 10], 25, -1, Error.NotFound, "Absent"}, 28 | //Empty slice 29 | {[], 2, -1, Error.NotFound, "Empty"}, 30 | ] 31 | 32 | static lowerBoundTests: []searchTest = [ 33 | //Sanity 34 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], -25, 0, Error.NA, "Sanity"}, 35 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1, 0, Error.NA, "Sanity"}, 36 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, 4, Error.NA, "Sanity"}, 37 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10, 9, Error.NA, "Sanity"}, 38 | {[1, 2, 2, 2, 2, 6, 7, 8, 9, 10], 2, 1, Error.NA, "Sanity"}, 39 | {[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], 2, 0, Error.NA, "Sanity"}, 40 | //Absent 41 | {[1, 4, 5, 6, 7, 10], 25, -1, Error.NotFound, "Absent"}, 42 | //Empty slice 43 | {[], 2, -1, Error.NotFound, "Empty"}, 44 | ] 45 | 46 | static upperBoundTests: []searchTest = [ 47 | //Sanity 48 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], -25, 0, Error.NA, "Sanity"}, 49 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1, 1, Error.NA, "Sanity"}, 50 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, 5, Error.NA, "Sanity"}, 51 | {[1, 2, 2, 2, 2, 6, 7, 8, 9, 10], 2, 5, Error.NA, "Sanity"}, 52 | //Absent 53 | {[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], 2, -1, Error.NotFound, "Sanity"}, 54 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10, -1, Error.NotFound, "Sanity"}, 55 | {[1, 4, 5, 6, 7, 10], 25, -1, Error.NotFound, "Absent"}, 56 | //Empty slice 57 | {[], 2, -1, Error.NotFound, "Empty"}, 58 | ] -------------------------------------------------------------------------------- /sort/binaryinsertion.jule: -------------------------------------------------------------------------------- 1 | // Binary Insertion Sort 2 | // description: Implementation of binary insertion sort in Go 3 | // details: Binary Insertion Sort is a variation of 4 | // Insertion sort in which proper location to 5 | // insert the selected element is found using the 6 | // Binary search algorithm. 7 | // ref: https://www.geeksforgeeks.org/binary-insertion-sort 8 | 9 | fn BinaryInsertion[T: ordered](mut arr: []T): []T { 10 | mut currentIndex := 1 11 | for currentIndex < len(arr); currentIndex++ { 12 | mut temporary := arr[currentIndex] 13 | mut low := 0 14 | mut high := currentIndex - 1 15 | 16 | for low <= high { 17 | mid := low + (high-low)/2 18 | if arr[mid] > temporary { 19 | high = mid - 1 20 | } else { 21 | low = mid + 1 22 | } 23 | } 24 | 25 | mut itr := currentIndex 26 | for itr > low; itr-- { 27 | arr[itr] = arr[itr-1] 28 | } 29 | 30 | arr[low] = temporary 31 | } 32 | ret arr 33 | } -------------------------------------------------------------------------------- /sort/bubble.jule: -------------------------------------------------------------------------------- 1 | // Implementation of basic bubble sort algorithm 2 | // Reference: https://en.wikipedia.org/wiki/Bubble_sort 3 | 4 | // Bubble is a simple generic definition of Bubble sort algorithm. 5 | fn Bubble[T: ordered](mut arr: []T): []T { 6 | mut swapped := true 7 | for swapped { 8 | swapped = false 9 | mut i := 0 10 | for i < len(arr)-1; i++ { 11 | if arr[i+1] < arr[i] { 12 | arr[i+1], arr[i] = arr[i], arr[i+1] 13 | swapped = true 14 | } 15 | } 16 | } 17 | ret arr 18 | } -------------------------------------------------------------------------------- /sort/bucket.jule: -------------------------------------------------------------------------------- 1 | // Bucket sorts a slice. It is mainly useful 2 | // when input is uniformly distributed over a range. 3 | fn Bucket[T: numeric](mut arr: []T): []T { 4 | // early return if the array too small 5 | if len(arr) <= 1 { 6 | ret arr 7 | } 8 | 9 | // find the maximum and minimum elements in arr 10 | mut max := arr[0] 11 | mut min := arr[0] 12 | for _, v in arr { 13 | if v > max { 14 | max = v 15 | } 16 | if v < min { 17 | min = v 18 | } 19 | } 20 | 21 | // create an empty bucket for each element in arr 22 | mut bucket := make([][]T, len(arr)) 23 | 24 | // put each element in the appropriate bucket 25 | for (_, mut v) in arr { 26 | bucketIndex := int((v - min) / (max - min) * T(len(arr)-1)) 27 | bucket[bucketIndex] = append(bucket[bucketIndex], v) 28 | } 29 | 30 | // use insertion sort to sort each bucket 31 | for i in bucket { 32 | bucket[i] = Insertion(bucket[i]) 33 | } 34 | 35 | // concatenate the sorted buckets 36 | mut sorted := make([]T, 0, len(arr)) 37 | for _, v in bucket { 38 | sorted = append(sorted, v...) 39 | } 40 | 41 | ret sorted 42 | } -------------------------------------------------------------------------------- /sort/circle.jule: -------------------------------------------------------------------------------- 1 | // Sorts an array using the circle sort algorithm. 2 | fn Circle[T: ordered](mut arr: []T): []T { 3 | if len(arr) == 0 { 4 | ret arr 5 | } 6 | for doSort(arr, 0, len(arr)-1) { 7 | } 8 | ret arr 9 | } 10 | 11 | // The recursive function that implements the circle sort algorithm. 12 | fn doSort[T: ordered](mut arr: []T, left: int, right: int): bool { 13 | if left == right { 14 | ret false 15 | } 16 | mut swapped := false 17 | mut low := left 18 | mut high := right 19 | 20 | for low < high { 21 | if arr[low] > arr[high] { 22 | arr[low], arr[high] = arr[high], arr[low] 23 | swapped = true 24 | } 25 | low++ 26 | high-- 27 | } 28 | 29 | if low == high && arr[low] > arr[high+1] { 30 | arr[low], arr[high+1] = arr[high+1], arr[low] 31 | swapped = true 32 | } 33 | 34 | mid := left + (right-left)/2 35 | leftHalf := doSort(arr, left, mid) 36 | rightHalf := doSort(arr, mid+1, right) 37 | 38 | ret swapped || leftHalf || rightHalf 39 | } -------------------------------------------------------------------------------- /sort/cocktail.jule: -------------------------------------------------------------------------------- 1 | // Implementation of Cocktail sorting 2 | // reference: https://en.wikipedia.org/wiki/Cocktail_shaker_sort 3 | 4 | // Sort is a variation of bubble sort, operating in two directions (beginning to end, end to beginning). 5 | fn Cocktail[T: ordered](mut arr: []T): []T { 6 | if len(arr) == 0 { // ignore 0 length arrays 7 | ret arr 8 | } 9 | 10 | mut swapped := true // true if swapped two or more elements in the last loop 11 | // if it loops through the array without swapping, the array is sorted 12 | 13 | // start and end indexes, this will be updated excluding already sorted elements 14 | mut start := 0 15 | mut end := len(arr) - 1 16 | 17 | for swapped { 18 | swapped = false 19 | mut newStart := 0 20 | mut newEnd := 0 21 | 22 | mut i := start 23 | for i < end; i++ { // first loop, from start to end 24 | if arr[i] > arr[i+1] { // if current and next elements are unordered 25 | arr[i], arr[i+1] = arr[i+1], arr[i] // swap two elements 26 | newEnd = i 27 | swapped = true 28 | } 29 | } 30 | 31 | end = newEnd 32 | 33 | if !swapped { // early exit, skipping the second loop 34 | break 35 | } 36 | 37 | swapped = false 38 | 39 | i = end 40 | for i > start; i-- { // second loop, from end to start 41 | if arr[i] < arr[i-1] { // same process of the first loop, now going 'backwards' 42 | arr[i], arr[i-1] = arr[i-1], arr[i] 43 | newStart = i 44 | swapped = true 45 | } 46 | } 47 | 48 | start = newStart 49 | } 50 | 51 | ret arr 52 | } -------------------------------------------------------------------------------- /sort/comb.jule: -------------------------------------------------------------------------------- 1 | // Implementation of comb sort algorithm, an improvement of bubble sort 2 | // Reference: https://www.geeksforgeeks.org/comb-sort/ 3 | 4 | fn getNextGap(mut gap: int): int { 5 | gap = (gap * 10) / 13 6 | if gap < 1 { 7 | ret 1 8 | } 9 | ret gap 10 | } 11 | 12 | // Simple sorting algorithm which is an improvement of the bubble sorting algorithm. 13 | fn Comb[T: ordered](mut data: []T): []T { 14 | n := len(data) 15 | mut gap := n 16 | mut swapped := true 17 | 18 | for gap != 1 || swapped { 19 | gap = getNextGap(gap) 20 | swapped = false 21 | mut i := 0 22 | for i < n-gap; i++ { 23 | if data[i] > data[i+gap] { 24 | data[i], data[i+gap] = data[i+gap], data[i] 25 | swapped = true 26 | } 27 | } 28 | } 29 | ret data 30 | } -------------------------------------------------------------------------------- /sort/count.jule: -------------------------------------------------------------------------------- 1 | // description: Implementation of counting sort algorithm 2 | // details: A simple counting sort algorithm implementation 3 | // author [Phil](https://github.com/pschik) 4 | 5 | fn Count[T: int | i64 | i32 | i16 | i8 | uint | u8 | u16 | u32 | u64](mut data: []T): []T { 6 | if len(data) == 0 { 7 | ret data 8 | } 9 | mut aMin, mut aMax := data[0], data[0] 10 | for _, x in data { 11 | if x < aMin { 12 | aMin = x 13 | } 14 | if x > aMax { 15 | aMax = x 16 | } 17 | } 18 | mut count := make([]int, aMax-aMin+1) 19 | for _, x in data { 20 | count[x-aMin]++ // this is the reason for having only Integer constraint instead of Ordered. 21 | } 22 | mut z := 0 23 | for (i, mut c) in count { 24 | for c > 0 { 25 | data[z] = T(i) + aMin 26 | z++ 27 | c-- 28 | } 29 | } 30 | ret data 31 | } -------------------------------------------------------------------------------- /sort/cycle.jule: -------------------------------------------------------------------------------- 1 | // Cycle sort is an in-place, unstable sorting algorithm that is particularly useful 2 | // when sorting arrays containing elements with a small range of values. It is theoretically 3 | // optimal in terms of the total number of writes to the original array. 4 | fn Cycle[T: numeric](mut arr: []T): []T { 5 | mut counter, mut cycle, len := 0, 0, len(arr) 6 | // Early return if the array too small 7 | if len <= 1 { 8 | ret arr 9 | } 10 | 11 | for cycle < len-1; cycle++ { 12 | mut elem := arr[cycle] 13 | // Find total smaller elements to right 14 | mut pos := cycle 15 | counter = cycle + 1 16 | for counter < len; counter++ { 17 | if arr[counter] < elem { 18 | pos++ 19 | } 20 | } 21 | // In case this element is already in correct position, let's skip processing 22 | if pos == cycle { 23 | continue 24 | } 25 | // In case we have same elements, we want to skip to the end of that list as well, ignoring order 26 | // This makes the algorithm unstable for composite elements 27 | for elem == arr[pos] { 28 | pos++ 29 | } 30 | // Now let us put the item to it's right position 31 | arr[pos], elem = elem, arr[pos] 32 | 33 | //We need to rotate the array till we have reached the start of the cycle again 34 | for pos != cycle { 35 | pos = cycle 36 | // Find smaller elements to right again 37 | counter = cycle + 1 38 | for counter < len; counter++ { 39 | if arr[counter] < elem { 40 | pos++ 41 | } 42 | } 43 | for elem == arr[pos] { 44 | pos++ 45 | } 46 | //We can do this unconditionally, but the check helps prevent redundant writes to the array 47 | if elem != arr[pos] { 48 | arr[pos], elem = elem, arr[pos] 49 | } 50 | } 51 | } 52 | 53 | ret arr 54 | } -------------------------------------------------------------------------------- /sort/exchange.jule: -------------------------------------------------------------------------------- 1 | // Implementation of exchange sort algorithm, a variant of bubble sort 2 | // Reference: https://en.wikipedia.org/wiki/Sorting_algorithm#Exchange_sort 3 | 4 | fn Exchange[T: ordered](mut arr: []T): []T { 5 | mut i := 0 6 | for i < len(arr)-1; i++ { 7 | mut j := i + 1 8 | for j < len(arr); j++ { 9 | if arr[i] > arr[j] { 10 | arr[i], arr[j] = arr[j], arr[i] 11 | } 12 | } 13 | } 14 | ret arr 15 | } -------------------------------------------------------------------------------- /sort/insertion.jule: -------------------------------------------------------------------------------- 1 | fn Insertion[T: ordered](mut arr: []T): []T { 2 | mut currentIndex := 1 3 | for currentIndex < len(arr); currentIndex++ { 4 | mut temporary := arr[currentIndex] 5 | mut iterator := currentIndex 6 | for iterator > 0 && arr[iterator-1] > temporary; iterator-- { 7 | arr[iterator] = arr[iterator-1] 8 | } 9 | arr[iterator] = temporary 10 | } 11 | ret arr 12 | } -------------------------------------------------------------------------------- /sort/merge.jule: -------------------------------------------------------------------------------- 1 | fn merge[T: ordered](a: []T, b: []T): []T { 2 | mut r := make([]T, len(a)+len(b)) 3 | mut i := 0 4 | mut j := 0 5 | 6 | for i < len(a) && j < len(b) { 7 | if a[i] <= b[j] { 8 | r[i+j] = a[i] 9 | i++ 10 | } else { 11 | r[i+j] = b[j] 12 | j++ 13 | } 14 | } 15 | 16 | for i < len(a) { 17 | r[i+j] = a[i] 18 | i++ 19 | } 20 | for j < len(b) { 21 | r[i+j] = b[j] 22 | j++ 23 | } 24 | 25 | ret r 26 | } 27 | 28 | // Perform merge sort on a slice 29 | fn Merge[T: ordered](mut items: []T): []T { 30 | if len(items) < 2 { 31 | ret items 32 | } 33 | middle := len(items) / 2 34 | mut a := Merge(items[:middle]) 35 | mut b := Merge(items[middle:]) 36 | ret merge(a, b) 37 | } -------------------------------------------------------------------------------- /sort/pancake.jule: -------------------------------------------------------------------------------- 1 | // Pancake sorts a slice using flip operations, 2 | // where flip refers to the idea of reversing the 3 | // slice from index `0` to `i`. 4 | fn Pancake[T: ordered](mut arr: []T): []T { 5 | // early return if the array too small 6 | if len(arr) <= 1 { 7 | ret arr 8 | } 9 | 10 | // start from the end of the array 11 | mut i := len(arr) - 1 12 | for i > 0; i-- { 13 | // find the index of the maximum element in arr 14 | mut max := 0 15 | mut j := 1 16 | for j <= i; j++ { 17 | if arr[j] > arr[max] { 18 | max = j 19 | } 20 | } 21 | 22 | // if the maximum element is not at the end of the array 23 | if max != i { 24 | // flip the maximum element to the beginning of the array 25 | arr = flip(arr, max) 26 | 27 | // flip the maximum element to the end of the array by flipping the whole array 28 | arr = flip(arr, i) 29 | } 30 | } 31 | 32 | ret arr 33 | } 34 | 35 | // flip reverses the input slice from `0` to `i`. 36 | fn flip[T: ordered](mut arr: []T, mut i: int): []T { 37 | mut j := 0 38 | for j < i; j++ { 39 | arr[j], arr[i] = arr[i], arr[j] 40 | i-- 41 | } 42 | ret arr 43 | } -------------------------------------------------------------------------------- /sort/quicksort.jule: -------------------------------------------------------------------------------- 1 | // description: Implementation of in-place quicksort algorithm 2 | // details: 3 | // A simple in-place quicksort algorithm implementation. [Wikipedia](https://en.wikipedia.org/wiki/Quicksort) 4 | // author(s) [Taj](https://github.com/tjgurwara99) 5 | 6 | fn Partition[T: ordered](mut arr: []T, low: int, high: int): int { 7 | mut index := low - 1 8 | pivotElement := arr[high] 9 | mut i := low 10 | for i < high; i++ { 11 | if arr[i] <= pivotElement { 12 | index += 1 13 | arr[index], arr[i] = arr[i], arr[index] 14 | } 15 | } 16 | arr[index+1], arr[high] = arr[high], arr[index+1] 17 | ret index + 1 18 | } 19 | 20 | // Sorts the specified range within the array 21 | fn QuicksortRange[T: ordered](mut arr: []T, low: int, high: int) { 22 | if len(arr) <= 1 { 23 | ret 24 | } 25 | 26 | if low < high { 27 | pivot := Partition(arr, low, high) 28 | QuicksortRange(arr, low, pivot-1) 29 | QuicksortRange(arr, pivot+1, high) 30 | } 31 | } 32 | 33 | // Sorts the entire array. 34 | fn Quicksort[T: ordered](mut arr: []T): []T { 35 | QuicksortRange(arr, 0, len(arr)-1) 36 | ret arr 37 | } -------------------------------------------------------------------------------- /sort/shell.jule: -------------------------------------------------------------------------------- 1 | fn Shell[T: ordered](mut arr: []T): []T { 2 | mut d := int(len(arr) / 2) 3 | for d > 0; d /= 2 { 4 | mut i := d 5 | for i < len(arr); i++ { 6 | mut j := i 7 | for j >= d && arr[j-d] > arr[j]; j -= d { 8 | arr[j], arr[j-d] = arr[j-d], arr[j] 9 | } 10 | } 11 | } 12 | ret arr 13 | } -------------------------------------------------------------------------------- /sort/testcases_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/slices" 4 | use "std/testing" 5 | 6 | struct sortTest { 7 | input: []int 8 | expected: []int 9 | name: str 10 | } 11 | 12 | static sortTests: []sortTest = [ 13 | //Sorted slice 14 | { 15 | input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 16 | expected: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 17 | name: "Sorted Unsigned", 18 | }, 19 | //Reversed slice 20 | { 21 | input: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], 22 | expected: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 23 | name: "Reversed Unsigned", 24 | }, 25 | //Sorted slice 26 | { 27 | input: [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 28 | expected: [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 29 | name: "Sorted Signed", 30 | }, 31 | //Reversed slice 32 | { 33 | input: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10], 34 | expected: [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 35 | name: "Reversed Signed", 36 | }, 37 | //Reversed slice, even length 38 | { 39 | input: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10], 40 | expected: [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 41 | name: "Reversed Signed #2", 42 | }, 43 | //Random order with repetitions 44 | { 45 | input: [-5, 7, 4, -2, 6, 5, 8, 3, 2, -7, -1, 0, -3, 9, -6, -4, 10, 9, 1, -8, -9, -10], 46 | expected: [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10], 47 | name: "Random order Signed", 48 | }, 49 | //Single-entry slice 50 | { 51 | input: [1], 52 | expected: [1], 53 | name: "Singleton", 54 | }, 55 | // Empty slice 56 | { 57 | input: [], 58 | expected: [], 59 | name: "Empty Slice", 60 | }, 61 | ] 62 | 63 | fn clone(s: []int): []int { 64 | ret append(make([]int, 0, len(s)), s...) 65 | } 66 | 67 | fn testGeneral(t: &testing::T, sortingFunction: fn(mut []int): []int) { 68 | for _, test in sortTests { 69 | actual := sortingFunction(clone(test.input)) 70 | sorted := slices::Equal(actual, test.expected) 71 | if !sorted { 72 | t.Errorf("test {} failed", test.name) 73 | t.Errorf("actual {} expected {}", actual, test.expected) 74 | } 75 | } 76 | } 77 | 78 | #test 79 | fn testBinaryInsertion(t: &testing::T) { 80 | testGeneral(t, BinaryInsertion[int]) 81 | } 82 | 83 | #test 84 | fn testBubble(t: &testing::T) { 85 | testGeneral(t, Bubble[int]) 86 | } 87 | 88 | #test 89 | fn testInsertion(t: &testing::T) { 90 | testGeneral(t, Insertion[int]) 91 | } 92 | 93 | #test 94 | fn testBucket(t: &testing::T) { 95 | testGeneral(t, Bucket[int]) 96 | } 97 | 98 | #test 99 | fn testCircle(t: &testing::T) { 100 | testGeneral(t, Circle[int]) 101 | } 102 | 103 | #test 104 | fn testCocktail(t: &testing::T) { 105 | testGeneral(t, Cocktail[int]) 106 | } 107 | 108 | #test 109 | fn testComb(t: &testing::T) { 110 | testGeneral(t, Comb[int]) 111 | } 112 | 113 | #test 114 | fn testCount(t: &testing::T) { 115 | testGeneral(t, Count[int]) 116 | } 117 | 118 | #test 119 | fn testCycle(t: &testing::T) { 120 | testGeneral(t, Cycle[int]) 121 | } 122 | 123 | #test 124 | fn testExchange(t: &testing::T) { 125 | testGeneral(t, Exchange[int]) 126 | } 127 | 128 | #test 129 | fn testMerge(t: &testing::T) { 130 | testGeneral(t, Merge[int]) 131 | } 132 | 133 | #test 134 | fn testPancake(t: &testing::T) { 135 | testGeneral(t, Pancake[int]) 136 | } 137 | 138 | #test 139 | fn testShell(t: &testing::T) { 140 | testGeneral(t, Shell[int]) 141 | } 142 | 143 | #test 144 | fn testQuicksort(t: &testing::T) { 145 | testGeneral(t, Quicksort[int]) 146 | } -------------------------------------------------------------------------------- /strings/issubsequence.jule: -------------------------------------------------------------------------------- 1 | // Checks if a given string is a subsequence of another string. 2 | // A subsequence of a given string is a string that can be derived from the given 3 | // string by deleting some or no characters without changing the order of the 4 | // remaining characters. (i.e., "dpr" is a subsequence of "depqr" while "drp" is not). 5 | // Author: sanjibgirics 6 | 7 | // Returns true if s is subsequence of t, otherwise return false. 8 | fn IsSubsequence(s: str, t: str): bool { 9 | if len(s) > len(t) { 10 | ret false 11 | } 12 | 13 | if s == t { 14 | ret true 15 | } 16 | 17 | if len(s) == 0 { 18 | ret true 19 | } 20 | 21 | mut sIndex := 0 22 | mut tIndex := 0 23 | for tIndex < len(t); tIndex++ { 24 | if s[sIndex] == t[tIndex] { 25 | sIndex++ 26 | } 27 | if sIndex == len(s) { 28 | ret true 29 | } 30 | } 31 | 32 | ret false 33 | } -------------------------------------------------------------------------------- /strings/issubsequence_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | struct subsequenceTest { 6 | name: str 7 | s: str 8 | t: str 9 | expected: bool 10 | } 11 | 12 | static subsequenceTests: []subsequenceTest = [ 13 | { 14 | "Valid case 1 ", 15 | "ace", 16 | "abcde", 17 | true, 18 | }, 19 | { 20 | "Invalid case 1", 21 | "aec", 22 | "abcde", 23 | false, 24 | }, 25 | { 26 | "Empty strings", 27 | "", 28 | "", 29 | true, 30 | }, 31 | { 32 | "s is more then t", 33 | "aeccccc", 34 | "abcde", 35 | false, 36 | }, 37 | { 38 | "s is empty", 39 | "", 40 | "abcde", 41 | true, 42 | }, 43 | { 44 | "Equal strings", 45 | "aec", 46 | "aec", 47 | true, 48 | }, 49 | { 50 | "Valid case 2", 51 | "pyr", 52 | "wpxqyrz", 53 | true, 54 | }, 55 | { 56 | "Invalid case 2", 57 | "prx", 58 | "wpxqyrz", 59 | false, 60 | }, 61 | ] 62 | 63 | #test 64 | fn testIsSubsequence(t: &testing::T) { 65 | for _, test in subsequenceTests { 66 | funcResult := IsSubsequence(test.s, test.t) 67 | if test.expected != funcResult { 68 | t.Errorf("expected: {}, got {}", test.expected, funcResult) 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /strings/reverse.jule: -------------------------------------------------------------------------------- 1 | // Reverse a given string and return reversed string. 2 | // e.g. input: Hello, output: olleH 3 | // Author: Jivanjamadar, mertcandav 4 | 5 | // Returns the reversed s. 6 | fn Reverse(s: str): str { 7 | mut runes := []rune(s) 8 | mut i, mut j := 0, len(runes)-1 9 | for i < j; i, j = i+1, j-1 { 10 | runes[i], runes[j] = runes[j], runes[i] 11 | } 12 | ret str(runes) 13 | } -------------------------------------------------------------------------------- /strings/reverse_test.jule: -------------------------------------------------------------------------------- 1 | #build test 2 | 3 | use "std/testing" 4 | 5 | struct reverseTest { 6 | name: str 7 | s: str 8 | expected: str 9 | } 10 | 11 | static reverseTests: []reverseTest = [ 12 | { 13 | "Simple case 1", 14 | "hello", 15 | "olleh", 16 | }, 17 | { 18 | "Simple case 2", 19 | "world", 20 | "dlrow", 21 | }, 22 | { 23 | "Palindrome case", 24 | "madam", 25 | "madam", 26 | }, 27 | { 28 | "Empty string", 29 | "", 30 | "", 31 | }, 32 | { 33 | "Single character", 34 | "a", 35 | "a", 36 | }, 37 | { 38 | "Numbers in string", 39 | "12345", 40 | "54321", 41 | }, 42 | { 43 | "Special characters", 44 | "!@#", 45 | "#@!", 46 | }, 47 | { 48 | "Mixed case", 49 | "HeLLo", 50 | "oLLeH", 51 | }, 52 | { 53 | "Spaces included", 54 | "hello world", 55 | "dlrow olleh", 56 | }, 57 | { 58 | "String with spaces only", 59 | " ", 60 | " ", 61 | }, 62 | { 63 | "Unicode characters", 64 | "çulha kuşu", 65 | "uşuk ahluç", 66 | }, 67 | ] 68 | 69 | #test 70 | fn testRevese(t: &testing::T) { 71 | for _, test in reverseTests { 72 | funcResult := Reverse(test.s) 73 | if test.expected != funcResult { 74 | t.Errorf("Expected: {}, got{}", test.expected, funcResult) 75 | } 76 | } 77 | } --------------------------------------------------------------------------------