// File: manpat[P]iterator_trait/main.cpp
// Function: main
int main() {
	int arr[] { 1, 2, 3, 4, 5 };

	// Construct an iterator
	int sum = 0;
	for (const auto v : iter(arr)) {
		sum += v;
	}

	std::printf("sum:  %d\n", sum);

	// Map values
	int sum2 = 0;
	for (const auto v : iter(arr).map([](int v) { return v * 2; })) {
		sum2 += v;
	}

	std::printf("sum2: %d\n", sum2);

	// Sum squares with map-fold
	int sum3 = iter(arr)
		.map([](int v) { return v * v; })
		.fold(0, [](int a, int) { return a + 1; });

	std::printf("sum3: %d\n", sum3);

	// Construct a string from an std::array + char doubling and uppercasing
	// NOTE: implicit cast from int return of std::toupper to char in fold
	auto str = iter(std::array<char, 3>{'a', 'b', 'c'})
		.map([](char v) { return std::toupper(v); })
		.fold(std::string{}, [](auto& a, char c) { a += c; return a += c; });

	std::printf("str:  %s\n", str.c_str());

	// Enumerate const char[]
	for (const auto kv : iter("ABCD").enumerate()) {
		if (kv.second)
			std::printf("%d '%c' ", (int)kv.first, kv.second);
	}

	std::putchar('\n');

	// Filter every second letter in string
	std::string str_to_filter {"Hello I'm a string"};
	auto filter_it = iter(str_to_filter).enumerate()
		.filter([](const auto& kv) { return !(kv.first & 1); })
		.map([](const auto& kv) { return kv.second; });

	std::printf("'%s' -> '", str_to_filter.data());
	for (auto c : filter_it) {
		std::putchar(c);
	}
	std::puts("'");

	// Zip two strings together
	const char* zip_string_a = "HloImasrn";
	const char* zip_string_b = "el '  tig";

	auto iter_a = iter(zip_string_a, zip_string_a + std::strlen(zip_string_a));
	auto iter_b = iter(zip_string_b, zip_string_b + std::strlen(zip_string_b));
	auto zip_string = iter_a.zip(iter_b)
		.fold(std::string{}, [](auto& s, auto ab) {
			s += ab.first;
			return s += ab.second;
		});

	std::printf("%s\n", zip_string.data());

	// Chain two strings together
	auto chain_string = iter("Hello").chain(iter(" world"))
		.filter([](char c) { return c != 0; }) // Skip the null terminator
		.fold(std::string{}, [](auto& s, auto c) { return s += c; });

	std::printf("%s\n", chain_string.data());
}

