# ==============================
# FUNGSI PENDUKUNG
# ==============================

validasi_pencarian <- function(x, target, urut = FALSE) {
  if (!is.numeric(x) || length(dim(x)) > 1L ||
      anyNA(x) || any(!is.finite(x))) {
    stop("x harus larik numerik satu dimensi tanpa NA atau Inf")
  }
  if (!is.numeric(target) || length(target) != 1L ||
      !is.finite(target)) {
    stop("target harus satu angka berhingga")
  }
  if (urut && is.unsorted(x, strictly = FALSE)) {
    stop("Data harus terurut menaik")
  }
  invisible(TRUE)
}

# Sequential search dengan Boolean
seq_boolean <- function(x, target) {
  validasi_pencarian(x, target)
  i <- 1L
  n <- length(x)
  ketemu <- FALSE
  while (i <= n && !ketemu) {
    if (x[i] == target) {
      ketemu <- TRUE
    } else {
      i <- i + 1L
    }
  }
  if (ketemu) return(i)
  NA_integer_
}

# Sequential search tanpa Boolean
seq_tanpa_boolean <- function(x, target) {
  validasi_pencarian(x, target)
  for (i in seq_along(x)) {
    if (x[i] == target) return(i)
  }
  NA_integer_
}

# Sequential search pada data terurut
seq_terurut <- function(x, target) {
  validasi_pencarian(x, target, urut = TRUE)
  i <- 1L
  n <- length(x)
  while (i <= n && x[i] < target) {
    i <- i + 1L
  }
  if (i <= n && x[i] == target) return(i)
  NA_integer_
}

# Sequential search dengan sentinel
seq_sentinel <- function(x, target) {
  validasi_pencarian(x, target)
  n <- length(x)
  kerja <- c(x, target)
  i <- 1L
  while (kerja[i] != target) {
    i <- i + 1L
  }
  if (i <= n) return(i)
  NA_integer_
}

# Binary search dengan Boolean
bin_boolean <- function(x, target) {
  validasi_pencarian(x, target, urut = TRUE)
  kiri <- 1L
  kanan <- length(x)
  ketemu <- FALSE
  posisi <- NA_integer_
  while (kiri <= kanan && !ketemu) {
    tengah <- kiri + (kanan - kiri) %/% 2
    if (x[tengah] == target) {
      ketemu <- TRUE
      posisi <- tengah
    } else if (x[tengah] < target) {
      kiri <- tengah + 1L
    } else {
      kanan <- tengah - 1L
    }
  }
  posisi
}

# Binary search tanpa Boolean
bin_tanpa_boolean <- function(x, target) {
  validasi_pencarian(x, target, urut = TRUE)
  kiri <- 1L
  kanan <- length(x)
  while (kiri <= kanan) {
    tengah <- kiri + (kanan - kiri) %/% 2
    if (x[tengah] == target) return(tengah)
    if (x[tengah] < target) {
      kiri <- tengah + 1L
    } else {
      kanan <- tengah - 1L
    }
  }
  NA_integer_
}

# Binary search posisi pertama (untuk data berulang)
bin_pertama <- function(x, target) {
  validasi_pencarian(x, target, urut = TRUE)
  kiri <- 1L
  kanan <- length(x)
  posisi <- NA_integer_
  while (kiri <= kanan) {
    tengah <- kiri + (kanan - kiri) %/% 2
    if (x[tengah] >= target) {
      if (x[tengah] == target) posisi <- tengah
      kanan <- tengah - 1L
    } else {
      kiri <- tengah + 1L
    }
  }
  posisi
}

# Jejak binary (tabel langkah)
jejak_binary <- function(x, target) {
  validasi_pencarian(x, target, urut = TRUE)
  kiri <- 1L
  kanan <- length(x)
  jejak <- data.frame(langkah = integer(), kiri = double(),
                      kanan = double(), tengah = double(), nilai = double(),
                      keputusan = character())
  while (kiri <= kanan) {
    tengah <- kiri + (kanan - kiri) %/% 2
    keputusan <- if (x[tengah] == target) "Ditemukan" else
      if (x[tengah] < target) "Cari kanan" else "Cari kiri"
    jejak <- rbind(jejak, data.frame(
      langkah = nrow(jejak) + 1L, kiri = kiri, kanan = kanan,
      tengah = tengah, nilai = x[tengah], keputusan = keputusan))
    if (x[tengah] == target) break
    if (x[tengah] < target) kiri <- tengah + 1L else
      kanan <- tengah - 1L
  }
  jejak
}

