Compare commits

...

10 commits

3 changed files with 173 additions and 65 deletions

View file

@ -22,12 +22,14 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
ColumnLayout { ColumnLayout {
anchors.fill: parent
spacing: 10 spacing: 10
TextField { TextField {
id: numberInput id: numberInput
placeholderText: "Enter a number" placeholderText: "Enter a number"
// NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long) // NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long)
// Entering invalid numbers might cause unexpected behavior (will be fed to parseInt).
Layout.fillWidth: true Layout.fillWidth: true
font.pixelSize: 16 font.pixelSize: 16
onTextChanged: factorizationController.reset() onTextChanged: factorizationController.reset()
@ -49,10 +51,16 @@ ApplicationWindow {
} }
Button { Button {
text: "Pause" text: factorizationController.isPaused ? "Reset" : factorizationController.isRunning ? "Pause" : "Reset"
Layout.fillWidth: true Layout.fillWidth: true
enabled: factorizationController.isRunning enabled: factorizationController.isRunning || factorizationController.isPaused
onClicked: factorizationController.stop() onClicked: {
if (factorizationController.isPaused) {
factorizationController.reset();
} else {
factorizationController.stop();
}
}
} }
} }
} }
@ -61,7 +69,9 @@ ApplicationWindow {
GroupBox { GroupBox {
title: "Settings" title: "Settings"
Layout.fillWidth: true Layout.fillWidth: true
ColumnLayout { ColumnLayout {
anchors.fill: parent
spacing: 10 spacing: 10
RowLayout { RowLayout {
@ -89,37 +99,64 @@ ApplicationWindow {
} }
} }
RowLayout {
Layout.fillWidth: true
CheckBox { CheckBox {
text: "Stop at sqrt(n)" text: "Stop at sqrt(n)"
checked: true checked: factorizationController.useSqrtOptimization
onCheckedChanged: factorizationController.useSqrtOptimization = checked onCheckedChanged: factorizationController.useSqrtOptimization = checked
} }
}
}
ProgressBar { CheckBox {
Layout.fillWidth: true text: "Pause when factor found"
from: 0 checked: factorizationController.pauseOnFound
to: 100 onCheckedChanged: factorizationController.pauseOnFound = checked
value: factorizationController.progress }
}
}
} }
GroupBox { GroupBox {
title: "Current Status" title: "Current Status"
Layout.fillWidth: true Layout.fillWidth: true
ColumnLayout {
spacing: 5
Text { ColumnLayout {
text: "Current Factor: " + factorizationController.currentFactor anchors.fill: parent
font.pixelSize: 14 spacing: 5
Layout.alignment: Qt.AlignHCenter
}
Text { Text {
text: "Current Number (being factorized): " + factorizationController.curFactNumber text: "Current Number (being factorized): " + factorizationController.curFactNumber
font.pixelSize: 14 font.pixelSize: 14
Layout.alignment: Qt.AlignHCenter }
Text {
text: "Current Factor: " + factorizationController.currentFactor
font.pixelSize: 14
}
Text {
text: "Stop Factor: " + factorizationController.stopFactor
font.pixelSize: 14
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: 25
ProgressBar {
anchors.fill: parent
to: 100
value: factorizationController.progress
}
Text {
text: factorizationController.progress.toFixed(1) + "%"
anchors.centerIn: parent
font.pixelSize: 16
color: "black"
font.bold: true
}
} }
} }
} }
@ -137,7 +174,7 @@ ApplicationWindow {
} }
Text { Text {
text: factorizationController.useSqrtOptimization ? "Note: Only factors up to sqrt(n) are searched." : "Note: Searching all factors (slower)." text: factorizationController.useSqrtOptimization ? "Note: Only factors up to sqrt(n) are searched." : "Warning: Searching all factors (inefficient)."
font.pixelSize: 12 font.pixelSize: 12
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }

View file

@ -3,30 +3,37 @@
#include <cmath> #include <cmath>
FactorizationController::FactorizationController(QObject* parent) : FactorizationController::FactorizationController(QObject* parent) :
QObject{parent}, m_isRunning(false), m_useSqrtOptimization(true), m_iterationsPerCycle(1), m_currentFactNumber(0), m_currentFactor(2) { QObject{parent}, m_isRunning(false), m_useSqrtOptimization(true), m_pauseOnFound(false), m_iterationsPerCycle(1), m_currentFactNumber(0), m_originalNumber(0),
m_currentFactor(0), m_stopFactor(0) {
assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick)); assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick));
} }
long long FactorizationController::number() const { unsigned long long FactorizationController::number() const {
return m_originalNumber; return m_originalNumber;
} }
long long FactorizationController::curFactNumber() const { unsigned long long FactorizationController::curFactNumber() const {
return m_currentFactNumber; return m_currentFactNumber;
} }
int FactorizationController::progress() const { double FactorizationController::progress() const {
// If current factor is at or below 2 already, we must be done, // If the original number is set to 0, the computation wasn't yet initialized / was reset.
// stopFactor can never be less than 2, that makes no sense. // If the current factor is at 2, the computation has just started, return early to avoid
if (m_currentFactNumber == 1 || m_currentFactor <= 2 || m_currentFactor > m_stopFactor) // zero division issues.
if (m_originalNumber == 0 || m_currentFactor <= 2)
return 0;
// If the current number being factorized is 1, we must be done, all factors were found
// Alternatively, we're also done when the current factor passes the stop factor.
if (m_currentFactNumber == 1 || m_currentFactor > m_stopFactor)
return 100; return 100;
// Determine the progress based on the current factor, in comparison to stop factor. // Determine the progress based on the current factor, in comparison to stop factor.
// since factor being <= 2 means 100%, we sub 2 here. // since factor being <= 2 means 100%, we sub 2 here.
double progress = static_cast<double>(m_currentFactor - 2) / static_cast<double>(m_stopFactor - 2); double progress = static_cast<double>(m_currentFactor - 2) / static_cast<double>(m_stopFactor - 2);
// Return the value as int percentage // Return the value as percentage
return static_cast<int>(std::clamp(progress * 100, 0.0, 100.0)); return std::clamp(progress * 100, 0.0, 100.0);
} }
bool FactorizationController::isFinished() const { bool FactorizationController::isFinished() const {
@ -51,7 +58,29 @@ void FactorizationController::setUseSqrtOptimization(bool value) {
m_useSqrtOptimization = value; m_useSqrtOptimization = value;
emit useSqrtOptimizationChanged(); emit useSqrtOptimizationChanged();
reset();
// We're in a reset state
if (m_originalNumber == 0)
return;
if (m_useSqrtOptimization)
m_stopFactor = static_cast<unsigned long long>(std::sqrt(m_currentFactNumber));
else
m_stopFactor = m_currentFactNumber - 1;
emit stopFactorChanged();
emit progressChanged();
}
bool FactorizationController::pauseOnFound() const {
return m_pauseOnFound;
}
void FactorizationController::setPauseOnFound(bool value) {
if (value == m_pauseOnFound)
return;
m_pauseOnFound = value;
emit pauseOnFoundChanged();
} }
int FactorizationController::iterationsPerCycle() const { int FactorizationController::iterationsPerCycle() const {
@ -66,21 +95,31 @@ void FactorizationController::setIterationsPerCycle(int number) {
emit iterationsPerCycleChanged(); emit iterationsPerCycleChanged();
} }
long long FactorizationController::currentFactor() const { unsigned long long FactorizationController::currentFactor() const {
return m_currentFactor; return m_currentFactor;
} }
QList<long long> FactorizationController::factors() const { unsigned long long FactorizationController::stopFactor() const {
return m_stopFactor;
}
QList<unsigned long long> FactorizationController::factors() const {
return m_factors; return m_factors;
} }
void FactorizationController::start(long long number) { void FactorizationController::start(unsigned long long number) {
m_originalNumber = number; m_originalNumber = number;
m_currentFactNumber = number; m_currentFactNumber = number;
m_currentFactor = 2; m_currentFactor = 2;
m_isRunning = true; m_isRunning = true;
m_isFinished = false; m_isFinished = false;
m_factors.clear(); m_factors.clear();
if (m_useSqrtOptimization) {
// we could also just compute this every time, but sqrt is pretty expensive
m_stopFactor = static_cast<unsigned long long>(std::sqrt(m_currentFactNumber));
} else {
m_stopFactor = m_currentFactNumber - 1;
}
emit isRunningChanged(); emit isRunningChanged();
emit isFinishedChanged(); emit isFinishedChanged();
emit numberChanged(); emit numberChanged();
@ -88,13 +127,7 @@ void FactorizationController::start(long long number) {
emit progressChanged(); emit progressChanged();
emit factorsChanged(); emit factorsChanged();
emit curFactNumberChanged(); emit curFactNumberChanged();
emit stopFactorChanged();
if (m_useSqrtOptimization) {
// we could also just compute this every time, but sqrt is pretty expensive
m_stopFactor = static_cast<long long>(std::sqrt(m_currentFactNumber));
} else {
m_stopFactor = m_currentFactNumber - 1;
}
m_timer.start(0); m_timer.start(0);
} }
@ -134,13 +167,17 @@ void FactorizationController::reset() {
return; return;
m_isPaused = false; m_isPaused = false;
m_currentFactNumber = m_originalNumber; m_originalNumber = 0;
m_currentFactor = 2; m_currentFactNumber = 0;
m_currentFactor = 0;
m_stopFactor = 0;
m_factors.clear(); m_factors.clear();
emit isPausedChanged(); emit isPausedChanged();
emit currentFactorChanged(); emit currentFactorChanged();
emit progressChanged(); emit curFactNumberChanged();
emit factorsChanged(); emit factorsChanged();
emit stopFactorChanged();
emit progressChanged();
} }
void FactorizationController::onTimerTick() { void FactorizationController::onTimerTick() {
@ -189,12 +226,16 @@ void FactorizationController::factorize() {
// the new number being factorized, rather than keeping it at the original number. // the new number being factorized, rather than keeping it at the original number.
// (This might make the progress bar jump radically) // (This might make the progress bar jump radically)
if (m_useSqrtOptimization) { if (m_useSqrtOptimization) {
m_stopFactor = static_cast<long long>(std::sqrt(m_currentFactNumber)); m_stopFactor = static_cast<unsigned long long>(std::sqrt(m_currentFactNumber));
} else { } else {
m_stopFactor = m_currentFactNumber - 1; m_stopFactor = m_currentFactNumber - 1;
} }
emit stopFactorChanged();
emit progressChanged(); emit progressChanged();
if (m_pauseOnFound)
stop();
// Don't increase current factor, keep dividing by it until no longer divisible // Don't increase current factor, keep dividing by it until no longer divisible
} else { } else {
m_currentFactor++; m_currentFactor++;

View file

@ -26,11 +26,13 @@ class FactorizationController : public QObject {
Q_PROPERTY(bool isPaused READ isPaused NOTIFY isPausedChanged) Q_PROPERTY(bool isPaused READ isPaused NOTIFY isPausedChanged)
Q_PROPERTY(int iterationsPerCycle READ iterationsPerCycle WRITE setIterationsPerCycle NOTIFY iterationsPerCycleChanged) Q_PROPERTY(int iterationsPerCycle READ iterationsPerCycle WRITE setIterationsPerCycle NOTIFY iterationsPerCycleChanged)
Q_PROPERTY(bool useSqrtOptimization READ useSqrtOptimization WRITE setUseSqrtOptimization NOTIFY useSqrtOptimizationChanged) Q_PROPERTY(bool useSqrtOptimization READ useSqrtOptimization WRITE setUseSqrtOptimization NOTIFY useSqrtOptimizationChanged)
Q_PROPERTY(long long number READ number NOTIFY numberChanged) Q_PROPERTY(bool pauseOnFound READ pauseOnFound WRITE setPauseOnFound NOTIFY pauseOnFoundChanged)
Q_PROPERTY(long long curFactNumber READ curFactNumber NOTIFY curFactNumberChanged) Q_PROPERTY(unsigned long long number READ number NOTIFY numberChanged)
Q_PROPERTY(long long currentFactor READ currentFactor NOTIFY currentFactorChanged) Q_PROPERTY(unsigned long long curFactNumber READ curFactNumber NOTIFY curFactNumberChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged) Q_PROPERTY(unsigned long long currentFactor READ currentFactor NOTIFY currentFactorChanged)
Q_PROPERTY(QList<long long> factors READ factors NOTIFY factorsChanged) Q_PROPERTY(unsigned long long stopFactor READ stopFactor NOTIFY stopFactorChanged)
Q_PROPERTY(double progress READ progress NOTIFY progressChanged)
Q_PROPERTY(QList<unsigned long long> factors READ factors NOTIFY factorsChanged)
public: public:
explicit FactorizationController(QObject* parent = nullptr); explicit FactorizationController(QObject* parent = nullptr);
@ -41,7 +43,7 @@ class FactorizationController : public QObject {
* @brief Gets the current progress of factorization. * @brief Gets the current progress of factorization.
* @return The progress percentage (0-100). * @return The progress percentage (0-100).
*/ */
int progress() const; double progress() const;
/** /**
* @brief Checks if the factorization process is running. * @brief Checks if the factorization process is running.
@ -87,11 +89,17 @@ class FactorizationController : public QObject {
*/ */
bool useSqrtOptimization() const; bool useSqrtOptimization() const;
/**
* @brief Checks whether the computation should be paused once a new factor is found.
* @return True if the computation will get paused on new factor being found, otherwise False.
*/
bool pauseOnFound() const;
/** /**
* @brief Gets the original number being factorized. * @brief Gets the original number being factorized.
* @return The number being factorized. * @return The number being factorized.
*/ */
long long number() const; unsigned long long number() const;
/** /**
* @brief The current number being factorized. * @brief The current number being factorized.
@ -100,7 +108,7 @@ class FactorizationController : public QObject {
* As the factorization process is going on, the number being factorized might change * As the factorization process is going on, the number being factorized might change
* from the original number, as we're dividing it by the found factors. * from the original number, as we're dividing it by the found factors.
*/ */
long long curFactNumber() const; unsigned long long curFactNumber() const;
/** /**
* @brief Gets the current factor being tested. * @brief Gets the current factor being tested.
@ -110,7 +118,17 @@ class FactorizationController : public QObject {
* is a factor, going one by one. This returns the current factor (to be checked in the * is a factor, going one by one. This returns the current factor (to be checked in the
* next iteration). * next iteration).
*/ */
long long currentFactor() const; unsigned long long currentFactor() const;
/**
* @brief Get the number at which the factorization process will stop
* @return The current stop factor.
*
* The stop factor determines at which factor will the factorization process be stopped.
* If sqrt optimizations are enabled, this will generally be the square root of the number
* currently being factorized. If not, it will be the number being factorized - 1.
*/
unsigned long long stopFactor() const;
/** /**
* @brief Gets the list of discovered factors (may not be complete). * @brief Gets the list of discovered factors (may not be complete).
@ -120,7 +138,7 @@ class FactorizationController : public QObject {
* be complete (if the computation is still running). If you need to know whether you * be complete (if the computation is still running). If you need to know whether you
* can trust this result, check isFinished first. * can trust this result, check isFinished first.
*/ */
QList<long long> factors() const; QList<unsigned long long> factors() const;
// endregion // endregion
// region: Setters // region: Setters
@ -149,11 +167,20 @@ class FactorizationController : public QObject {
* Generally, there's no reason to disable this optimization, however, if you wish to * Generally, there's no reason to disable this optimization, however, if you wish to
* perform a slower search, it is a way to achieve that. * perform a slower search, it is a way to achieve that.
* *
* Note that changing this value while the computation is in progress will reset the * This value can be changed even as a computation is ongoing; if you do so, the stop factor
* computation. * will change immediately, which will very likely cause the progress bar to rapidly change.
* It is possible that after enabling, the computation will immediately finish, as the stop
* factor will be less than the sqrt(x).
*/ */
void setUseSqrtOptimization(bool value); void setUseSqrtOptimization(bool value);
/**
* @brief Enable or disable auto-pausing on new factor being found.
*
* This value can be changed even as a computation is ongoing.
*/
void setPauseOnFound(bool value);
// endregion // endregion
// region: Functions // region: Functions
@ -164,7 +191,7 @@ class FactorizationController : public QObject {
* This will start a 0-tick timer, which triggers a partial computation on each run, * This will start a 0-tick timer, which triggers a partial computation on each run,
* to let the event loop cycle in between. This implementation does NOT rely on threads. * to let the event loop cycle in between. This implementation does NOT rely on threads.
*/ */
Q_INVOKABLE void start(long long number); Q_INVOKABLE void start(unsigned long long number);
/** /**
* @brief Stops the ongoing factorization process. * @brief Stops the ongoing factorization process.
@ -197,12 +224,14 @@ class FactorizationController : public QObject {
void isFinishedChanged(); void isFinishedChanged();
void isPausedChanged(); void isPausedChanged();
void useSqrtOptimizationChanged(); void useSqrtOptimizationChanged();
void pauseOnFoundChanged();
void iterationsPerCycleChanged(); void iterationsPerCycleChanged();
void numberChanged(); void numberChanged();
void currentFactorChanged(); void currentFactorChanged();
void factorsChanged(); void factorsChanged();
void progressChanged(); void progressChanged();
void curFactNumberChanged(); void curFactNumberChanged();
void stopFactorChanged();
private slots: private slots:
void onTimerTick(); void onTimerTick();
@ -212,12 +241,13 @@ class FactorizationController : public QObject {
bool m_isFinished; ///< Indicates whether the factorization process is done. 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_isPaused; ///< Indicates whether the factorization process is paused (can be resumed).
bool m_useSqrtOptimization; ///< Indicates whether to use the sqrt optimization bool m_useSqrtOptimization; ///< Indicates whether to use the sqrt optimization
bool m_pauseOnFound; ///< Indicates whether the computation should be paused when a new factor is found.
int m_iterationsPerCycle; ///< The number of iterations to perform per cycle. int m_iterationsPerCycle; ///< The number of iterations to perform per cycle.
long long m_currentFactNumber; ///< The number currently being factorized. unsigned long long m_currentFactNumber; ///< The number currently being factorized.
long long m_originalNumber; ///< The original input number. unsigned long long m_originalNumber; ///< The original input number.
long long m_currentFactor; ///< The current divisor being checked. unsigned long long m_currentFactor; ///< The current divisor being checked.
long long m_stopFactor; ///< The stopping limit (square root of original number, saved for efficiency). unsigned long long m_stopFactor; ///< The stopping limit (square root of original number, saved for efficiency).
QList<long long> m_factors; ///< List of discovered prime factors. QList<unsigned long long> m_factors; ///< List of discovered prime factors.
QTimer m_timer; ///< Timer for stepwise execution. QTimer m_timer; ///< Timer for stepwise execution.
/** /**