Basic implementation

This commit is contained in:
ItsDrike 2025-02-25 18:59:49 +01:00
parent 54332b6384
commit e0178e27d2
Signed by: ItsDrike
GPG key ID: FA2745890B7048C0
4 changed files with 417 additions and 12 deletions

View file

@ -1,8 +1,107 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PrimeNumbers
Window { ApplicationWindow {
width: 640
height: 480
visible: true visible: true
title: qsTr("Hello World") width: 400
height: 500
title: "Factorization"
FactorizationController {
id: factorizationController
}
ColumnLayout {
anchors.fill: parent
spacing: 10
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)
Layout.fillWidth: true
onTextChanged: {
// If the number changes, reset
factorizationController.reset();
}
}
RowLayout {
Layout.fillWidth: true
spacing: 10
Button {
text: {
if (factorizationController.isPaused) {
return "Resume";
} else if (factorizationController.isRunning) {
return "...";
} else {
return "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()
}
}
ProgressBar {
Layout.fillWidth: true
from: 0
to: 100
value: factorizationController.progress
}
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 {
Layout.fillWidth: true
Layout.fillHeight: true
model: factorizationController.factors
delegate: Text {
text: modelData
font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter
width: parent.width
}
}
Text {
text: "Warning: This only shows factors below sqrt(num)"
font.pixelSize: 12
Layout.alignment: Qt.AlignHCenter
}
}
} }

View file

@ -0,0 +1,142 @@
#include "FactorizationController.h"
#include <cassert>
#include <cmath>
FactorizationController::FactorizationController(QObject* parent) : QObject{parent}, m_isRunning(false), m_currentFactNumber(0), m_currentFactor(2) {
assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick));
}
long long FactorizationController::number() const {
return m_originalNumber;
}
int FactorizationController::progress() const {
// If current factor is at or below 2 already, we must be done,
// stopFactor can never be less than 2, that makes no sense.
if (m_currentFactNumber == 1 || m_currentFactor <= 2 || m_currentFactor > m_stopFactor)
return 100;
// Determine the progress based on the current factor, in comparison to stop factor.
// since factor being <= 2 means 100%, we sub 2 here.
double progress = static_cast<double>(m_currentFactor - 2) / static_cast<double>(m_stopFactor - 2);
// Return the value as int percentage
return static_cast<int>(std::clamp(progress * 100, 0.0, 100.0));
}
bool FactorizationController::isFinished() const {
return m_isFinished;
}
bool FactorizationController::isRunning() const {
return m_isRunning;
}
bool FactorizationController::isPaused() const {
return m_isPaused;
}
long long FactorizationController::currentFactor() const {
return m_currentFactor;
}
QList<long long> FactorizationController::factors() const {
return m_factors;
}
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<long long>(std::sqrt(m_currentFactNumber));
m_isRunning = true;
m_isFinished = false;
m_factors.clear();
emit isRunningChanged();
emit isFinishedChanged();
emit numberChanged();
emit currentFactorChanged();
emit progressChanged();
emit factorsChanged();
m_timer.start(0);
}
void FactorizationController::resume() {
if (!m_isPaused)
return;
m_isRunning = true;
m_isPaused = false;
emit isRunningChanged();
emit isPausedChanged();
m_timer.start(0);
}
void FactorizationController::stop() {
if (!m_isRunning)
return;
m_isRunning = false;
m_isPaused = true;
emit isRunningChanged();
emit isPausedChanged();
m_timer.stop();
}
void FactorizationController::reset() {
if (m_isRunning)
stop();
// Only continue if we were in a paused state, or running state
// (since we called stop before, that would put is into a paused state now)
// Otherwise, it means start wasn't yet called, or reset was called already.
if (!m_isPaused)
return;
m_isPaused = false;
m_currentFactNumber = m_originalNumber;
m_currentFactor = 2;
m_factors.clear();
emit isPausedChanged();
emit currentFactorChanged();
emit progressChanged();
emit factorsChanged();
}
void FactorizationController::onTimerTick() {
factorize();
}
void FactorizationController::factorize() {
if (!m_isRunning)
return;
if (m_currentFactNumber == 1 || m_currentFactor > m_stopFactor) {
// Computation done
m_timer.stop();
m_isRunning = false;
m_isFinished = true;
emit isRunningChanged();
emit isFinishedChanged();
emit progressChanged();
return;
}
// The factorization process (very basic factorization example)
if (m_currentFactNumber % m_currentFactor == 0) {
m_currentFactNumber /= m_currentFactor;
m_factors.append(m_currentFactor);
emit factorsChanged();
// Don't increase current factor, keep dividing by it until no longer divisible
} else {
m_currentFactor++;
emit currentFactorChanged();
emit progressChanged();
}
}

