//! Memoization with a single cache slot. This is useful in retained mode GUIs where the cache is not cleared between frames.

///|
/// Memoize a function with a single cache slot, taking one argument.
pub fn[K : Eq, V] memo_once1(f : (K) -> V) -> (K) -> V {
  let mut cache : (K, V)? = None
  fn(k : K) -> V {
    match cache {
      Some(pair) =>
        if pair.0 == k {
          pair.1
        } else {
          let v = f(k)
          cache = Some((k, v))
          v
        }
      None => {
        let v = f(k)
        cache = Some((k, v))
        v
      }
    }
  }
}

///|
/// Memoize a function with a single cache slot, taking two arguments.
pub fn[K1 : Eq, K2 : Eq, V] memo_once2(f : (K1, K2) -> V) -> (K1, K2) -> V {
  let mut cache : ((K1, K2), V)? = None
  fn(k1 : K1, k2 : K2) -> V {
    match cache {
      Some(pair) =>
        if pair.0 == (k1, k2) {
          pair.1
        } else {
          let v = f(k1, k2)
          cache = Some(((k1, k2), v))
          v
        }
      None => {
        let v = f(k1, k2)
        cache = Some(((k1, k2), v))
        v
      }
    }
  }
}

///|
/// Memoize a function with a single cache slot, taking three arguments.
pub fn[K1 : Eq, K2 : Eq, K3 : Eq, V] memo_once3(
  f : (K1, K2, K3) -> V,
) -> (K1, K2, K3) -> V {
  let mut cache : ((K1, K2, K3), V)? = None
  fn(k1 : K1, k2 : K2, k3 : K3) -> V {
    match cache {
      Some(pair) =>
        if pair.0 == (k1, k2, k3) {
          pair.1
        } else {
          let v = f(k1, k2, k3)
          cache = Some(((k1, k2, k3), v))
          v
        }
      None => {
        let v = f(k1, k2, k3)
        cache = Some(((k1, k2, k3), v))
        v
      }
    }
  }
}

///|
/// Memoize a function with a single cache slot, taking four arguments.
pub fn[K1 : Eq, K2 : Eq, K3 : Eq, K4 : Eq, V] memo_once4(
  f : (K1, K2, K3, K4) -> V,
) -> (K1, K2, K3, K4) -> V {
  let mut cache : ((K1, K2, K3, K4), V)? = None
  fn(k1 : K1, k2 : K2, k3 : K3, k4 : K4) -> V {
    match cache {
      Some(pair) =>
        if pair.0 == (k1, k2, k3, k4) {
          pair.1
        } else {
          let v = f(k1, k2, k3, k4)
          cache = Some(((k1, k2, k3, k4), v))
          v
        }
      None => {
        let v = f(k1, k2, k3, k4)
        cache = Some(((k1, k2, k3, k4), v))
        v
      }
    }
  }
}

///|
/// Memoize a function with a single cache slot, taking five arguments.
pub fn[K1 : Eq, K2 : Eq, K3 : Eq, K4 : Eq, K5 : Eq, V] memo_once5(
  f : (K1, K2, K3, K4, K5) -> V,
) -> (K1, K2, K3, K4, K5) -> V {
  let mut cache : ((K1, K2, K3, K4, K5), V)? = None
  fn(k1 : K1, k2 : K2, k3 : K3, k4 : K4, k5 : K5) -> V {
    match cache {
      Some(pair) =>
        if pair.0 == (k1, k2, k3, k4, k5) {
          pair.1
        } else {
          let v = f(k1, k2, k3, k4, k5)
          cache = Some(((k1, k2, k3, k4, k5), v))
          v
        }
      None => {
        let v = f(k1, k2, k3, k4, k5)
        cache = Some(((k1, k2, k3, k4, k5), v))
        v
      }
    }
  }
}