# Hitung kunjungan sequential
kunjungan_sequential <- function(x, target) {
  validasi_pencarian(x, target)
  banyak <- 0L
  for (i in seq_along(x)) {
    banyak <- banyak + 1L
    if (x[i] == target) break
  }
  banyak
}

# Data produk simulasi
produk <- data.frame(
  kode = c(104, 101, 107, 103, 106, 102, 105),
  nama = c("Telur", "Beras", "Kopi", "Minyak",
           "Garam", "Gula", "Tepung"),
  stok = c(25, 40, 18, 30, 50, 35, 28)
)
urutan <- order(produk$kode)
produk_urut <- produk[urutan, , drop = FALSE]

# ==============================
# LATIHAN 11
# ==============================

lat_x <- c(12, 7, 19, 7, 25)

# 11A. Sequential Boolean, target 7
print(seq_boolean(lat_x, 7))                 # hasil: 2
## [1] 2
# 11B. Sequential tanpa Boolean, target 8
print(seq_tanpa_boolean(lat_x, 8))           # hasil: NA
## [1] NA
print(seq_tanpa_boolean(numeric(), 8))       # hasil: NA
## [1] NA
# 11C. Sequential terurut, cari 12
print(seq_terurut(c(5, 10, 15, 20, 25), 12)) # hasil: NA
## [1] NA
# 11D. Sentinel, target 99
lat_awal <- lat_x
print(seq_sentinel(lat_x, 99))               # hasil: NA
## [1] NA
print(identical(lat_x, lat_awal))            # TRUE = data asli tidak berubah
## [1] TRUE
# 11E. Posisi pertama vs semua posisi
print(match(7, lat_x))                       # hasil: 2
## [1] 2
print(which(lat_x == 7))                     # hasil: 2 4
## [1] 2 4
# 11F. Data mengandung NA
print(tryCatch(seq_boolean(c(12, NA, 19), 19),
               error = function(e) conditionMessage(e)))
## [1] "x harus larik numerik satu dimensi tanpa NA atau Inf"
# ==============================
# LATIHAN 12
# ==============================

lat_urut <- seq(10, 100, by = 10)

# 12A. Jejak binary target 80
print(jejak_binary(lat_urut, 80))
##   langkah kiri kanan tengah nilai  keputusan
## 1       1    1    10      5    50 Cari kanan
## 2       2    6    10      8    80  Ditemukan
# 12B. Kasus batas dua varian binary
print(bin_boolean(lat_urut, 10))             # hasil: 1
## [1] 1
print(bin_tanpa_boolean(lat_urut, 100))      # hasil: 10
## [1] 10
print(bin_boolean(lat_urut, 105))            # hasil: NA
## [1] NA
print(bin_tanpa_boolean(lat_urut, 105))      # hasil: NA
## [1] NA
# 12C. Data berulang
print(bin_boolean(c(10, 20, 20, 20, 30), 20))  # hasil: 3
## [1] 3
print(bin_pertama(c(10, 20, 20, 20, 30), 20))  # hasil: 2
## [1] 2
# 12D. Cari kode 103 pada tabel produk terurut
p <- bin_tanpa_boolean(produk_urut$kode, 103)
print(c(posisi_terurut = p, baris_asli = urutan[p]))
## posisi_terurut     baris_asli 
##              3              4
print(produk_urut[p, , drop = FALSE])
##   kode   nama stok
## 4  103 Minyak   30
# 12E. Data tidak terurut ditolak
print(tryCatch(bin_boolean(c(30, 10, 20), 20),
               error = function(e) conditionMessage(e)))
## [1] "Data harus terurut menaik"
# 12F. Bandingkan kunjungan sequential vs binary, target 1024
print(c(sequential = kunjungan_sequential(seq_len(1024), 1024),
        binary = nrow(jejak_binary(seq_len(1024), 1024))))
## sequential     binary 
##       1024         11