├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.zig └── src ├── ctmain.zig └── main.zig /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | zig-cache/* 3 | src/zig-cache/* 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language : shell 2 | 3 | os: 4 | - osx 5 | - linux 6 | 7 | addons: 8 | snaps: 9 | - name: zig 10 | classic: true 11 | channel: beta 12 | homebrew: 13 | update: true 14 | packages: 15 | - zig 16 | 17 | 18 | script: 19 | - zig test src/main.zig 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2020 Bhargav Srinivasan. 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zig itertools 2 | 3 | [![travis](https://travis-ci.org/onebsv1/zigraph.svg?branch=master)](https://travis-ci.org/github/onebsv1/zigraph) 4 | [![zig070](https://img.shields.io/badge/zig-0.7.0-orange)](https://ziglang.org/) 5 | 6 | This is an attempt to port the itertools library from python to zig, in order to introduce a functional 7 | paradigm to the language. Maintains efficiency by reducing temporary allocations, and moving through 8 | slices using an iterator. The library also includes some constructs such as map, filter and reduce which 9 | are part of the python builtin library which are essential for functional programming. 10 | 11 | And of course, their compile time counterparts! 12 | 13 | Suggestions and contributions are welcome. 14 | 15 | NOTE: De-initializing the iterator is the responsibility of the user. 16 | 17 | ## Generic Iterator 18 | 19 | ## Iterators 20 | 21 | ### Min 22 | 23 | ### Max 24 | 25 | ### Reduce 26 | 27 | ### Map 28 | 29 | ### Filter 30 | 31 | ### Accumulate 32 | 33 | ### Dropwhile 34 | 35 | ### Filterfalse 36 | 37 | ### Compress 38 | 39 | ### Takewhile 40 | 41 | ## Combinatoric Iterators 42 | 43 | ### Powerset 44 | 45 | ### Permutations 46 | 47 | #### Lexicographically ordered 48 | 49 | #### Efficient: Heap's method 50 | 51 | ### Combinations 52 | 53 | #### Generate from permutations 54 | 55 | #### Twiddle 56 | 57 | ### Product 58 | 59 | Note: The library does not support generators yet 60 | 61 | ## Sources 62 | 63 | #### [itertools](https://docs.python.org/3/library/itertools.html#itertools-recipes) 64 | 65 | #### Martin Heinz, [Tour of Python Itertools](https://martinheinz.dev/blog/16) 66 | 67 | #### MITx: 6.00.2x 68 | 69 | #### Sedgewick R, Permutation Generation Methods, Princeton University 70 | 71 | #### [ctregex](https://github.com/alexnask/ctregex.zig) by Alex Naskos 72 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const Builder = @import("std").build.Builder; 2 | 3 | pub fn build(b: *Builder) void { 4 | // Standard target options allows the person running `zig build` to choose 5 | // what target to build for. Here we do not override the defaults, which 6 | // means any target is allowed, and the default is native. Other options 7 | // for restricting supported target set are available. 8 | const target = b.standardTargetOptions(.{}); 9 | 10 | // Standard release options allow the person running `zig build` to select 11 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. 12 | const mode = b.standardReleaseOptions(); 13 | 14 | const exe = b.addExecutable("zitertools", "src/main.zig"); 15 | exe.setTarget(target); 16 | exe.setBuildMode(mode); 17 | exe.install(); 18 | 19 | const run_cmd = exe.run(); 20 | run_cmd.step.dependOn(b.getInstallStep()); 21 | 22 | const run_step = b.step("run", "Run the app"); 23 | run_step.dependOn(&run_cmd.step); 24 | } 25 | -------------------------------------------------------------------------------- /src/ctmain.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = std.builtin; 3 | const warn = std.debug.warn; 4 | const assertEqual = std.testing.expectEqual; 5 | 6 | pub fn ctreduce( 7 | comptime func: anytype, 8 | comptime iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?, 9 | comptime init: @typeInfo(@TypeOf(func)).Fn.return_type.? 10 | ) @typeInfo(@TypeOf(func)).Fn.return_type.? { 11 | comptime var ans = init; 12 | inline for (iterable) | item | { 13 | ans = func(ans, item); 14 | } 15 | return ans; 16 | } 17 | 18 | fn add8(a: u8, b: u8) u8 { 19 | return a + b; 20 | } 21 | fn mul8(a: u8, b: u8) u8 { 22 | return a * b; 23 | } 24 | 25 | test "Reduce" { 26 | comptime { 27 | comptime var A = [_]u8{1, 2, 4}; 28 | assertEqual(ctreduce(add8, &A, 0), 7); 29 | assertEqual(ctreduce(mul8, &A, 0), 0); 30 | assertEqual(ctreduce(mul8, &A, 1), 8); 31 | } 32 | warn("\r\n", .{}); 33 | } 34 | 35 | 36 | pub fn CTIterator(comptime T: type) type { 37 | return struct { 38 | iterable: []const T, 39 | index: usize = 0, 40 | len: usize, 41 | 42 | const Self = @This(); 43 | 44 | fn incr(it: *Self) void { 45 | it.index += 1; 46 | } 47 | 48 | pub fn next(it: *Self) ?*const T { 49 | if(it.index < it.len) { 50 | defer it.incr(); 51 | return &it.iterable[it.index]; 52 | } 53 | return null; 54 | } 55 | 56 | pub fn init(iter: []const T) Self { 57 | return Self{ 58 | .iterable = iter, 59 | .len = iter.len 60 | }; 61 | } 62 | }; 63 | } 64 | 65 | fn printTest(comptime T: type, comptime iter: *CTIterator(T), comptime ans: []T) void{ 66 | comptime var i: usize = 0; 67 | inline while (iter.next()) | item | { 68 | // warn("\r\n ans: {} item: {} ", .{ans[i], item.*}); 69 | assertEqual(ans[i], item.*); 70 | i += 1; 71 | } 72 | } 73 | 74 | test "Iterator" { 75 | comptime { 76 | comptime var A = [_]u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 77 | comptime var iter = CTIterator(u8).init(&A); 78 | printTest(u8, &iter, &A); 79 | } 80 | warn("\r\n", .{}); 81 | 82 | } 83 | 84 | pub fn ctmap( 85 | comptime func: anytype, 86 | comptime iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 87 | ) CTIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 88 | comptime var rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 89 | comptime var ans: []const rtype = &[0]rtype{}; 90 | 91 | inline for(iterable) | item, i | { 92 | ans = ans ++ &[1]rtype{ func(item) }; 93 | } 94 | 95 | return CTIterator(rtype).init(ans); 96 | } 97 | 98 | fn addOne(comptime a: u8) u8 { 99 | return a + @intCast(u8, 1); 100 | } 101 | 102 | test "Map" { 103 | comptime { 104 | comptime var A = [_]u8{'a', 'b', 'c'}; 105 | comptime var ans = [_]u8{'b', 'c', 'd'}; 106 | comptime var res = ctmap(addOne, &A); 107 | printTest(u8, &res, &ans); 108 | } 109 | warn("\r\n", .{}); 110 | } 111 | 112 | pub fn ctfilter( 113 | comptime func: anytype, 114 | comptime iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 115 | ) CTIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 116 | comptime var rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 117 | comptime var ans: []const rtype = &[0]rtype{}; 118 | 119 | inline for(iterable) | item, i | { 120 | if (func(item) == true) { 121 | ans = ans ++ &[1]rtype{ item }; 122 | } 123 | } 124 | 125 | return CTIterator(rtype).init(ans); 126 | } 127 | 128 | fn isLessThan10(a: u8) bool { 129 | if ( a < 10 ){ 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | test "Filter" { 136 | comptime { 137 | comptime var A = [_]u8{1, 'a', 2, 'b', 3, 'c', 'd', 'e'}; 138 | comptime var ans = [_]u8{1, 2, 3}; 139 | comptime var res = ctfilter(isLessThan10, &A); 140 | printTest(u8, &res, &ans); 141 | } 142 | warn("\r\n", .{}); 143 | } 144 | 145 | fn mul32 (comptime a: u32, comptime b: u32) u32 { 146 | return a * b; 147 | } 148 | 149 | pub fn starmap( 150 | comptime func: anytype, 151 | comptime iterables: []const []const @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 152 | ) CTIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 153 | const N: usize = iterables.len; 154 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 155 | comptime var ans: []const rtype = &[0]rtype{}; 156 | 157 | comptime var i: usize = 0; 158 | comptime var args = .{}; 159 | inline while ( i < N ) : ( i += 1 ) { 160 | var a = @call(.{}, mul32, .{1, 2} ); 161 | ans = ans ++ &[1]rtype{ a }; 162 | } 163 | 164 | return CTIterator(rtype).init(ans); 165 | } 166 | 167 | // test "Starmap" { 168 | // comptime { 169 | // comptime var A = &[_][]const u32{ 170 | // &[_]u32{1, 2}, 171 | // &[_]u32{3, 4} 172 | // }; 173 | // comptime var ans = [_]u32{2, 12}; 174 | 175 | // comptime var res = starmap(mul32, A); 176 | // defer res.deinit(); 177 | 178 | // //printTest(u32, &res, &ans); 179 | // } 180 | 181 | // warn("\r\n", .{}); 182 | // } -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const tallocator = std.heap.page_allocator; 4 | const warn = std.debug.warn; 5 | const assertEqual = std.testing.expectEqual; 6 | 7 | pub fn reduce( 8 | comptime func: anytype, 9 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?, 10 | comptime init: @typeInfo(@TypeOf(func)).Fn.return_type.? 11 | ) @typeInfo(@TypeOf(func)).Fn.return_type.? { 12 | var ans = init; 13 | for (iterable) | item | { 14 | ans = func(ans, item); 15 | } 16 | return ans; 17 | } 18 | 19 | fn add8(a: u8, b: u8) u8 { 20 | return a + b; 21 | } 22 | fn mul8(a: u8, b: u8) u8 { 23 | return a * b; 24 | } 25 | 26 | pub fn FunctionalIterator(comptime T: type) type { 27 | return struct { 28 | allocator: *std.mem.Allocator, 29 | items: []const T, 30 | index: usize = 0, 31 | len: usize, 32 | 33 | const Self = @This(); 34 | 35 | fn incr(it: *Self) void { 36 | it.index += 1; 37 | } 38 | 39 | pub fn next(it: *Self) ?*const T { 40 | if(it.index < it.len) { 41 | defer it.incr(); 42 | return &it.items[it.index]; 43 | } 44 | return null; 45 | } 46 | 47 | pub fn init(alloc: *std.mem.Allocator, iter: []const T) Self { 48 | return Self{ 49 | .allocator = alloc, 50 | .items = iter, 51 | .len = iter.len 52 | }; 53 | } 54 | 55 | pub fn deinit(it: *Self) void { 56 | it.allocator.free(it.items); 57 | } 58 | }; 59 | } 60 | 61 | fn printTest(comptime T: type, iter: *FunctionalIterator(T), ans: []T) void{ 62 | var i: usize = 0; 63 | while (iter.next()) | item | { 64 | assertEqual(ans[i], item.*); 65 | i += 1; 66 | } 67 | } 68 | 69 | pub fn map( 70 | allocat: *std.mem.Allocator, 71 | comptime func: anytype, 72 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 73 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 74 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 75 | var ans: []rtype = allocat.alloc(rtype, iterable.len) catch unreachable; 76 | errdefer allocat.destroy(ans.ptr); 77 | 78 | for(iterable) | item, i | { 79 | ans[i] = func(item); 80 | } 81 | 82 | return FunctionalIterator(rtype).init(allocat, ans); 83 | } 84 | 85 | fn addOne(a: u8) u8 { 86 | return a + @intCast(u8, 1); 87 | } 88 | 89 | pub fn filter( 90 | allocat: *std.mem.Allocator, 91 | comptime func: anytype, 92 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 93 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 94 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 95 | var ans: []rtype = allocat.alloc(rtype, iterable.len) catch unreachable; 96 | errdefer allocat.destroy(ans.ptr); 97 | 98 | var j: usize = 0; 99 | for (iterable) | item | { 100 | if (func(item) == true) { 101 | ans[j] = item; 102 | j += 1; 103 | } 104 | } 105 | 106 | _ = allocat.shrink(ans, j); 107 | ans.len = j; 108 | 109 | return FunctionalIterator(rtype).init(allocat, ans); 110 | } 111 | 112 | fn isLessThan10(a: u8) bool { 113 | if ( a < 10 ){ 114 | return true; 115 | } 116 | return false; 117 | } 118 | 119 | pub fn accumulate( 120 | allocat: *std.mem.Allocator, 121 | comptime func: anytype, 122 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?, 123 | comptime init: @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 124 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.return_type.?) { 125 | const rtype = @typeInfo(@TypeOf(func)).Fn.return_type.?; 126 | var ans: []rtype = allocat.alloc(rtype, iterable.len) catch unreachable; 127 | errdefer allocat.destroy(ans.ptr); 128 | 129 | var i: usize = 0; 130 | ans[0] = func(init, iterable[0]); 131 | i += 1; 132 | while (i < iterable.len) : ( i += 1 ) { 133 | ans[i] = func(ans[i-1], iterable[i]); 134 | } 135 | 136 | return FunctionalIterator(rtype).init(allocat, ans); 137 | } 138 | 139 | fn add(a: u32, b:u32) u32 { 140 | return a + b; 141 | } 142 | 143 | fn mul(a:u32, b:u32) u32 { 144 | return a * b; 145 | } 146 | 147 | pub fn chain( 148 | allocat: *std.mem.Allocator, 149 | comptime func: anytype, 150 | iterables: []const []const @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 151 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.return_type.?) { 152 | var totalLength: usize = 0; 153 | for (iterables) | iterable | { totalLength += iterable.len; } 154 | const rtype = @typeInfo(@TypeOf(func)).Fn.return_type.?; 155 | var ans: []rtype = allocat.alloc(rtype, totalLength) catch unreachable; 156 | errdefer allocat.destroy(ans.ptr); 157 | 158 | var index: usize = 0; 159 | for (iterables) | iterable | { 160 | for (iterable) | item | { 161 | ans[index] = func(item); 162 | index += 1; 163 | } 164 | } 165 | return FunctionalIterator(rtype).init(allocat, ans); 166 | } 167 | 168 | fn addOne8(a: i32) i32 { 169 | return a+1; 170 | } 171 | 172 | pub fn min( 173 | allocat: *std.mem.Allocator, 174 | comptime func: anytype, 175 | iterables: []const []const @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 176 | ) @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? { 177 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 178 | 179 | var min_value: rtype = iterables[0][0]; 180 | for (iterables) | iterable | { 181 | for (iterable) | item | { 182 | if ( func(item, min_value) == true ) { 183 | min_value = item; 184 | } 185 | } 186 | } 187 | return min_value; 188 | } 189 | 190 | fn compareFnMin(a: i32, b: i32) bool { 191 | if (a < b) { 192 | return true; 193 | } 194 | return false; 195 | } 196 | 197 | pub fn max( 198 | allocat: *std.mem.Allocator, 199 | comptime func: anytype, 200 | iterables: []const []const @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 201 | ) @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? { 202 | return min(allocat, func, iterables); 203 | } 204 | 205 | fn compareFnMax(a: i32, b: i32) bool { 206 | if (a > b) { 207 | return true; 208 | } 209 | return false; 210 | } 211 | 212 | pub fn filterfalse( 213 | allocat: *std.mem.Allocator, 214 | comptime func: anytype, 215 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 216 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 217 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 218 | var ans: []rtype = allocat.alloc(rtype, iterable.len) catch unreachable; 219 | errdefer allocat.destroy(ans.ptr); 220 | 221 | var j: usize = 0; 222 | for (iterable) | item, i | { 223 | if (func(item) == false) { 224 | ans[j] = item; 225 | j += 1; 226 | } 227 | } 228 | 229 | _ = allocat.shrink(ans, j); 230 | ans.len = j; 231 | 232 | return FunctionalIterator(rtype).init(allocat, ans); 233 | } 234 | 235 | pub fn dropwhile( 236 | allocat: *std.mem.Allocator, 237 | comptime func: anytype, 238 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 239 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 240 | var totalLength: usize = iterable.len; 241 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 242 | var ans: []rtype = allocat.alloc(rtype, totalLength) catch unreachable; 243 | errdefer allocat.destroy(ans.ptr); 244 | 245 | var i: usize = 0; 246 | while ( func(iterable[i]) == true ) : ( i += 1 ) {} 247 | 248 | var j: usize = 0; 249 | while ( i < totalLength ) : ({ j += 1; i += 1; }) { 250 | ans[j] = iterable[i]; 251 | } 252 | 253 | _ = allocat.shrink(ans, j); 254 | ans.len = j; 255 | 256 | return FunctionalIterator(rtype).init(allocat, ans); 257 | } 258 | 259 | pub fn takewhile( 260 | allocat: *std.mem.Allocator, 261 | comptime func: anytype, 262 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 263 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 264 | var totalLength: usize = iterable.len; 265 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 266 | var ans: []rtype = allocat.alloc(rtype, totalLength) catch unreachable; 267 | errdefer allocat.destroy(ans.ptr); 268 | 269 | var i: usize = 0; 270 | while ( func(iterable[i]) == true ) : ( i += 1 ) { 271 | ans[i] = iterable[i]; 272 | } 273 | 274 | _ = allocat.shrink(ans, i); 275 | ans.len = i; 276 | 277 | return FunctionalIterator(rtype).init(allocat, ans); 278 | } 279 | 280 | fn recurranceRelation(N: usize) !usize { 281 | switch (N) { 282 | 0 => { return 1; }, 283 | 1 => { return 2; }, 284 | 2 => { return 4; }, 285 | 3 => { return 12; }, 286 | 4 => { return 32; }, 287 | 5 => { return 80; }, 288 | else => {} 289 | } 290 | var T: usize = 80; 291 | if ( N > 5 ) { 292 | var i: usize = 6; 293 | while (i < N+1) : ( i += 1 ) { 294 | T = (2 * i * T) / (i-1); 295 | } 296 | } 297 | return T; 298 | } 299 | 300 | pub fn powerset( 301 | allocat: *std.mem.Allocator, 302 | comptime func: anytype, 303 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 304 | ) !FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 305 | // Total number of sets is pow(2, iterable.len) 306 | // the total number of elements can be calculated by (N*1) + ((N-1)*2) + ((N-2)*3) ... + (2*(N-1)) + (1*N) 307 | // this is given by the recurrance relation T(N) = 2*N*T(N-1)//(N-1) => should be the allocated length 308 | // a func needs to be constructed to get the result of the above recurrance relation. 309 | // Source: the recurrance relation is mine, and the powerset algorithm is from MITx: 6.00.2x 310 | const totalLength: usize = @intCast(usize, 1) << @truncate(std.math.Log2Int(usize), iterable.len); 311 | const allocatLength: usize = recurranceRelation(iterable.len) catch unreachable; 312 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 313 | var ans: []rtype = allocat.alloc(rtype, allocatLength) catch unreachable; 314 | errdefer allocat.free(ans); 315 | 316 | var index: usize = 0; 317 | var i: usize = 0; 318 | while( i < totalLength ) : ( i += 1 ) { 319 | var j: usize = 0; 320 | while ( j < iterable.len ) : ( j += 1 ) { 321 | // Bit shift counter i of the array j bits to the right, this makes the 322 | // last bit of i become the test bit to see if the index should be incl. 323 | if ( ( ( i >> @truncate(std.math.Log2Int(usize), j) ) % 2 ) == 1 ) { 324 | ans[index] = func(iterable[j]); 325 | index += 1; 326 | } 327 | } 328 | } 329 | 330 | return FunctionalIterator(rtype).init(allocat, ans); 331 | 332 | } 333 | 334 | fn mulOne32 (a: u32) u32 { 335 | return a * 1; 336 | } 337 | 338 | fn fact(N: u128) u128 { 339 | switch (N) { 340 | 0 => { return 1; }, 341 | 1 => { return 1; }, 342 | else => {} 343 | } 344 | 345 | var T: u128 = 1; 346 | var i: u128 = 2; 347 | 348 | while( i < N+1 ) : ( i += 1 ) { 349 | T = T * i; 350 | } 351 | return T; 352 | } 353 | 354 | pub fn permutations_lex( 355 | allocat: *std.mem.Allocator, 356 | comptime func: anytype, 357 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 358 | ) !FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 359 | // There are N! possible permutations of a set of cardinality N 360 | // The number of elements of N! such sets is N! * N => should be allocated for the result 361 | const N: usize = iterable.len; 362 | const allocatLength: usize = @intCast(usize, fact(N) * N); 363 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 364 | var ans: []rtype = allocat.alloc(rtype, allocatLength) catch unreachable; 365 | errdefer allocat.free(ans); 366 | 367 | var index: usize = 0; 368 | for (iterable) | item | { 369 | ans[index] = func(item); 370 | index += 1; 371 | } 372 | 373 | 374 | var c: u128 = 0; 375 | var numSets: u128 = fact(N) - 1; 376 | 377 | while ( c < numSets ) : ( c += 1 ) { 378 | var i: usize = N - 2; 379 | var j: usize = N - 1; 380 | 381 | while ( iterable[i] > iterable[i+1] ) : ( i -= 1 ) {} 382 | while ( iterable[j] < iterable[i] ) : ( j -= 1 ) {} 383 | 384 | mem.swap(rtype, &iterable[i], &iterable[j]); 385 | 386 | i += 1; 387 | j = N - 1; 388 | 389 | while ( i < j ) : ({ i += 1; j -= 1; }) { 390 | mem.swap(rtype, &iterable[i], &iterable[j]); 391 | } 392 | 393 | for (iterable) | item | { 394 | ans[index] = func(item); 395 | index += 1; 396 | } 397 | } 398 | 399 | return FunctionalIterator(rtype).init(allocat, ans); 400 | 401 | } 402 | 403 | pub fn permutations( 404 | allocat: *std.mem.Allocator, 405 | comptime func: anytype, 406 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.? 407 | ) !FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 408 | // There are N! possible permutations of a set of cardinality N 409 | // The number of elements of N! such sets is N! * N => should be allocated for the result 410 | const N: usize = iterable.len; 411 | const allocatLength: usize = @intCast(usize, fact(N) * N); 412 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 413 | var ans: []rtype = allocat.alloc(rtype, allocatLength) catch unreachable; 414 | errdefer allocat.free(ans); 415 | 416 | var c: []usize = allocat.alloc(usize, N) catch unreachable; 417 | defer allocat.free(c); 418 | 419 | var i: usize = 0; 420 | var index: usize = 0; 421 | for (iterable) | item, x | { 422 | c[x] = 0; 423 | ans[index] = func(item); 424 | index += 1; 425 | } 426 | 427 | while ( i < N ) { 428 | 429 | if (c[i] < i) { 430 | if ( (i%2) == 0 ) { 431 | mem.swap(rtype, &iterable[0], &iterable[i]); 432 | } else { 433 | mem.swap(rtype, &iterable[c[i]], &iterable[i]); 434 | } 435 | 436 | for (iterable) | item | { 437 | ans[index] = func(item); 438 | index += 1; 439 | } 440 | 441 | c[i] += 1; 442 | i = 0; 443 | 444 | } else { 445 | c[i] = 0; 446 | i += 1; 447 | } 448 | 449 | } 450 | 451 | return FunctionalIterator(rtype).init(allocat, ans); 452 | 453 | } 454 | 455 | fn mulOne1 (a: u1) u1 { 456 | return a * 1; 457 | } 458 | 459 | pub fn combinations( 460 | allocat: *std.mem.Allocator, 461 | comptime func: anytype, 462 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?, 463 | choose: usize 464 | ) !FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 465 | // Did not find a good answer to this problem. 466 | // Will be using permutation for this, and making a bit array of size iter.len (n) 467 | // Out of this we can set k bits to simulate n choose k, and generate all permutations 468 | // of the same to get the k-combination set of the iterable. 469 | 470 | // There will be nCk sets of k elements each, so the total memory that needs to be allocated 471 | // is nCk * k for all the elements. allocatLength = ( fact(N) / (fact(k) * fact(N-k)) ) * k 472 | 473 | const N: usize = iterable.len; 474 | const k: usize = choose; 475 | const allocatLength: usize = @intCast(usize, ( fact(N) / (fact(k) * fact(N-k)) ) * k); 476 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 477 | var ans: []rtype = allocat.alloc(rtype, allocatLength) catch unreachable; 478 | errdefer allocat.free(ans); 479 | 480 | var c: []u1 = allocat.alloc(u1, N) catch unreachable; 481 | defer allocat.free(c); 482 | 483 | var i: usize = 0; 484 | while ( i < N ) : ( i += 1 ) { 485 | if ( i < k ) { 486 | c[i] = 1; 487 | } else { 488 | c[i] = 0; 489 | } 490 | } 491 | 492 | // Taking a bit array of n and choosing k bits, and getting all permutations 493 | // of such a configuration. We will use this to generate the k-combination set of the iterable. 494 | var res = permutations(allocat, mulOne1, c) catch unreachable; 495 | defer res.deinit(); 496 | 497 | var bufset = std.StringHashMap(void).init(allocat); 498 | defer bufset.deinit(); 499 | var buffer = std.ArrayList(u8).init(allocat); 500 | defer buffer.deinit(); 501 | try buffer.resize(N); 502 | 503 | i = 0; 504 | while ( res.next() ) | item | { 505 | if ( (i != 0) and (i % N) == 0 ) { 506 | if ( ! bufset.contains(buffer.items) ) { 507 | try bufset.put( buffer.toOwnedSlice(), .{} ); 508 | try buffer.resize(N); 509 | } else { 510 | buffer.shrinkRetainingCapacity(0); 511 | } 512 | } 513 | buffer.appendAssumeCapacity( @as(u8, (0x00|item.*) ) ); 514 | i += 1; 515 | } 516 | 517 | 518 | i = 0; 519 | var j: usize = 0; 520 | var bit: u8 = 0x01; 521 | var index: usize = 0; 522 | var it = bufset.iterator(); 523 | while ( it.next() ) | buf | { 524 | j = 0; 525 | while ( j < N ) { 526 | if ( (bit & buf.key[j]) >= 1 ) { 527 | ans[index] = func(iterable[j]); 528 | index += 1; 529 | } 530 | j += 1; 531 | } 532 | i += 1; 533 | } 534 | 535 | // Deinit all the keys of the bufset. 536 | it = bufset.iterator(); 537 | while ( it.next() ) | buf | { 538 | allocat.free(buf.key); 539 | } 540 | 541 | return FunctionalIterator(rtype).init(allocat, ans); 542 | 543 | } 544 | 545 | pub fn compress( 546 | allocat: *std.mem.Allocator, 547 | comptime func: anytype, 548 | iterable: []@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?, 549 | selectors: []u1 550 | ) FunctionalIterator(@typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?) { 551 | const N: usize = iterable.len; 552 | const rtype = @typeInfo(@TypeOf(func)).Fn.args[0].arg_type.?; 553 | var ans: []rtype = allocat.alloc(rtype, N) catch unreachable; 554 | errdefer allocat.destroy(ans.ptr); 555 | 556 | var i: usize = 0; 557 | var index: usize = 0; 558 | while ( i < N ) : ( i += 1 ) { 559 | if (selectors[i] == 1) { 560 | ans[index] = func(iterable[i]); 561 | index += 1; 562 | } 563 | } 564 | 565 | _ = allocat.shrink(ans, index); 566 | ans.len = index; 567 | 568 | return FunctionalIterator(rtype).init(allocat, ans); 569 | } 570 | 571 | pub fn product( 572 | allocat: *std.mem.Allocator, 573 | comptime T: type, 574 | iterables: []const []const T 575 | ) !FunctionalIterator(T) { 576 | // My own, novel solution to generate the cartesian product of the iterables. 577 | // The total memeory required for this is the product of the length of each iterable 578 | // and the total number of iterables. 579 | const N: usize = iterables.len; 580 | var totalLength: usize = 1; 581 | var indices = std.ArrayList(usize).init(allocat); 582 | defer indices.deinit(); 583 | 584 | for (iterables) | iterable | { 585 | totalLength *= iterable.len; 586 | _ = try indices.append(0); 587 | } 588 | var ans: []T = allocat.alloc(T, totalLength*N) catch unreachable; 589 | errdefer allocat.destroy(ans.ptr); 590 | 591 | var i: usize = 0; 592 | var j: usize = 0; 593 | var k: usize = N - 1; 594 | var flag: u1 = 1; 595 | var index: usize = 0; 596 | while( i < totalLength ) : ( i += 1 ) { 597 | j = 0; 598 | while ( j < N ) : ( j += 1 ) { 599 | ans[index] = iterables[j][indices.items[j]]; 600 | index += 1; 601 | } 602 | 603 | k = N - 1; 604 | flag = 1; 605 | while ( k >= 0 and flag > 0 ) { 606 | indices.items[k] = (indices.items[k]+1) % iterables[k].len; 607 | if ( indices.items[k] != 0 ) { 608 | flag = 0; 609 | } 610 | if ( k > 0 ) { 611 | k -= 1; 612 | } 613 | } 614 | 615 | } 616 | 617 | return FunctionalIterator(T).init(allocat, ans); 618 | } 619 | 620 | 621 | test "Reduce" { 622 | var A = [_]u8{1, 2, 4}; 623 | assertEqual(reduce(add8, &A, 0), 7); 624 | assertEqual(reduce(mul8, &A, 0), 0); 625 | assertEqual(reduce(mul8, &A, 1), 8); 626 | warn("\r\n", .{}); 627 | } 628 | 629 | test "FunctionalIterator" { 630 | 631 | var A: []u8 = tallocator.alloc(u8, 10) catch unreachable; 632 | errdefer tallocator.destroy(A.ptr); 633 | 634 | var i: u8 = 0; 635 | while ( i < 10 ) : ( i += 1 ) { 636 | A[i] = i; 637 | } 638 | var iter = FunctionalIterator(u8).init(tallocator, A); 639 | defer iter.deinit(); 640 | 641 | printTest(u8, &iter, A); 642 | 643 | var B = std.ArrayList(u8).init(tallocator); 644 | defer B.deinit(); 645 | 646 | i = 0; 647 | while ( i < 10 ) : ( i += 1 ) { 648 | _ = try B.append(i*2); 649 | } 650 | 651 | var its: []u8 = B.toOwnedSlice(); 652 | errdefer tallocator.free(its); 653 | 654 | var iter2 = FunctionalIterator(u8).init(tallocator, its); 655 | defer iter2.deinit(); 656 | 657 | printTest(u8, &iter2, its); 658 | warn("\r\n", .{}); 659 | 660 | } 661 | 662 | test "Map" { 663 | var A = [_]u8{'a', 'b', 'c'}; 664 | var ans = [_]u8{'b', 'c', 'd'}; 665 | 666 | var res = map(tallocator, addOne, &A); 667 | defer res.deinit(); 668 | 669 | printTest(u8, &res, &ans); 670 | warn("\r\n", .{}); 671 | } 672 | 673 | test "Filter" { 674 | var A = [_]u8{1, 'a', 2, 'b', 3, 'c', 'd', 'e'}; 675 | var ans = [_]u8{1, 2, 3}; 676 | 677 | var res = filter(tallocator, isLessThan10, &A); 678 | defer res.deinit(); 679 | 680 | printTest(u8, &res, &ans); 681 | warn("\r\n", .{}); 682 | } 683 | 684 | test "Accumulate" { 685 | var A = [_]u32{1, 2, 4}; 686 | var ans1 = [_]u32{1, 3, 7}; 687 | var ans2 = [_]u32{0, 0, 0}; 688 | var ans3 = [_]u32{1, 2, 8}; 689 | 690 | var res = accumulate(tallocator, add, &A, 0); 691 | defer res.deinit(); 692 | 693 | var res2 = accumulate(tallocator, mul, &A, 0); 694 | defer res2.deinit(); 695 | 696 | var res3 = accumulate(tallocator, mul, &A, 1); 697 | defer res3.deinit(); 698 | 699 | printTest(u32, &res, &ans1); 700 | printTest(u32, &res2, &ans2); 701 | printTest(u32, &res3, &ans3); 702 | warn("\r\n", .{}); 703 | } 704 | 705 | test "Chain" { 706 | var A = &[_][]const i32{ 707 | &[_]i32{1, 2}, 708 | &[_]i32{3, 4} 709 | }; 710 | 711 | var ans = [_]i32{2,3,4,5}; 712 | var res = chain(tallocator, addOne8, A); 713 | defer res.deinit(); 714 | 715 | printTest(i32, &res, &ans); 716 | 717 | warn("\r\n", .{}); 718 | } 719 | 720 | test "Min" { 721 | var A = &[_][]const i32{ 722 | &[_]i32{1, 2}, 723 | &[_]i32{3, 4} 724 | }; 725 | assertEqual(min(tallocator, compareFnMin, A), 1); 726 | warn("\r\n", .{}); 727 | } 728 | 729 | test "Max" { 730 | var A = &[_][]const i32{ 731 | &[_]i32{1, 2}, 732 | &[_]i32{3, 4} 733 | }; 734 | assertEqual(max(tallocator, compareFnMax, A), 4); 735 | warn("\r\n", .{}); 736 | } 737 | 738 | test "FilterFalse" { 739 | var A = [_]u8{1, 'a', 2, 'b', 3, 'c', 'd', 'e'}; 740 | var ans = [_]u8{'a', 'b', 'c', 'd', 'e'}; 741 | 742 | var res = filterfalse(tallocator, isLessThan10, &A); 743 | defer res.deinit(); 744 | 745 | printTest(u8, &res, &ans); 746 | warn("\r\n", .{}); 747 | } 748 | 749 | test "Dropwhile" { 750 | var A = [_]u8{1, 2, 3, 5, 'a', 1, 'b', 2, 'c', 11, 'd', 'e', 1, 3, 4}; 751 | var ans = [_]u8{'a', 1, 'b', 2, 'c', 11, 'd', 'e', 1, 3, 4}; 752 | 753 | var res = dropwhile(tallocator, isLessThan10, &A); 754 | defer res.deinit(); 755 | 756 | printTest(u8, &res, &ans); 757 | warn("\r\n", .{}); 758 | } 759 | 760 | test "Takewhile" { 761 | var A = [_]u8{1, 2, 3, 5, 'a', 1, 'b', 2, 'c', 11, 'd', 'e', 1, 3, 4}; 762 | var ans = [_]u8{1, 2, 3, 5}; 763 | 764 | var res = takewhile(tallocator, isLessThan10, &A); 765 | defer res.deinit(); 766 | 767 | printTest(u8, &res, &ans); 768 | warn("\r\n", .{}); 769 | } 770 | 771 | test "PowerSet" { 772 | var A = [_]u32{1, 2, 3, 4}; 773 | var ans = [_]u32{1, 2, 1, 2, 3, 1, 3, 2, 3, 1, 2, 3, 4, 1, 4, 2, 4, 1, 2, 4, 3, 4, 1, 3, 4, 2, 3, 4, 1, 2, 3, 4}; 774 | 775 | var res = powerset(tallocator, mulOne32, &A) catch unreachable; 776 | defer res.deinit(); 777 | 778 | printTest(u32, &res, &ans); 779 | 780 | var A1 = [_]u32{1, 2, 3}; 781 | var ans1 = [_]u32{1, 2, 1, 2, 3, 1, 3, 2, 3, 1, 2, 3}; 782 | 783 | var res1 = powerset(tallocator, mulOne32, &A1) catch unreachable; 784 | defer res1.deinit(); 785 | 786 | printTest(u32, &res1, &ans1); 787 | 788 | var A2 = [_]u32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 789 | 790 | var res2 = powerset(tallocator, mulOne32, &A2) catch unreachable; 791 | defer res2.deinit(); 792 | 793 | const allocLength: usize = recurranceRelation(A2.len) catch unreachable; 794 | assertEqual(res2.len, allocLength); 795 | 796 | warn("\r\n", .{}); 797 | } 798 | 799 | test "Permutations" { 800 | var ans: u128 = fact(3); 801 | assertEqual(ans, 6); 802 | 803 | 804 | var A = [_]u32{1, 2, 3}; 805 | var ans1 = [_]u32{1, 2, 3, 1, 3, 2, 2, 1, 3, 2, 3, 1, 3, 1, 2, 3, 2, 1}; 806 | 807 | var res = permutations_lex(tallocator, mulOne32, &A) catch unreachable; 808 | defer res.deinit(); 809 | 810 | printTest(u32, &res, &ans1); 811 | 812 | var A2 = [_]u32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 813 | var res1 = permutations(tallocator, mulOne32, &A2) catch unreachable; 814 | defer res1.deinit(); 815 | 816 | // printTest(u32, &res, &ans1); 817 | 818 | warn("\r\n", .{}); 819 | } 820 | 821 | test "Combinations" { 822 | var ans: u128 = fact(3); 823 | assertEqual(ans, 6); 824 | 825 | var A = [_]u32{1, 2, 3, 4}; 826 | var ans1 = [_]u32{1, 3, 2, 3, 2, 4, 1, 4, 1, 2, 3, 4}; 827 | 828 | var res = combinations(tallocator, mulOne32, &A, 2) catch unreachable; 829 | defer res.deinit(); 830 | 831 | printTest(u32, &res, &ans1); 832 | warn("\r\n", .{}); 833 | } 834 | 835 | test "Compress" { 836 | var A = [_]u32{1, 2, 3, 4, 5, 6, 7, 8}; 837 | var selectors = [_]u1{0, 1, 1, 1, 1, 0, 0, 0}; 838 | var ans = [_]u32{2,3,4,5}; 839 | 840 | var res = compress(tallocator, mulOne32, &A, &selectors); 841 | defer res.deinit(); 842 | 843 | printTest(u32, &res, &ans); 844 | warn("\r\n", .{}); 845 | } 846 | 847 | test "Cartesian Product" { 848 | var A = &[_][]const u8{ 849 | &[_]u8{'a', 'b', 'c'}, 850 | &[_]u8{'x', 'y'}, 851 | &[_]u8{'d', 'e', 'f', 'l', 'm'}, 852 | &[_]u8{'t', 'v', 'r'} 853 | }; 854 | 855 | var ans = [_]u8{'a', 'x', 'd', 't', 'a', 'x', 'd', 'v', 'a', 'x', 'd', 'r', 'a', 'x', 'e', 't', 'a', 'x', 'e', 'v', 'a', 'x', 'e', 'r', 'a', 'x', 'f', 't', 'a', 'x', 'f', 'v', 'a', 'x', 'f', 'r', 'a', 'x', 'l', 't', 'a', 'x', 'l', 'v', 'a', 'x', 'l', 'r', 'a', 'x', 'm', 't', 'a', 'x', 'm', 'v', 'a', 'x', 'm', 'r', 'a', 'y', 'd', 't', 'a', 'y', 'd', 'v', 'a', 'y', 'd', 'r', 'a', 'y', 'e', 't', 'a', 'y', 'e', 'v', 'a', 'y', 'e', 'r', 'a', 'y', 'f', 't', 'a', 'y', 'f', 'v', 'a', 'y', 'f', 'r', 'a', 'y', 'l', 't', 'a', 'y', 'l', 'v', 'a', 'y', 'l', 'r', 'a', 'y', 'm', 't', 'a', 'y', 'm', 'v', 'a', 'y', 'm', 'r', 'b', 'x', 'd', 't', 'b', 'x', 'd', 'v', 'b', 'x', 'd', 'r', 'b', 'x', 'e', 't', 'b', 'x', 'e', 'v', 'b', 'x', 'e', 'r', 'b', 'x', 'f', 't', 'b', 'x', 'f', 'v', 'b', 'x', 'f', 'r', 'b', 'x', 'l', 't', 'b', 'x', 'l', 'v', 'b', 'x', 'l', 'r', 'b', 'x', 'm', 't', 'b', 'x', 'm', 'v', 'b', 'x', 'm', 'r', 'b', 'y', 'd', 't', 'b', 'y', 'd', 'v', 'b', 'y', 'd', 'r', 'b', 'y', 'e', 't', 'b', 'y', 'e', 'v', 'b', 'y', 'e', 'r', 'b', 'y', 'f', 't', 'b', 'y', 'f', 'v', 'b', 'y', 'f', 'r', 'b', 'y', 'l', 't', 'b', 'y', 'l', 'v', 'b', 'y', 'l', 'r', 'b', 'y', 'm', 't', 'b', 'y', 'm', 'v', 'b', 'y', 'm', 'r', 'c', 'x', 'd', 't', 'c', 'x', 'd', 'v', 'c', 'x', 'd', 'r', 'c', 'x', 'e', 't', 'c', 'x', 'e', 'v', 'c', 'x', 'e', 'r', 'c', 'x', 'f', 't', 'c', 'x', 'f', 'v', 'c', 'x', 'f', 'r', 'c', 'x', 'l', 't', 'c', 'x', 'l', 'v', 'c', 'x', 'l', 'r', 'c', 'x', 'm', 't', 'c', 'x', 'm', 'v', 'c', 'x', 'm', 'r', 'c', 'y', 'd', 't', 'c', 'y', 'd', 'v', 'c', 'y', 'd', 'r', 'c', 'y', 'e', 't', 'c', 'y', 'e', 'v', 'c', 'y', 'e', 'r', 'c', 'y', 'f', 't', 'c', 'y', 'f', 'v', 'c', 'y', 'f', 'r', 'c', 'y', 'l', 't', 'c', 'y', 'l', 'v', 'c', 'y', 'l', 'r', 'c', 'y', 'm', 't', 'c', 'y', 'm', 'v', 'c', 'y', 'm', 'r'}; 856 | var res = product(tallocator, u8, A) catch unreachable; 857 | defer res.deinit(); 858 | 859 | printTest(u8, &res, &ans); 860 | warn("\r\n", .{}); 861 | } 862 | 863 | 864 | pub fn main() !void { 865 | var A2 = [_]u32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; 866 | var res1 = permutations(tallocator, mulOne32, &A2) catch unreachable; 867 | defer res1.deinit(); 868 | warn("\r\n", .{}); 869 | return; 870 | } 871 | --------------------------------------------------------------------------------