package main

import (
	"fmt"
	"strings"
)

// Map mengubah setiap elemen []T menjadi []U lewat fungsi ubah.
func Map[T, U any](data []T, ubah func(T) U) []U {
	hasil := make([]U, 0, len(data))
	for _, v := range data {
		hasil = append(hasil, ubah(v))
	}
	return hasil
}

// Filter menyimpan elemen []T yang memenuhi fungsi syarat.
func Filter[T any](data []T, syarat func(T) bool) []T {
	hasil := make([]T, 0, len(data))
	for _, v := range data {
		if syarat(v) {
			hasil = append(hasil, v)
		}
	}
	return hasil
}

// Reduce melipat seluruh elemen []T menjadi satu nilai bertipe sama.
func Reduce[T any](data []T, awal T, gabung func(T, T) T) T {
	total := awal
	for _, v := range data {
		total = gabung(total, v)
	}
	return total
}

func main() {
	harga := []int{25000, 15000, 48000, 12000}

	// T=int dan U=float64 tersimpulkan dari argumen.
	diskon := Map(harga, func(h int) float64 { return float64(h) / 1000 })
	fmt.Println("Dalam ribuan :", diskon)

	mahal := Filter(harga, func(h int) bool { return h >= 20000 })
	fmt.Println(">= 20 ribu   :", mahal)

	total := Reduce(harga, 0, func(a, b int) int { return a + b })
	fmt.Println("Total        :", total)

	// Instansiasi eksplisit: kedua type parameter ditulis manual.
	nama := []string{"budi", "siti", "dewi"}
	kapital := Map[string, string](nama, strings.ToUpper)
	fmt.Println("Kapital      :", kapital)
}
