diff --git a/common/common.h b/common/common.h
index 0d452cf0..a652f181 100644
--- a/common/common.h
+++ b/common/common.h
@@ -87,14 +87,15 @@ enum common_sampler_type {
     COMMON_SAMPLER_TYPE_NONE        = 0,
     COMMON_SAMPLER_TYPE_DRY         = 1,
     COMMON_SAMPLER_TYPE_TOP_K       = 2,
-    COMMON_SAMPLER_TYPE_TOP_P       = 3,
-    COMMON_SAMPLER_TYPE_MIN_P       = 4,
-  //COMMON_SAMPLER_TYPE_TFS_Z       = 5,
-    COMMON_SAMPLER_TYPE_TYPICAL_P   = 6,
-    COMMON_SAMPLER_TYPE_TEMPERATURE = 7,
-    COMMON_SAMPLER_TYPE_XTC         = 8,
-    COMMON_SAMPLER_TYPE_INFILL      = 9,
-    COMMON_SAMPLER_TYPE_PENALTIES   = 10,
+    COMMON_SAMPLER_TYPE_BOT_K       = 3,
+    COMMON_SAMPLER_TYPE_TOP_P       = 4,
+    COMMON_SAMPLER_TYPE_MIN_P       = 5,
+  //COMMON_SAMPLER_TYPE_TFS_Z       = 6,
+    COMMON_SAMPLER_TYPE_TYPICAL_P   = 7,
+    COMMON_SAMPLER_TYPE_TEMPERATURE = 8,
+    COMMON_SAMPLER_TYPE_XTC         = 9,
+    COMMON_SAMPLER_TYPE_INFILL      = 10,
+    COMMON_SAMPLER_TYPE_PENALTIES   = 11,
 };

 // dimensionality reduction methods, used by cvector-generator
