//! Memoization utilities. based on work of [@hackwaly](https://taolun.moonbitlang.com/t/topic/958/2)

///|
pub fn[K : Hash + Eq, V] memoize1(f : (K) -> V) -> (K) -> V {
  let map = @hashmap.new()
  fn(k : K) -> V {
    match map.get(k) {
      Some(v) => v
      None => {
        let v = f(k)
        map[k] = v
        v
      }
    }
  }
}

///|
pub fn[K1 : Hash + Eq, K2 : Hash + Eq, V] memoize2(
  f : (K1, K2) -> V,
) -> (K1, K2) -> V {
  let map = @hashmap.new()
  fn(k1 : K1, k2 : K2) -> V {
    match map.get((k1, k2)) {
      Some(v) => v
      None => {
        let v = f(k1, k2)
        map[(k1, k2)] = v
        v
      }
    }
  }
}

///|
pub fn[K1 : Hash + Eq, K2 : Hash + Eq, K3 : Hash + Eq, V] memoize3(
  f : (K1, K2, K3) -> V,
) -> (K1, K2, K3) -> V {
  let map = @hashmap.new()
  fn(k1 : K1, k2 : K2, k3 : K3) -> V {
    match map.get((k1, k2, k3)) {
      Some(v) => v
      None => {
        let v = f(k1, k2, k3)
        map[(k1, k2, k3)] = v
        v
      }
    }
  }
}

///|
pub fn[K1 : Hash + Eq, K2 : Hash + Eq, K3 : Hash + Eq, K4 : Hash + Eq, V] memoize4(
  f : (K1, K2, K3, K4) -> V,
) -> (K1, K2, K3, K4) -> V {
  let map = @hashmap.new()
  fn(k1 : K1, k2 : K2, k3 : K3, k4 : K4) -> V {
    match map.get((k1, k2, k3, k4)) {
      Some(v) => v
      None => {
        let v = f(k1, k2, k3, k4)
        map[(k1, k2, k3, k4)] = v
        v
      }
    }
  }
}

///|
pub fn[
  K1 : Hash + Eq,
  K2 : Hash + Eq,
  K3 : Hash + Eq,
  K4 : Hash + Eq,
  K5 : Hash + Eq,
  V,
] memoize5(
  f : (K1, K2, K3, K4, K5) -> V,
) -> (K1, K2, K3, K4, K5) -> V {
  let map = @hashmap.new()
  fn(k1 : K1, k2 : K2, k3 : K3, k4 : K4, k5 : K5) -> V {
    match map.get((k1, k2, k3, k4, k5)) {
      Some(v) => v
      None => {
        let v = f(k1, k2, k3, k4, k5)
        map[(k1, k2, k3, k4, k5)] = v
        v
      }
    }
  }
}