View file

@ -0,0 +1,164 @@
#ifndef FACTORIZATIONCONTROLLER_H
#define FACTORIZATIONCONTROLLER_H
#include <QObject>
#include <QTimer>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
/**
* @class FactorizationController
* @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.
*/
class FactorizationController : public QObject {
Q_OBJECT
QML_ELEMENT
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(long long number READ number NOTIFY numberChanged)
Q_PROPERTY(long long currentFactor READ currentFactor NOTIFY currentFactorChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
Q_PROPERTY(QList<long long> factors READ factors NOTIFY factorsChanged)
public:
explicit FactorizationController(QObject* parent = nullptr);
// region: Getters
/**
* @brief Gets the current progress of factorization.
* @return The progress percentage (0-100).
*/
int progress() const;
/**
* @brief Checks if the factorization process is running.
* @return True if the factorization is in progress, otherwise false.
*
* This just informs whether the task is active (scheduled to run), not whether it's
* running at this very moment, as this implementation doesn't use threads.
*/
bool isRunning() const;
/**
* @brief Checks if the factorization process is done.
* @return True if the factorization is completed, otherwise false.
*
* This will only be true if the factorization process finished without being
* explicitly stopped ahead of time (e.g. when the computation is done and we
* have all the factors).
*/
bool isFinished() const;
/**
* @brief Checks if the factorization process is currently paused.
* @return True if the factorization process is paused, otherwise false.
*
* This function differs from just !isRunning, as this will only be set when
* the computation is currently paused, but can still be resumed. E.g. the
* computation was already started at some point, and stopped (paused) later,
* without having been reset.
*
* This being true is a prerequisite to resuming.
*/
bool isPaused() const;
/**
* @brief Gets the original number being factorized.
* @return The number being factorized.
*/
long long number() const;
/**
* @brief Gets the current factor being tested.
* @return The current factor.
*
* The factorization process is implemented in a way that simply checks whether a number
* is a factor, going one by one. This returns the current factor (to be checked in the
* next iteration).
*/
long long currentFactor() const;
/**
* @brief Gets the list of discovered factors (may not be complete).
* @return A QList of extracted prime factors.
*
* This returns the factors that were already discovered Note that this list might not
* be complete (if the computation is still running). If you need to know whether you
* can trust this result, check isFinished first.
*/
QList<long long> factors() const;
// endregion
// region: Functions
/**
* @brief Starts the factorization process for a given number.
* @param number The number to be factorized.
*
* 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.
*/
Q_INVOKABLE void start(long long number);
/**
* @brief Stops the ongoing factorization process.
*
* This will stop the timer, meaning the computation will no longer get triggered
* by the event loop, essentially pausing it. Note that this does not reset the already
* computed details, which means the computation can be resumed later, allowing for
* inspection of the currently computed values.
*/
Q_INVOKABLE void stop();
/**
* @brief Resumes a previously stopped factorization process.
*/
Q_INVOKABLE void resume();
/**
* @brief Resets the factorization process data.
*
* This will clear the current progress (if any). If the factorization process is running,
* this will stop it. If the factorization process is paused, this will reset isPaused,
* forcing a restart.
*/
Q_INVOKABLE void reset();
// endregion
signals:
void isRunningChanged();
void isFinishedChanged();
void numberChanged();
void currentFactorChanged();
void factorsChanged();
void progressChanged();
void isPausedChanged();
private slots:
void onTimerTick();
private:
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).
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.
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.
QTimer m_timer; ///< Timer for stepwise execution.
/**
* @brief Performs one step of the factorization process.
*/
void factorize();
};
#endif // FACTORIZATIONCONTROLLER_H

View file

@ -1,13 +1,13 @@
#include "FactorizationController.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
QObject::connect( QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
[]() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
engine.load(QStringLiteral("qrc:/qml/Main.qml")); engine.load(QStringLiteral("qrc:/qml/Main.qml"));