// Assembly:
subq $0x5c8, %rsp            # imm = 0x5C8
movl $0x0, 0x5c4(%rsp)
movaps 0x5d87(%rip), %xmm0     # 0x7010
movaps %xmm0, 0x5b0(%rsp)
movl $0x5, 0x5c0(%rsp)
movl $0x0, 0x5ac(%rsp)
leaq 0x590(%rsp), %rdi
movq %rdi, 0x98(%rsp)
leaq 0x5b0(%rsp), %rsi
callq 0x4290
movq 0x98(%rsp), %rax
movq %rax, 0x5a0(%rsp)
movq 0x5a0(%rsp), %rsi
leaq 0x570(%rsp), %rdi
callq 0x42e0
movq 0x5a0(%rsp), %rsi
leaq 0x550(%rsp), %rdi
callq 0x4310
jmp 0x1300
jmp 0x1302
leaq 0x570(%rsp), %rdi
leaq 0x550(%rsp), %rsi
callq 0x4340
movb %al, 0x97(%rsp)
jmp 0x1320
movb 0x97(%rsp), %al
testb $0x1, %al
jne 0x1385
jmp 0x132d
leaq 0x550(%rsp), %rdi
callq 0x44c0
leaq 0x570(%rsp), %rdi
callq 0x44c0
jmp 0x13e4
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x13d2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x550(%rsp), %rdi
callq 0x44c0
jmp 0x13d2
leaq 0x570(%rsp), %rdi
callq 0x43b0
movl %eax, 0x90(%rsp)
jmp 0x139b
movl 0x90(%rsp), %eax
movl %eax, 0x540(%rsp)
movl 0x540(%rsp), %eax
addl 0x5ac(%rsp), %eax
movl %eax, 0x5ac(%rsp)
leaq 0x570(%rsp), %rdi
callq 0x43f0
jmp 0x13cd
jmp 0x1302
leaq 0x570(%rsp), %rdi
callq 0x44c0
jmp 0x1e10
movl 0x5ac(%rsp), %esi
leaq 0x5c53(%rip), %rdi      # 0x7045
xorl %eax, %eax
callq 0x1030
movl $0x0, 0x53c(%rsp)
leaq 0x508(%rsp), %rdi
movq %rdi, 0x80(%rsp)
leaq 0x5b0(%rsp), %rsi
callq 0x4290
movq 0x80(%rsp), %rsi
leaq 0x518(%rsp), %rdi
movq %rdi, 0x88(%rsp)
leaq 0x507(%rsp), %rdx
callq 0x1e20
movq 0x88(%rsp), %rax
movq %rax, 0x530(%rsp)
movq 0x530(%rsp), %rsi
leaq 0x4d8(%rsp), %rdi
callq 0x1e70
movq 0x530(%rsp), %rsi
leaq 0x4b0(%rsp), %rdi
callq 0x1ea0
jmp 0x1482
jmp 0x1484
leaq 0x4d8(%rsp), %rdi
leaq 0x4b0(%rsp), %rsi
callq 0x1ed0
movb %al, 0x7f(%rsp)
jmp 0x149f
movb 0x7f(%rsp), %al
testb $0x1, %al
jne 0x1501
jmp 0x14a9
leaq 0x4b0(%rsp), %rdi
callq 0x2030
leaq 0x4d8(%rsp), %rdi
callq 0x2030
jmp 0x155a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1548
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x4b0(%rsp), %rdi
callq 0x2030
jmp 0x1548
leaq 0x4d8(%rsp), %rdi
callq 0x1f40
movl %eax, 0x78(%rsp)
jmp 0x1514
movl 0x78(%rsp), %eax
movl %eax, 0x4ac(%rsp)
movl 0x4ac(%rsp), %eax
addl 0x53c(%rsp), %eax
movl %eax, 0x53c(%rsp)
leaq 0x4d8(%rsp), %rdi
callq 0x1f80
jmp 0x1543
jmp 0x1484
leaq 0x4d8(%rsp), %rdi
callq 0x2030
jmp 0x1e10
movl 0x53c(%rsp), %esi
leaq 0x5ae7(%rip), %rdi      # 0x704f
xorl %eax, %eax
movl %eax, 0x58(%rsp)
movb %al, 0x5f(%rsp)
callq 0x1030
leaq 0x480(%rsp), %rdi
movq %rdi, 0x48(%rsp)
leaq 0x5b0(%rsp), %rsi
callq 0x4290
movq 0x48(%rsp), %rsi
leaq 0x490(%rsp), %rdi
movq %rdi, 0x50(%rsp)
leaq 0x47f(%rsp), %rdx
callq 0x2060
movq 0x50(%rsp), %rdi
movl 0x58(%rsp), %esi
leaq 0x47e(%rsp), %rdx
callq 0x20b0
movl %eax, %ecx
movb 0x5f(%rsp), %al
movl %ecx, 0x4a8(%rsp)
movl 0x4a8(%rsp), %esi
leaq 0x5a78(%rip), %rdi      # 0x7059
callq 0x1030
movb $0x61, 0x42d(%rsp)
movb $0x62, 0x42e(%rsp)
movb $0x63, 0x42f(%rsp)
leaq 0x430(%rsp), %rdi
movq %rdi, 0x60(%rsp)
leaq 0x42d(%rsp), %rsi
callq 0x44f0
movq 0x60(%rsp), %rsi
leaq 0x440(%rsp), %rdi
movq %rdi, 0x68(%rsp)
leaq 0x42c(%rsp), %rdx
callq 0x21c0
leaq 0x408(%rsp), %rdi
movq %rdi, 0x70(%rsp)
callq 0x1110
movq 0x68(%rsp), %rsi
movq 0x70(%rsp), %rdx
leaq 0x458(%rsp), %rdi
leaq 0x407(%rsp), %rcx
callq 0x2210
jmp 0x166a
leaq 0x408(%rsp), %rdi
callq 0x10a0
leaq 0x458(%rsp), %rdi
callq 0x1040
movq %rax, %rsi
leaq 0x59d5(%rip), %rdi      # 0x7063
xorl %eax, %eax
callq 0x1030
jmp 0x1697
leaq 0x59cf(%rip), %rsi      # 0x706d
leaq 0x3d0(%rsp), %rdi
callq 0x4540
jmp 0x16ad
leaq 0x3e0(%rsp), %rdi
leaq 0x3d0(%rsp), %rsi
callq 0x4590
jmp 0x16c4
leaq 0x3e0(%rsp), %rax
movq %rax, 0x3f8(%rsp)
movq 0x3f8(%rsp), %rsi
leaq 0x398(%rsp), %rdi
callq 0x45d0
jmp 0x16eb
movq 0x3f8(%rsp), %rsi
leaq 0x360(%rsp), %rdi
callq 0x4600
jmp 0x1702
jmp 0x1704
leaq 0x398(%rsp), %rdi
leaq 0x360(%rsp), %rsi
callq 0x4630
movb %al, 0x47(%rsp)
jmp 0x171f
movb 0x47(%rsp), %al
testb $0x1, %al
jne 0x17ca
jmp 0x172d
leaq 0x360(%rsp), %rdi
callq 0x47d0
leaq 0x398(%rsp), %rdi
callq 0x47d0
jmp 0x186b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x408(%rsp), %rdi
callq 0x10a0
jmp 0x1e10
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1e03
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1859
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x360(%rsp), %rdi
callq 0x47d0
jmp 0x1859
leaq 0x398(%rsp), %rdi
callq 0x46a0
movb %dl, 0x37(%rsp)
movq %rax, 0x38(%rsp)
jmp 0x17e2
movb 0x37(%rsp), %al
movq 0x38(%rsp), %rcx
movq %rcx, 0x340(%rsp)
movb %al, 0x348(%rsp)
movq 0x340(%rsp), %rax
movq %rax, 0x350(%rsp)
movb 0x348(%rsp), %al
movb %al, 0x358(%rsp)
cmpb $0x0, 0x358(%rsp)
je 0x1843
movl 0x350(%rsp), %esi
movsbl 0x358(%rsp), %edx
leaq 0x583a(%rip), %rdi      # 0x7072
xorl %eax, %eax
callq 0x1030
jmp 0x1841
jmp 0x1843
jmp 0x1845
leaq 0x398(%rsp), %rdi
callq 0x4700
jmp 0x1854
jmp 0x1704
leaq 0x398(%rsp), %rdi
callq 0x47d0
jmp 0x1e03
movl $0xa, %edi
callq 0x1120
jmp 0x1877
leaq 0x31f(%rsp), %rdi
movq %rdi, 0x28(%rsp)
callq 0x1150
movq 0x28(%rsp), %rdx
leaq 0x57e6(%rip), %rsi      # 0x707b
leaq 0x320(%rsp), %rdi
callq 0x1100
jmp 0x18a4
leaq 0x31f(%rsp), %rdi
callq 0x10d0
leaq 0x288(%rsp), %rdi
leaq 0x320(%rsp), %rsi
callq 0x4800
jmp 0x18c8
leaq 0x2a0(%rsp), %rdi
leaq 0x288(%rsp), %rsi
callq 0x4860
jmp 0x18df
leaq 0x2c0(%rsp), %rdi
leaq 0x2a0(%rsp), %rsi
leaq 0x287(%rsp), %rdx
callq 0x2370
jmp 0x18fe
leaq 0x2e8(%rsp), %rdi
leaq 0x2c0(%rsp), %rsi
leaq 0x286(%rsp), %rdx
callq 0x23c0
jmp 0x191d
leaq 0x2c0(%rsp), %rdi
callq 0x2410
leaq 0x2a0(%rsp), %rdi
callq 0x48a0
leaq 0x288(%rsp), %rdi
callq 0x48b0
leaq 0x320(%rsp), %rdi
callq 0x10f0
movq %rax, %rsi
leaq 0x5733(%rip), %rdi      # 0x708e
xorl %eax, %eax
callq 0x1030
jmp 0x1964
leaq 0x2e8(%rsp), %rax
movq %rax, 0x278(%rsp)
movq 0x278(%rsp), %rsi
leaq 0x238(%rsp), %rdi
callq 0x2420
jmp 0x198b
movq 0x278(%rsp), %rsi
leaq 0x1f8(%rsp), %rdi
callq 0x2450
jmp 0x19a2
jmp 0x19a4
leaq 0x238(%rsp), %rdi
leaq 0x1f8(%rsp), %rsi
callq 0x2480
movb %al, 0x27(%rsp)
jmp 0x19bf
movb 0x27(%rsp), %al
testb $0x1, %al
jne 0x1ae9
jmp 0x19cd
leaq 0x1f8(%rsp), %rdi
callq 0x2600
leaq 0x238(%rsp), %rdi
callq 0x2600
jmp 0x1b3e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x31f(%rsp), %rdi
callq 0x10d0
jmp 0x1e03
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1df6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1a85
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1a78
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x2c0(%rsp), %rdi
callq 0x2410
leaq 0x2a0(%rsp), %rdi
callq 0x48a0
leaq 0x288(%rsp), %rdi
callq 0x48b0
jmp 0x1df6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1de9
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1b2c
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x1f8(%rsp), %rdi
callq 0x2600
jmp 0x1b2c
leaq 0x238(%rsp), %rdi
callq 0x24f0
movb %al, 0x26(%rsp)
jmp 0x1afc
movb 0x26(%rsp), %al
movb %al, 0x1f7(%rsp)
movsbl 0x1f7(%rsp), %edi
callq 0x1120
jmp 0x1b16
jmp 0x1b18
leaq 0x238(%rsp), %rdi
callq 0x2530
jmp 0x1b27
jmp 0x19a4
leaq 0x238(%rsp), %rdi
callq 0x2600
jmp 0x1de9
leaq 0x5551(%rip), %rdi      # 0x7096
callq 0x1130
jmp 0x1b4c
leaq 0x5545(%rip), %rax      # 0x7098
movq %rax, 0x1e8(%rsp)
leaq 0x5540(%rip), %rax      # 0x70a2
movq %rax, 0x1e0(%rsp)
movq 0x1e8(%rsp), %rdi
movq %rdi, 0x18(%rsp)
callq 0x1060
movq 0x18(%rsp), %rsi
movq %rsi, %rdx
addq %rax, %rdx
leaq 0x1d0(%rsp), %rdi
callq 0x48d0
jmp 0x1b96
movq 0x1e0(%rsp), %rdi
movq %rdi, 0x10(%rsp)
callq 0x1060
movq 0x10(%rsp), %rsi
movq %rsi, %rdx
addq %rax, %rdx
leaq 0x1c0(%rsp), %rdi
callq 0x48d0
jmp 0x1bc2
leaq 0x180(%rsp), %rdi
leaq 0x1d0(%rsp), %rsi
leaq 0x1c0(%rsp), %rdx
callq 0x4910
jmp 0x1be1
leaq 0x160(%rsp), %rdi
movq %rdi, 0x8(%rsp)
callq 0x1110
movq 0x8(%rsp), %rdx
leaq 0x1a0(%rsp), %rdi
leaq 0x180(%rsp), %rsi
leaq 0x15f(%rsp), %rcx
callq 0x2630
jmp 0x1c17
leaq 0x160(%rsp), %rdi
callq 0x10a0
leaq 0x1a0(%rsp), %rdi
callq 0x10f0
movq %rax, %rsi
leaq 0x542e(%rip), %rdi      # 0x7069
xorl %eax, %eax
callq 0x1030
jmp 0x1c44
leaq 0x5461(%rip), %rsi      # 0x70ac
leaq 0xe0(%rsp), %rdi
callq 0x4960
jmp 0x1c5a
leaq 0x5451(%rip), %rsi      # 0x70b2
leaq 0xd0(%rsp), %rdi
callq 0x4a00
jmp 0x1c70
leaq 0xf0(%rsp), %rdi
leaq 0xe0(%rsp), %rsi
leaq 0xd0(%rsp), %rdx
callq 0x49b0
jmp 0x1c8f
leaq 0x110(%rsp), %rdi
leaq 0xf0(%rsp), %rsi
leaq 0xcf(%rsp), %rdx
callq 0x27b0
jmp 0x1cae
leaq 0xa8(%rsp), %rdi
movq %rdi, (%rsp)
callq 0x1110
movq (%rsp), %rdx
leaq 0x138(%rsp), %rdi
leaq 0x110(%rsp), %rsi
leaq 0xa7(%rsp), %rcx
callq 0x2800
jmp 0x1ce2
leaq 0xa8(%rsp), %rdi
callq 0x10a0
leaq 0x138(%rsp), %rdi
callq 0x10f0
movq %rax, %rsi
leaq 0x5363(%rip), %rdi      # 0x7069
xorl %eax, %eax
callq 0x1030
jmp 0x1d0f
leaq 0x138(%rsp), %rdi
callq 0x10a0
leaq 0x1a0(%rsp), %rdi
callq 0x10a0
leaq 0x2e8(%rsp), %rdi
callq 0x2970
leaq 0x320(%rsp), %rdi
callq 0x10a0
leaq 0x458(%rsp), %rdi
callq 0x10a0
movl 0x5c4(%rsp), %eax
addq $0x5c8, %rsp            # imm = 0x5C8
retq
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x160(%rsp), %rdi
callq 0x10a0
jmp 0x1de9
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
jmp 0x1ddc
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0xa8(%rsp), %rdi
callq 0x10a0
jmp 0x1ddc
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x548(%rsp)
movl %eax, 0x544(%rsp)
leaq 0x138(%rsp), %rdi
callq 0x10a0
leaq 0x1a0(%rsp), %rdi
callq 0x10a0
leaq 0x2e8(%rsp), %rdi
callq 0x2970
leaq 0x320(%rsp), %rdi
callq 0x10a0
leaq 0x458(%rsp), %rdi
callq 0x10a0
movq 0x548(%rsp), %rdi
callq 0x1140
nopl (%rax)