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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
|
@ -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"));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue