diff --git a/src/doc/en/reference/data_structures/index.rst b/src/doc/en/reference/data_structures/index.rst
index 08c03313ad3..1832d01eb75 100644
--- a/src/doc/en/reference/data_structures/index.rst
+++ b/src/doc/en/reference/data_structures/index.rst
@@ -9,5 +9,6 @@ Data Structures
sage/data_structures/bounded_integer_sequences
sage/data_structures/stream
sage/data_structures/mutable_poset
+ sage/data_structures/pairing_heap
.. include:: ../footer.txt
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index 3ebb8fbe2f1..7719f5956e9 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -2773,6 +2773,11 @@ REFERENCES:
Cambridge University Press, Cambridge, 2009.
See also the `Errata list `_.
+.. [FSST1986] Michael L. Fredman, Robert Sedgewick, Daniel D. Sleator,
+ and Robert E. Tarjan. *The pairing heap: A new form of
+ self-adjusting heap*, Algorithmica, 1:111-129, 1986.
+ :doi:`10.1007/BF01840439`
+
.. [FST2012] \A. Felikson, \M. Shapiro, and \P. Tumarkin, *Cluster Algebras of
Finite Mutation Type Via Unfoldings*, Int Math Res Notices (2012)
2012 (8): 1768-1804.
diff --git a/src/sage/data_structures/meson.build b/src/sage/data_structures/meson.build
index 8a94548917b..16694b083da 100644
--- a/src/sage/data_structures/meson.build
+++ b/src/sage/data_structures/meson.build
@@ -9,6 +9,8 @@ py.install_sources(
'bounded_integer_sequences.pxd',
'list_of_pairs.pxd',
'mutable_poset.py',
+ 'pairing_heap.h',
+ 'pairing_heap.pxd',
'sparse_bitset.pxd',
'stream.py',
subdir: 'sage/data_structures',
@@ -39,3 +41,18 @@ foreach name, pyx : extension_data
)
endforeach
+extension_data_cpp = {
+ 'pairing_heap' : files('pairing_heap.pyx'),
+}
+
+foreach name, pyx : extension_data_cpp
+ py.extension_module(
+ name,
+ sources: pyx,
+ subdir: 'sage/data_structures/',
+ install: true,
+ override_options: ['cython_language=cpp'],
+ include_directories: [inc_cpython, inc_data_structures],
+ dependencies: [py_dep, cysignals, gmp],
+ )
+endforeach
diff --git a/src/sage/data_structures/pairing_heap.h b/src/sage/data_structures/pairing_heap.h
new file mode 100644
index 00000000000..3c6cbbf4216
--- /dev/null
+++ b/src/sage/data_structures/pairing_heap.h
@@ -0,0 +1,344 @@
+/*
+ * Pairing heap
+ *
+ * Implements a pairing heap data structure as described in [1]. See also [2]
+ * for more details.
+ *
+ * This implementation is templated by the type TI of items and the type TV of
+ * the value associated with an item. The type TI must be either a standard type
+ * (int, size_t, etc.) or a type equipped with a has function as supported by
+ * std::unordered_map. The top of the heap is the item with smallest value,
+ * i.e., this is a min heap data structure. The number of items in the heap is
+ * not fixed. It supports the following operations:
+ *
+ * - empty(): return true if the heap is empty, and false otherwise.
+ *
+ * - push(item, value): push an item to the heap with specified value.
+ *
+ * - top(): access the pair (item, value) at the top of the heap, i.e., with
+ * smallest value in time O(1).
+ * This operation assumes that the heap is not empty.
+ *
+ * - top_item(): access the item at the top of the heap in time O(1).
+ * This operation assumes that the heap is not empty.
+ *
+ * - top_value(): access the value of the item at the top of the heap in O(1).
+ * This operation assumes that the heap is not empty.
+ *
+ * - pop(): remove top item from the heap in amortize time O(log(n)).
+ *
+ * - decrease(item, new_value): change the value associated with the item to the
+ * specified value ``new_value`` in time o(log(n)). The new value must be
+ * smaller than the previous one. Otherwise the structure of the heap is no
+ * longer guaranteed.
+ * If the item is not already in the heap, this method calls method ``push``.
+ *
+ * - contains(item): check whether specified item is in the heap in time O(1).
+ *
+ * - value(item): return the value associated with the item in the heap.
+ * This operation assumes that the item is already in the heap.
+ *
+ * References:
+ *
+ * [1] M. L. Fredman, R. Sedgewick, D. D. Sleator, and R. E. Tarjan.
+ * "The pairing heap: a new form of self-adjusting heap".
+ * Algorithmica. 1 (1): 111-129, 1986. doi:10.1007/BF01840439.
+ *
+ * [2] https://en.wikipedia.org/wiki/Pairing_heap
+ *
+ * Author:
+ * - David Coudert
+ *
+ */
+
+#ifndef PAIRING_HEAP_H
+#define PAIRING_HEAP_H
+
+#include
+#include
+
+namespace pairing_heap {
+
+ template<
+ typename TV, // type of values
+ typename T // type of the child class
+ >
+ struct PairingHeapNodeBase {
+ public:
+
+ bool operator<=(PairingHeapNodeBase const& other) const {
+ return static_cast(this)->le_implem(static_cast(other));
+ }
+
+ // Pair list of heaps and return pointer to the top of resulting heap
+ static T *_pair(T *p) {
+ if (p == nullptr) {
+ return nullptr;
+ }
+
+ /*
+ * Move toward the end of the list, counting elements along the way.
+ * This is done in order to:
+ * - know whether the list has odd or even number of nodes
+ * - speed up going-back through the list
+ */
+ size_t children = 1;
+ T *it = p;
+ while (it->next != nullptr) {
+ it = it->next;
+ children++;
+ }
+
+ T *result;
+
+ if (children % 2 == 1) {
+ T *a = it;
+ it = it->prev;
+ a->prev = a->next = nullptr;
+ result = a;
+ } else {
+ T *a = it;
+ T *b = it->prev;
+ it = it->prev->prev;
+ a->prev = a->next = b->prev = b->next = nullptr;
+ result = _merge(a, b);
+ }
+
+ for (size_t i = 0; i < (children - 1) / 2; i++) {
+ T *a = it;
+ T *b = it->prev;
+ it = it->prev->prev;
+ a->prev = a->next = b->prev = b->next = nullptr;
+ result = _merge(_merge(a, b), result);
+ }
+
+ return result;
+ } // end _pair
+
+
+ // Merge 2 heaps and return pointer to the top of resulting heap
+ static T *_merge(T *a, T *b) {
+ if (*a <= *b) { // Use comparison method of PairingHeapNodeBase
+ _link(a, b);
+ return a;
+ } else {
+ _link(b, a);
+ return b;
+ }
+ } // end _merge
+
+
+ // Make b a child of a
+ static void _link(T *a, T *b) {
+ if (a->child != nullptr) {
+ b->next = a->child;
+ a->child->prev = b;
+ }
+ b->prev = a;
+ a->child = b;
+ } // end _link
+
+
+ // Remove p from its parent children list
+ static void _unlink(T *p) {
+ if (p->prev->child == p) {
+ p->prev->child = p->next;
+ } else {
+ p->prev->next = p->next;
+ }
+ if (p->next != nullptr) {
+ p->next->prev = p->prev;
+ }
+ p->prev = nullptr;
+ p->next = nullptr;
+ } // end _unlink
+
+
+ TV value; // value associated to the node
+ T * prev; // Previous sibling of the node or parent
+ T * next; // Next sibling of the node
+ T * child; // First child of the node
+
+ protected:
+ // Only derived class can build a PairingHeapNodeBase
+ explicit PairingHeapNodeBase(const TV &some_value)
+ : value{some_value}, prev{nullptr}, next{nullptr}, child{nullptr} {
+ }
+ }; // end struct PairingHeapNodeBase
+
+
+ template<
+ typename TI, // type of items stored in the node
+ typename TV // type of values associated with the stored item
+ // Assumes TV is a comparable type
+ >
+ class PairingHeapNode
+ : public PairingHeapNodeBase> {
+
+ public:
+ PairingHeapNode(TI const& some_item, TV const& some_value)
+ : Base_(some_value), item(some_item) {
+ }
+
+ bool le_implem(PairingHeapNode const& other) const {
+ return this->value <= other.value;
+ }
+
+ TI item; // item contained in the node
+
+ private:
+ using Base_ = PairingHeapNodeBase>;
+ };
+
+
+ class PairingHeapNodePy
+ : public PairingHeapNodeBase {
+ public:
+ PairingHeapNodePy(PyObject *some_value)
+ : Base_(some_value) {
+ }
+
+ bool le_implem(PairingHeapNodePy const& other) const {
+ return PyObject_RichCompareBool(this->value, other.value, Py_LE);
+ }
+
+ private:
+ using Base_ = PairingHeapNodeBase;
+ };
+
+
+
+ template<
+ typename TI, // type of items stored in the node
+ typename TV // type of values associated with the stored item
+ // Assume TV is a comparable type
+ >
+ class PairingHeap
+ {
+ public:
+ using HeapNodeType = PairingHeapNode;
+
+ // Constructor
+ explicit PairingHeap()
+ : root(nullptr) {
+ }
+
+ // Copy constructor
+ PairingHeap(PairingHeap const *other)
+ : root(nullptr) {
+ for (auto const& it: other->nodes) {
+ push(it.first, it.second->value);
+ }
+ }
+
+ // Destructor
+ virtual ~PairingHeap() {
+ for (auto const& it: nodes) {
+ delete it.second;
+ }
+ }
+
+ // Return true if the heap is empty, else false
+ bool empty() const {
+ return root == nullptr;
+ }
+
+ // Return true if the heap is not empty, else false
+ explicit operator bool() const {
+ return root != nullptr;
+ }
+
+ // Insert an item into the heap with specified value (priority)
+ void push(const TI &some_item, const TV &some_value) {
+ if (contains(some_item)) {
+ throw std::invalid_argument("item already in the heap");
+ }
+ PairingHeapNode *p = new PairingHeapNode(some_item, some_value);
+ nodes[some_item] = p;
+ root = empty() ? p : HeapNodeType::_merge(root, p);
+ }
+
+ // Return the top pair (item, value) of the heap
+ std::pair top() const {
+ if (empty()) {
+ throw std::domain_error("trying to access the top of an empty heap");
+ }
+ return std::make_pair(root->item, root->value);
+ }
+
+ // Return the top item of the heap
+ TI top_item() const {
+ if (empty()) {
+ throw std::domain_error("trying to access the top of an empty heap");
+ }
+ return root->item;
+ }
+
+ // Return the top value of the heap
+ TV top_value() const {
+ if (empty()) {
+ throw std::domain_error("trying to access the top of an empty heap");
+ }
+ return root->value;
+ }
+
+ // Remove the top element from the heap. Do nothing if empty
+ void pop() {
+ if (not empty()) {
+ PairingHeapNode *p = root->child;
+ nodes.erase(root->item);
+ delete root;
+ root = HeapNodeType::_pair(p);
+ }
+ }
+
+ // Decrease the value of specified item
+ // If the item is not in the heap, push it
+ void decrease(const TI &some_item, const TV &new_value) {
+ if (contains(some_item)) {
+ PairingHeapNode *p = nodes[some_item];
+ if (p->value <= new_value) {
+ throw std::invalid_argument("the new value must be less than the current value");
+ }
+ p->value = new_value;
+ if (p->prev != nullptr) {
+ HeapNodeType::_unlink(p);
+ root = HeapNodeType::_merge(root, p);
+ }
+ } else {
+ push(some_item, new_value);
+ }
+ }
+
+ // Check if specified item is in the heap
+ bool contains(TI const& some_item) const {
+ return nodes.find(some_item) != nodes.end();
+ }
+
+ // Return the value associated with the item
+ TV value(const TI &some_item) const {
+ auto it = nodes.find(some_item);
+ if (it == nodes.end()) {
+ throw std::invalid_argument("the specified item is not in the heap");
+ }
+ return it->second->value;
+ }
+
+ // Return the number of items in the heap
+ size_t size() const {
+ return nodes.size();
+ }
+
+ private:
+
+ // Pointer to the top of the heap
+ PairingHeapNode *root;
+
+ // Map used to access stored items
+ std::unordered_map *> nodes;
+
+ }; // end class PairingHeap
+
+} // end namespace pairing_heap
+
+#endif
diff --git a/src/sage/data_structures/pairing_heap.pxd b/src/sage/data_structures/pairing_heap.pxd
new file mode 100644
index 00000000000..d749cc7ec31
--- /dev/null
+++ b/src/sage/data_structures/pairing_heap.pxd
@@ -0,0 +1,86 @@
+# ******************************************************************************
+# Copyright (C) 2024 David Coudert
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# https://www.gnu.org/licenses/
+# ******************************************************************************
+
+from cpython cimport PyObject
+from libcpp.pair cimport pair
+from sage.data_structures.bitset_base cimport bitset_t
+
+
+# ==============================================================================
+# Interface to pairing heap data structure from ./pairing_heap.h
+# ==============================================================================
+
+cdef extern from "./pairing_heap.h" namespace "pairing_heap":
+ cdef cppclass PairingHeap[TypeOfItem, TypeOfValue]:
+ PairingHeap() except +
+ PairingHeap(PairingHeap[TypeOfItem, TypeOfValue]) except +
+ bint empty()
+ void push(TypeOfItem, TypeOfValue) except +
+ pair[TypeOfItem, TypeOfValue] top() except +
+ TypeOfItem top_item() except +
+ TypeOfValue top_value() except +
+ void pop() except +
+ void decrease(TypeOfItem, TypeOfValue) except +
+ bint contains(TypeOfItem)
+ TypeOfValue value(TypeOfItem) except +
+
+ cdef cppclass PairingHeapNodePy:
+ PyObject * value # value associated with the item
+ PairingHeapNodePy * prev # Previous sibling of the node or parent
+ PairingHeapNodePy * next # Next sibling of the node
+ PairingHeapNodePy * child # First child of the node
+
+ @staticmethod
+ PairingHeapNodePy * _merge(PairingHeapNodePy * a, PairingHeapNodePy * b) except +
+
+ @staticmethod
+ PairingHeapNodePy * _pair(PairingHeapNodePy * p) except +
+
+ @staticmethod
+ void _link(PairingHeapNodePy * a, PairingHeapNodePy * b)
+
+ @staticmethod
+ void _unlink(PairingHeapNodePy * p)
+
+
+# ==============================================================================
+# Pairing heap data structure with fixed capacity n
+# ==============================================================================
+
+cdef class PairingHeap_class:
+ cdef size_t n # maximum number of items
+ cdef PairingHeapNodePy * root # pointer to the top of the heap
+ cdef PairingHeapNodePy * nodes # array of size n to store items
+ cdef size_t number_of_items # number of active items
+ cpdef bint empty(self) noexcept
+ cpdef bint full(self) noexcept
+ cpdef size_t capacity(self) noexcept
+ cpdef size_t size(self) noexcept
+ cpdef tuple top(self)
+ cpdef object top_value(self)
+ cpdef void pop(self) noexcept
+
+
+cdef class PairingHeap_of_n_integers(PairingHeap_class):
+ cdef bitset_t active # bitset to identify active items
+ cpdef void push(self, size_t item, object value) except *
+ cpdef size_t top_item(self) except *
+ cpdef void decrease(self, size_t item, object new_value) except *
+ cpdef object value(self, size_t item)
+
+
+cdef class PairingHeap_of_n_hashables(PairingHeap_class):
+ cdef list _int_to_item # mapping from integers to items
+ cdef dict _item_to_int # mapping from items to integers
+ cdef list free_idx # list of free indexes
+ cpdef void push(self, object item, object value) except *
+ cpdef object top_item(self)
+ cpdef void decrease(self, object item, object new_value) except *
+ cpdef object value(self, object item)
diff --git a/src/sage/data_structures/pairing_heap.pyx b/src/sage/data_structures/pairing_heap.pyx
new file mode 100644
index 00000000000..ec08be43652
--- /dev/null
+++ b/src/sage/data_structures/pairing_heap.pyx
@@ -0,0 +1,1462 @@
+# distutils: language = c++
+r"""
+Pairing Heap
+
+This module proposes several implementations of the pairing heap data structure
+[FSST1986]_. See the :wikipedia:`Pairing_heap` for more information on this
+min-heap data structure.
+
+- :class:`PairingHeap_of_n_integers`: a pairing heap data structure with fixed
+ capacity `n`. Its items are integers in the range `[0, n-1]`. Values can be of
+ any type equipped with a comparison method (``<=``).
+
+- :class:`PairingHeap_of_n_hashables`: a pairing heap data structure with fixed
+ capacity `n`. Its items can be of any hashable type. Values can be of any type
+ equipped with a comparison method (``<=``).
+
+- ``PairingHeap``: interface to a pairing heap data structure written in C++.
+ The advantages of this data structure are that: its capacity is unbounded;
+ items can be of any hashable type equipped with a hashing method that can be
+ supported by ``std::unordered_map``; values can be of any specified type
+ equipped with a comparison method (``<=``). This data structure is for
+ internal use and therefore cannot be accessed from a shell.
+
+EXAMPLES:
+
+Pairing heap of `n` integers in the range `[0, n-1]`::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(10); P
+ PairingHeap_of_n_integers: capacity 10, size 0
+ sage: P.push(1, 3)
+ sage: P.push(2, 2)
+ sage: P
+ PairingHeap_of_n_integers: capacity 10, size 2
+ sage: P.top()
+ (2, 2)
+ sage: P.decrease(1, 1)
+ sage: P.top()
+ (1, 1)
+ sage: P.pop()
+ sage: P.top()
+ (2, 2)
+
+ sage: P = PairingHeap_of_n_integers(10)
+ sage: P.push(1, (2, 'a'))
+ sage: P.push(2, (2, 'b'))
+ sage: P.top()
+ (1, (2, 'a'))
+
+Pairing heap of `n` hashables::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_hashables
+ sage: P = PairingHeap_of_n_hashables(10); P
+ PairingHeap_of_n_hashables: capacity 10, size 0
+ sage: P.push(1, 3)
+ sage: P.push('b', 2)
+ sage: P.push((1, 'abc'), 4)
+ sage: P.top()
+ ('b', 2)
+ sage: P.decrease((1, 'abc'), 1)
+ sage: P.top()
+ ((1, 'abc'), 1)
+ sage: P.pop()
+ sage: P.top()
+ ('b', 2)
+
+ sage: # needs sage.graphs
+ sage: P = PairingHeap_of_n_hashables(10)
+ sage: P.push(('a', 1), (2, 'b'))
+ sage: P.push(2, (2, 'a'))
+ sage: g = Graph(2, immutable=True)
+ sage: P.push(g, (3, 'z'))
+ sage: P.top()
+ (2, (2, 'a'))
+ sage: P.decrease(g, (1, 'z'))
+ sage: P.top()
+ (Graph on 2 vertices, (1, 'z'))
+ sage: while P:
+ ....: print(P.top())
+ ....: P.pop()
+ (Graph on 2 vertices, (1, 'z'))
+ (2, (2, 'a'))
+ (('a', 1), (2, 'b'))
+"""
+# ******************************************************************************
+# Copyright (C) 2024 David Coudert
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# https://www.gnu.org/licenses/
+# ******************************************************************************
+
+
+from libcpp.pair cimport pair
+from cpython.ref cimport PyObject, Py_INCREF, Py_XDECREF
+from cysignals.signals cimport sig_on, sig_off, sig_check
+from cysignals.memory cimport check_allocarray, sig_free
+from sage.data_structures.bitset_base cimport (bitset_init, bitset_free,
+ bitset_add, bitset_remove,
+ bitset_in)
+from sage.misc.prandom import shuffle
+
+
+# ==============================================================================
+# Class PairingHeap_class
+# ==============================================================================
+
+cdef class PairingHeap_class:
+ r"""
+ Common class and methods for :class:`PairingHeap_of_n_integers` and
+ :class:`PairingHeap_of_n_hashables`.
+ """
+
+ def __repr__(self):
+ r"""
+ Return a string representing ``self``.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5); P
+ PairingHeap_of_n_integers: capacity 5, size 0
+ sage: P.push(1, 2)
+ sage: P
+ PairingHeap_of_n_integers: capacity 5, size 1
+ """
+ return f"{type(self).__name__}: capacity {self.n}, size {len(self)}"
+
+ def __bool__(self):
+ r"""
+ Check whether ``self`` is not empty.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: 'not empty' if P else 'empty'
+ 'empty'
+ sage: P.push(1, 2)
+ sage: 'not empty' if P else 'empty'
+ 'not empty'
+ """
+ return self.root != NULL
+
+ cpdef bint empty(self) noexcept:
+ r"""
+ Check whether the heap is empty.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: P.empty()
+ True
+ sage: P.push(1, 2)
+ sage: P.empty()
+ False
+ """
+ return self.root == NULL
+
+ cpdef bint full(self) noexcept:
+ r"""
+ Check whether the heap is full.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(2)
+ sage: P.full()
+ False
+ sage: P.push(0, 2)
+ sage: P.push(1, 3)
+ sage: P.full()
+ True
+ """
+ return self.n == self.number_of_items
+
+ cpdef size_t capacity(self) noexcept:
+ r"""
+ Return the maximum capacity of the heap.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: P.capacity()
+ 5
+ sage: P.push(1, 2)
+ sage: P.capacity()
+ 5
+ """
+ return self.n
+
+ cpdef size_t size(self) noexcept:
+ r"""
+ Return the number of items in the heap.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: P.size()
+ 0
+ sage: P.push(1, 2)
+ sage: P.size()
+ 1
+
+ One may also use Python's ``__len__``::
+
+ sage: len(P)
+ 1
+ """
+ return self.number_of_items
+
+ def __len__(self):
+ r"""
+ Return the number of items in the heap.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: len(P)
+ 0
+ sage: P.push(1, 2)
+ sage: len(P)
+ 1
+ """
+ return self.number_of_items
+
+ cpdef tuple top(self):
+ r"""
+ Return the top pair (item, value) of the heap.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: P.push(1, 2)
+ sage: P.top()
+ (1, 2)
+ sage: P.push(3, 1)
+ sage: P.top()
+ (3, 1)
+
+ sage: P = PairingHeap_of_n_integers(3)
+ sage: P.top()
+ Traceback (most recent call last):
+ ...
+ ValueError: trying to access the top of an empty heap
+ """
+ raise NotImplementedError()
+
+ cpdef object top_value(self):
+ r"""
+ Return the value of the top item of the heap.
+
+ EXAMPLES::
+
+ sage: from sage.data_structures.pairing_heap import PairingHeap_of_n_integers
+ sage: P = PairingHeap_of_n_integers(5)
+ sage: P.push(1, 2)
+ sage: P.top()
+ (1, 2)
+ sage: P.top_value()
+ 2
+
+ sage: P = PairingHeap_of_n_integers(3)
+ sage: P.top_value()
+ Traceback (most recent call last):
+ ...
+ ValueError: trying to access the top of an empty heap
+ """
+ if not self:
+ raise ValueError("trying to access the top of an empty heap")
+ return