Basic implementation
This commit is contained in:
parent
54332b6384
commit
e0178e27d2
4 changed files with 417 additions and 12 deletions
107
qml/Main.qml
107
qml/Main.qml
|
@ -1,8 +1,107 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import PrimeNumbers
|
||||
|
||||
Window {
|
||||
width: 640
|
||||
height: 480
|
||||
ApplicationWindow {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
142
src/FactorizationController.cpp
Normal file
142
src/FactorizationController.cpp
Normal 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();
|
||||
}
|
||||
}
|
164
src/FactorizationController.h
Normal file
164
src/FactorizationController.h
Normal 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
|
16
src/main.cpp
16
src/main.cpp
|
@ -1,15 +1,15 @@
|
|||
#include "FactorizationController.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QGuiApplication app(argc, argv);
|
||||
int main(int argc, char* argv[]) {
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
QObject::connect(
|
||||
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
|
||||
[]() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
|
||||
QQmlApplicationEngine engine;
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
|
||||
|
||||
engine.load(QStringLiteral("qrc:/qml/Main.qml"));
|
||||
engine.load(QStringLiteral("qrc:/qml/Main.qml"));
|
||||
|
||||
return app.exec();
|
||||
return app.exec();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue