diff --git a/qml/Main.qml b/qml/Main.qml index 561972b..189bae1 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -15,55 +15,85 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent - spacing: 10 + spacing: 15 - Text { - text: "Enter a number to factorize:" - font.pixelSize: 16 - Layout.alignment: Qt.AlignHCenter - } - - TextField { - id: numberInput - placeholderText: "Enter number" - // NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long) + GroupBox { + title: "Input" Layout.fillWidth: true - onTextChanged: { - // If the number changes, reset - factorizationController.reset(); - } - } - RowLayout { - Layout.fillWidth: true - spacing: 10 + ColumnLayout { + spacing: 10 - Button { - text: { - if (factorizationController.isPaused) { - return "Resume"; - } else if (factorizationController.isRunning) { - return "..."; - } else { - return "Start"; - } + TextField { + id: numberInput + placeholderText: "Enter a number" + // NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long) + Layout.fillWidth: true + font.pixelSize: 16 + onTextChanged: factorizationController.reset() } - Layout.fillWidth: true - enabled: !factorizationController.isRunning - onClicked: { - if (factorizationController.isPaused) { - factorizationController.resume(); - } else { - factorizationController.start(parseInt(numberInput.text, 10)); + + RowLayout { + Layout.fillWidth: true + Button { + text: factorizationController.isPaused ? "Resume" : factorizationController.isRunning ? "..." : "Start" + Layout.fillWidth: true + enabled: !factorizationController.isRunning + onClicked: { + if (factorizationController.isPaused) { + factorizationController.resume(); + } else { + factorizationController.start(parseInt(numberInput.text, 10)); + } + } + } + + Button { + text: "Pause" + Layout.fillWidth: true + enabled: factorizationController.isRunning + onClicked: factorizationController.stop() } } } + } - Button { - text: "Pause" - Layout.fillWidth: true - enabled: factorizationController.isRunning - onClicked: factorizationController.stop() + GroupBox { + title: "Settings" + Layout.fillWidth: true + ColumnLayout { + spacing: 10 + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Iterations per cycle:" + Layout.alignment: Qt.AlignLeft + } + + Slider { + id: iterationSlider + Layout.fillWidth: true + from: 1 + to: 100000 + stepSize: 1 + value: 1 + onValueChanged: factorizationController.iterationsPerCycle = value + } + + Text { + text: iterationSlider.value.toFixed(0) + font.bold: true + Layout.alignment: Qt.AlignRight + } + } + + CheckBox { + text: "Stop at sqrt(n)" + checked: true + onCheckedChanged: factorizationController.useSqrtOptimization = checked + } } } @@ -74,32 +104,34 @@ ApplicationWindow { value: factorizationController.progress } - Text { - text: "Current Factor: " + factorizationController.currentFactor - font.pixelSize: 14 - Layout.alignment: Qt.AlignHCenter + GroupBox { + title: "Current Status" + Layout.fillWidth: true + ColumnLayout { + spacing: 5 + + Text { + text: "Current Factor: " + factorizationController.currentFactor + font.pixelSize: 14 + Layout.alignment: Qt.AlignHCenter + } + } } - Text { - text: "Factors Found:" - font.pixelSize: 14 - Layout.alignment: Qt.AlignHCenter - } - - ListView { + GroupBox { + title: "Factors Found" Layout.fillWidth: true Layout.fillHeight: true - model: factorizationController.factors - delegate: Text { - text: modelData + + Text { + text: factorizationController.factors.join(", ") font.pixelSize: 14 - horizontalAlignment: Text.AlignHCenter - width: parent.width + color: "black" } } Text { - text: "Warning: This only shows factors below sqrt(num)" + text: factorizationController.useSqrtOptimization ? "Note: Only factors up to sqrt(n) are shown." : "Note: Finding all factors (slower)." font.pixelSize: 12 Layout.alignment: Qt.AlignHCenter } diff --git a/src/FactorizationController.cpp b/src/FactorizationController.cpp index 089ac7a..098e59a 100644 --- a/src/FactorizationController.cpp +++ b/src/FactorizationController.cpp @@ -2,7 +2,8 @@ #include #include -FactorizationController::FactorizationController(QObject* parent) : QObject{parent}, m_isRunning(false), m_currentFactNumber(0), m_currentFactor(2) { +FactorizationController::FactorizationController(QObject* parent) : + QObject{parent}, m_isRunning(false), m_useSqrtOptimization(true), m_iterationsPerCycle(1), m_currentFactNumber(0), m_currentFactor(2) { assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick)); } @@ -36,6 +37,31 @@ bool FactorizationController::isPaused() const { return m_isPaused; } +bool FactorizationController::useSqrtOptimization() const { + return m_useSqrtOptimization; +} + +void FactorizationController::setUseSqrtOptimization(bool value) { + if (value == m_useSqrtOptimization) + return; + + m_useSqrtOptimization = value; + emit useSqrtOptimizationChanged(); + reset(); +} + +int FactorizationController::iterationsPerCycle() const { + return m_iterationsPerCycle; +} + +void FactorizationController::setIterationsPerCycle(int number) { + if (number == m_iterationsPerCycle) + return; + + m_iterationsPerCycle = number; + emit iterationsPerCycleChanged(); +} + long long FactorizationController::currentFactor() const { return m_currentFactor; } @@ -48,8 +74,6 @@ void FactorizationController::start(long long number) { m_originalNumber = number; m_currentFactNumber = number; m_currentFactor = 2; - // we could also just compute this every time, but sqrt is pretty expensive - m_stopFactor = static_cast(std::sqrt(m_currentFactNumber)); m_isRunning = true; m_isFinished = false; m_factors.clear(); @@ -60,6 +84,13 @@ void FactorizationController::start(long long number) { emit progressChanged(); emit factorsChanged(); + if (m_useSqrtOptimization) { + // we could also just compute this every time, but sqrt is pretty expensive + m_stopFactor = static_cast(std::sqrt(m_currentFactNumber)); + } else { + m_stopFactor = m_currentFactNumber; + } + m_timer.start(0); } @@ -108,7 +139,12 @@ void FactorizationController::reset() { } void FactorizationController::onTimerTick() { - factorize(); + for (int i = 0; i < m_iterationsPerCycle; i++) { + if (!m_isRunning) + break; + + factorize(); + } } void FactorizationController::factorize() { diff --git a/src/FactorizationController.h b/src/FactorizationController.h index 803f8c6..530a4a4 100644 --- a/src/FactorizationController.h +++ b/src/FactorizationController.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -11,8 +12,11 @@ * @brief A class that performs integer factorization in a step-by-step manner. * * This class uses a timer to progressively factorize a given number, allowing - * for real-time updates on progress and discovered factors. It is designed - * to be used in a Qt-based QML application. + * for real-time updates on progress and discovered factors. The computation will + * run in a single thread, utilizing 0 tick timers to perform a small number of + * iterations on each cycle of the event loop. + * + * It is designed to be used in a Qt-based QML application. */ class FactorizationController : public QObject { Q_OBJECT @@ -20,6 +24,8 @@ class FactorizationController : public QObject { Q_PROPERTY(bool isRunning READ isRunning NOTIFY isRunningChanged) Q_PROPERTY(bool isFinished READ isFinished NOTIFY isFinishedChanged) Q_PROPERTY(bool isPaused READ isPaused NOTIFY isPausedChanged) + Q_PROPERTY(int iterationsPerCycle READ iterationsPerCycle WRITE setIterationsPerCycle NOTIFY iterationsPerCycleChanged) + Q_PROPERTY(bool useSqrtOptimization READ useSqrtOptimization WRITE setUseSqrtOptimization NOTIFY useSqrtOptimizationChanged) Q_PROPERTY(long long number READ number NOTIFY numberChanged) Q_PROPERTY(long long currentFactor READ currentFactor NOTIFY currentFactorChanged) Q_PROPERTY(int progress READ progress NOTIFY progressChanged) @@ -68,6 +74,18 @@ class FactorizationController : public QObject { */ bool isPaused() const; + /** + * @brief Gets the number of iterations to be performed per cycle. + * @return The number of iterations performed per cycle. + */ + int iterationsPerCycle() const; + + /** + * @brief Checks whether sqrt optimization is enabled. + * @return True if the sqrt optimization is enabled, otherwise False. + */ + bool useSqrtOptimization() const; + /** * @brief Gets the original number being factorized. * @return The number being factorized. @@ -94,6 +112,37 @@ class FactorizationController : public QObject { */ QList factors() const; + // endregion + // region: Setters + + /** + * @brief Set the number of iterations per cycle. + * + * This is a way to speed up the computation, by increasing the amount of iterations + * performed per each computation (on every event loop cycle). Setting this to a number + * too high might lead to OS thinking the application is not responding, and the UI + * feeling slow. The optimal number depends on the speed of the CPU and the allocated + * CPU time to this process. + * + * This value can be changed even as a computation is ongoing. + */ + void setIterationsPerCycle(int number); + + /** + * @brief Enable or disable using sqrt optimizations. + * + * When sqrt optimization is enabled, only factors up to sqrt of the original number + * will be checked. This allows the algorithm to finish quicker. + * + * If the intention is purely to check whether number is or isn't prime, this optimization + * is very useful. However, enabling it will lead to some factors not being found, so if + * you wish to perform a full factorization, leave this turned off. + * + * Note that changing this value while the computation is in progress will reset the + * computation. + */ + void setUseSqrtOptimization(bool value); + // endregion // region: Functions @@ -135,11 +184,13 @@ class FactorizationController : public QObject { signals: void isRunningChanged(); void isFinishedChanged(); + void isPausedChanged(); + void useSqrtOptimizationChanged(); + void iterationsPerCycleChanged(); void numberChanged(); void currentFactorChanged(); void factorsChanged(); void progressChanged(); - void isPausedChanged(); private slots: void onTimerTick(); @@ -148,6 +199,8 @@ class FactorizationController : public QObject { bool m_isRunning; ///< Indicates whether factorization is active. bool m_isFinished; ///< Indicates whether the factorization process is done. bool m_isPaused; ///< Indicates whether the factorization process is paused (can be resumed). + bool m_useSqrtOptimization; ///< Indicates whether to use the sqrt optimization + int m_iterationsPerCycle; ///< The number of iterations to perform per cycle. long long m_currentFactNumber; ///< The number currently being factorized. long long m_originalNumber; ///< The original input number. long long m_currentFactor; ///< The current divisor being checked.