package main

import "fmt"

// Stack adalah tipe generik yang menyimpan elemen bertipe T.
type Stack[T any] struct {
	data []T
}

// NewStack adalah konstruktor untuk Stack.
func NewStack[T any]() *Stack[T] {
	return &Stack[T]{}
}

// Receiver WAJIB menyebut type parameter: (s *Stack[T]).
func (s *Stack[T]) Push(nilai T) {
	s.data = append(s.data, nilai)
}

func (s *Stack[T]) Pop() (T, bool) {
	var kosong T
	if len(s.data) == 0 {
		return kosong, false
	}
	terakhir := s.data[len(s.data)-1]
	s.data = s.data[:len(s.data)-1]
	return terakhir, true
}

func (s *Stack[T]) Len() int {
	return len(s.data)
}

// Pair memakai dua type parameter dengan constraint berbeda.
type Pair[K comparable, V any] struct {
	Kunci K
	Nilai V
}

func main() {
	// Konstruktor tanpa argumen: instansiasi harus eksplisit.
	angka := NewStack[int]()
	angka.Push(10)
	angka.Push(20)
	angka.Push(30)

	v, ok := angka.Pop()
	fmt.Println("Pop angka   :", v, ok)
	fmt.Println("Sisa elemen :", angka.Len())

	teks := NewStack[string]()
	teks.Push("halo")
	teks.Push("go")
	s, _ := teks.Pop()
	fmt.Println("Pop teks    :", s)

	// Generic type sebagai tipe nilai map.
	konfig := map[string]Pair[string, int]{
		"timeout": {Kunci: "detik", Nilai: 30},
	}
	fmt.Println("Timeout     :", konfig["timeout"].Kunci, konfig["timeout"].Nilai)
}