@@ -111,6 +112,7 @@ struct common_params_sampling {
     int32_t n_probs            = 0;     // if greater than 0, output the probabilities of top n_probs tokens.
     int32_t min_keep           = 0;     // 0 = disabled, otherwise samplers should return at least min_keep tokens
     int32_t top_k              = 40;    // <= 0 to use vocab size
+    int32_t bot_k              = 40;    // <= 0 to use vocab size
     float   top_p              = 0.95f; // 1.0 = disabled
     float   min_p              = 0.05f; // 0.0 = disabled
     float   xtc_probability    = 0.00f; // 0.0 = disabled
diff --git a/common/sampling.cpp b/common/sampling.cpp
index e83a971c..26d32d02 100644
--- a/common/sampling.cpp
+++ b/common/sampling.cpp
@@ -178,6 +178,9 @@ struct common_sampler * common_sampler_init(const struct llama_model * model, co
                 case COMMON_SAMPLER_TYPE_TOP_K:
                     llama_sampler_chain_add(result->chain, llama_sampler_init_top_k    (params.top_k));
                     break;
+                case COMMON_SAMPLER_TYPE_BOT_K:
+                    llama_sampler_chain_add(result->chain, llama_sampler_init_bot_k    (params.top_k));
+                    break;
                 case COMMON_SAMPLER_TYPE_TOP_P:
                     llama_sampler_chain_add(result->chain, llama_sampler_init_top_p    (params.top_p, params.min_keep));
                     break;
@@ -430,6 +433,7 @@ std::vector<common_sampler_type> common_sampler_types_from_names(const std::vect
     std::unordered_map<std::string, common_sampler_type> sampler_canonical_name_map {
         { "dry",         COMMON_SAMPLER_TYPE_DRY },
         { "top_k",       COMMON_SAMPLER_TYPE_TOP_K },
+        { "bot_k",       COMMON_SAMPLER_TYPE_BOT_K },
         { "top_p",       COMMON_SAMPLER_TYPE_TOP_P },
         { "typ_p",       COMMON_SAMPLER_TYPE_TYPICAL_P },
         { "min_p",       COMMON_SAMPLER_TYPE_MIN_P },
@@ -443,6 +447,7 @@ std::vector<common_sampler_type> common_sampler_types_from_names(const std::vect
     // make it ready for both system names and input names
     std::unordered_map<std::string, common_sampler_type> sampler_alt_name_map {
         { "top-k",       COMMON_SAMPLER_TYPE_TOP_K },
+        { "bot-k",       COMMON_SAMPLER_TYPE_BOT_K },
         { "top-p",       COMMON_SAMPLER_TYPE_TOP_P },
         { "nucleus",     COMMON_SAMPLER_TYPE_TOP_P },
         { "typical-p",   COMMON_SAMPLER_TYPE_TYPICAL_P },
diff --git a/include/llama.h b/include/llama.h
index 0295a51f..061fbedd 100644
--- a/include/llama.h
+++ b/include/llama.h
@@ -1112,6 +1112,9 @@ extern "C" {
     DEPRECATED(LLAMA_API struct llama_sampler * llama_sampler_init_softmax    (void),
         "will be removed in the future (see https://github.com/ggerganov/llama.cpp/pull/9896#discussion_r1800920915)");

+    /// @details Finding the dual of natural language
+    LLAMA_API struct llama_sampler * llama_sampler_init_bot_k      (int32_t k);
+
     /// @details Top-K sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
     LLAMA_API struct llama_sampler * llama_sampler_init_top_k      (int32_t k);

diff --git a/src/llama-sampling.cpp b/src/llama-sampling.cpp
index ef5a576c..3f3f8249 100644
--- a/src/llama-sampling.cpp
+++ b/src/llama-sampling.cpp
@@ -301,6 +301,29 @@ static void llama_sampler_top_k_impl(llama_token_data_array * cur_p, int32_t k)
     cur_p->size = k;
 }

+// TODO: I wonder if this is sorta broken, seems to sometimes give sensible completions
+
+static void llama_sampler_bot_k_impl(llama_token_data_array * cur_p, int32_t k) {
+    // TODO: move bucket sort to separate function so that top_p/typical/softmax first is equally fast
+    // if (k >= (int32_t)cur_p->size) {
+    //     return;
+    // }
+
+    if (k <= 0) {
+        k = cur_p->size;
+    }
+
+    k = std::min(k, (int) cur_p->size);
+
+    // Sort scores in ascending order
+    auto comp = [](const llama_token_data & a, const llama_token_data & b) {
+        return a.logit < b.logit;
+    };
+    std::partial_sort(cur_p->data, cur_p->data + k, cur_p->data + cur_p->size, comp);
+    cur_p->sorted = true;
+    cur_p->size = k;
+}
+
 static uint32_t get_rng_seed(uint32_t seed) {
     if (seed == LLAMA_DEFAULT_SEED) {
         // use system clock if std::random_device is not a true RNG
@@ -641,6 +664,48 @@ struct llama_sampler * llama_sampler_init_softmax() {
     };
 }

+// bot-k
+
+struct llama_sampler_bot_k {
+    const int32_t k;
+};
+
+static const char * llama_sampler_bot_k_name(const struct llama_sampler * /*smpl*/) {
+    return "bot-k";
+}
+
+static void llama_sampler_bot_k_apply(struct llama_sampler * smpl, llama_token_data_array * cur_p) {
+    const auto * ctx = (llama_sampler_bot_k *) smpl->ctx;
+    llama_sampler_bot_k_impl(cur_p, ctx->k);
+}
+
+static struct llama_sampler * llama_sampler_bot_k_clone(const struct llama_sampler * smpl) {
+    const auto * ctx = (const llama_sampler_bot_k *) smpl->ctx;
+    return llama_sampler_init_bot_k(ctx->k);
+}
+
+static void llama_sampler_bot_k_free(struct llama_sampler * smpl) {
+    delete (llama_sampler_bot_k *) smpl->ctx;
+}
+
+static struct llama_sampler_i llama_sampler_bot_k_i = {
+    /* .name   = */ llama_sampler_bot_k_name,
+    /* .accept = */ nullptr,
+    /* .apply  = */ llama_sampler_bot_k_apply,
+    /* .reset  = */ nullptr,
+    /* .clone  = */ llama_sampler_bot_k_clone,
+    /* .free   = */ llama_sampler_bot_k_free,
+};
+
+struct llama_sampler * llama_sampler_init_bot_k(int32_t k) {
+    return new llama_sampler {
+        /* .iface = */ &llama_sampler_bot_k_i,
+        /* .ctx   = */ new llama_sampler_bot_k {
+            /* .k = */ k,
+        },
+    };
+}
+
 // top-k

 struct llama_sampler_top_k {
