This commit is contained in:
hladu357 2024-03-08 15:27:04 +01:00
commit 9d90fa79b0
40 changed files with 5997 additions and 0 deletions

2727
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

65
Makefile Normal file
View File

@ -0,0 +1,65 @@
CXX=g++
LD=g++
CXXFLAGS=-Wall -pedantic -Wextra -g
DBGFLAGS=-g
SRC_DIR=src/code/
BUILD_DIR=build/
TARGET=hladuond
SRC = $(wildcard src/code/*.cpp)
OBJ = $(SRC:src/code/%.cpp=build/%.o)
.PHONY: all
all: compile doc
.PHONY: run
run: hladuond
./hladuond
doc: doc/html
doc/html:
doxygen Doxyfile
cp ./doc/html/index.html ./doc/index.html
compile:
mkdir -p $(BUILD_DIR)
$(MAKE) -j8 hladuond
test:
mkdir -p $(BUILD_DIR)
$(MAKE) -j8 examples/test
examples/test: build/test.o $(filter-out $(BUILD_DIR)main.o, $(OBJ))
$(LD) $(CXXFLAGS) -o $@ $^
# link
$(TARGET): $(OBJ)
$(LD) $(CXXFLAGS) -o $@ $^
# compile
$(BUILD_DIR)%.o: $(SRC_DIR)%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
build/test.o: examples/test.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
.PHONY: clean
clean:
rm -rf hladuond
rm -f hladuond.zip
rm -rf ./build
rm -rf ./doc/html ./doc/index.html
rm -f Makefile.d
rm -f ./examples/test
progtest: clean
mkdir hladuond
cp -r src hladuond/src
cp -r doc hladuond/doc
cp -r examples hladuond/examples
cp README.md hladuond/README.md
cp prohlaseni.txt hladuond/prohlaseni.txt
cp zadani.txt hladuond/zadani.txt
cp Doxyfile hladuond/Doxyfile
cp Makefile hladuond/Makefile
zip -r hladuond.zip hladuond/
rm -rf hladuond

49
README.md Normal file
View File

@ -0,0 +1,49 @@
#Kalkulačka racionálních čísel
Interaktivní terminálová kalkulačka
##Struktura programu:
Instance třídy CParser zpracovává algebraické výrazy v LL1 gramatice
(viz. doc/dor/grammar.dot)
Pamatuje si proměnné a řídí dodaný saver.
Ze zadaných výrazů složí strom na jehož vrchol zavolá rekurzivní eval()
Abstraktní třída CPrintable zastřešuje veškeré výstupy z parseru,
tedy čísla a operace. Její potomci imlepentují metodu eval() kterou
se rekurzivně vyhodnotí celý strom operací. Polymorfismus zajištěje
že operandy můžou být jak čísla tak jiné operace.
Kalkulačcka implementuje exaktní operace pro racionální čísla,
celočíselné modulo, a proximaxi logaritmů a exponenciel.
+-------------------------+ +---------------------------+
| CPrintable *abstract* | | CParser |
|-input from parser | | parses input into |
|-output to UI |--+ | tree of CPrintables |
| SPrintableValue *eval() | | | |
+-------------------------+ | +---------------------------+
|
|
|
+-------------------------+ | +---------------------------+
| COperator *abstarct* |<-+->| CRational |
| CPrintable *operandB | | CBigInt *deniminator |
| CPrintable *operandA | | CBigInt *numerator |
| | | |
+-------------------------+ +---------------------------+
|
| +-----------+ +---------------------------+
+-->| CAdittion | | CBigInt |
| +-----------+ | CBigInt *add( CBigint *) |
| | CBigInt *sub( CBigint *) |
| +----------------+ | CBigInt *mul( CBigint *) |
+-->| CMultipication | +---------------------------+
| +----------------+
|
| +------------+
+-->| CFactorial |
| +------------+
.
.
.

47
doc/dot/classdiagram.dot Normal file
View File

@ -0,0 +1,47 @@
digraph UML_Class_diagram {
graph [
label="Class diagram"
labelloc="t"
fontname="Helvetica,Arial,sans-serif"
rankdir=LR
splines=ortho
]
node [
fontname="Helvetica,Arial,sans-serif"
shape=record
style=filled
fillcolor=gray95
]
edge [
]
CPrintable [label=<{ <b>CPrintable</b> } | { virtual std::stringtoString() } | { virtual CRational *evaluate() }>]
COperator [label=<{ <b>COperator</b> } | { controls CRational }>]
CRational [label=<{ <b>CRational</b> } | { controls CBigInt }>]
COperator -> CPrintable
CRational -> CPrintable
subgraph cluster_0 {
style=filled
color=lightgrey
node [style=filled,color=gray95,width=1.5]
edge []
CAdittion -> COperator
CSubtraction -> COperator
CMultiplication -> COperator
CDivision -> COperator
CLogarithm -> COperator
CExponencial -> COperator
CModulo -> COperator
label = "Operators";
}
CTextsaver->CSaver
CBinsaver->CSaver
}

13
doc/dot/fika.dot Normal file
View File

@ -0,0 +1,13 @@
digraph UML_Class_diagram {
// rankdir=LR
b->a
c->b
c->a
d->c
d->b
d->a
e->a
e->b
e->c
e->d
}

36
doc/dot/grammar.dot Normal file
View File

@ -0,0 +1,36 @@
digraph Grammar_diagram {
graph [
label="Grammar diagram"
labelloc="t"
fontname="Helvetica,Arial,sans-serif"
// rankdir=LR
// splines=ortho
]
node [
fontname="Helvetica,Arial,sans-serif"
// shape=record
style=filled
fillcolor=gray95
]
edge [
]
START -> OPEN_BRACKET
SIGN_PREFIX //[label="SIGN_PREFIX (unary operator)"]
SIGN_PREFIX -> NUMBER
OPEN_BRACKET -> NUMBER
OPEN_BRACKET -> OPEN_BRACKET
OPEN_BRACKET -> SIGN_PREFIX
NUMBER -> BINARY_OPERATOR
BINARY_OPERATOR -> NUMBER
BINARY_OPERATOR -> OPEN_BRACKET
NUMBER -> CLOSE_BRACKET
CLOSE_BRACKET -> CLOSE_BRACKET
CLOSE_BRACKET -> BINARY_OPERATOR
CLOSE_BRACKET -> END
}

34
doc/dot/parserFSM.dot Normal file
View File

@ -0,0 +1,34 @@
digraph parser_FSM_diagram {
graph [
label="parser FSM diagram"
labelloc="t"
fontname="Helvetica,Arial,sans-serif"
rankdir=BT
]
node [
fontname="Helvetica,Arial,sans-serif"
// shape=record
style=filled
fillcolor=gray95
]
edge [
]
NUM //[label=<{ NUM } | { 1 - 9 } >]
START -> NUM
NUM -> NUM
NUM -> DOT
DOT -> NUM
Operator //[label=<{ Operator } | { ! $ + - / * ( ) ^ % = }>]
NUM -> Operator
Operator -> NUM
Operator -> Label
Operator -> END
START -> Label
START -> END
Label -> Operator
Label -> END
NUM -> END
}

116
doc/dot/test.dot Normal file
View File

@ -0,0 +1,116 @@
digraph UML_Class_diagram {
graph [
label="UML Class diagram demo"
labelloc="t"
fontname="Helvetica,Arial,sans-serif"
]
node [
fontname="Helvetica,Arial,sans-serif"
shape=record
style=filled
fillcolor=gray95
]
edge [fontname="Helvetica,Arial,sans-serif"]
edge [arrowhead=vee style=dashed]
Client -> Interface1 [label=dependency]
Client -> Interface2
edge [dir=back arrowtail=empty style=""]
Interface1 -> Class1 [xlabel=inheritance]
Interface2 -> Class1 [dir=none]
Interface2 [label="" xlabel="Simple\ninterface" shape=circle]
Interface1[label = <{<b>«interface» I/O</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
Class1[label = <{<b>I/O class</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
edge [dir=back arrowtail=empty style=dashed]
Class1 -> System_1 [label=implementation]
System_1 [
shape=plain
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr> <td> <b>System</b> </td> </tr>
<tr> <td>
<table border="0" cellborder="0" cellspacing="0" >
<tr> <td align="left" >+ property</td> </tr>
<tr> <td port="ss1" align="left" >- Subsystem 1</td> </tr>
<tr> <td port="ss2" align="left" >- Subsystem 2</td> </tr>
<tr> <td port="ss3" align="left" >- Subsystem 3</td> </tr>
<tr> <td align="left">...</td> </tr>
</table>
</td> </tr>
<tr> <td align="left">+ method<br/>...<br align="left"/></td> </tr>
</table>>
]
edge [dir=back arrowtail=diamond]
System_1:ss1 -> Subsystem_1 [xlabel="composition"]
Subsystem_1 [
shape=plain
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr> <td> <b>Subsystem 1</b> </td> </tr>
<tr> <td>
<table border="0" cellborder="0" cellspacing="0" >
<tr> <td align="left">+ property</td> </tr>
<tr> <td align="left" port="r1">- resource</td> </tr>
<tr> <td align="left">...</td> </tr>
</table>
</td> </tr>
<tr> <td align="left">
+ method<br/>
...<br align="left"/>
</td> </tr>
</table>>
]
Subsystem_2 [
shape=plain
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr> <td> <b>Subsystem 2</b> </td> </tr>
<tr> <td>
<table align="left" border="0" cellborder="0" cellspacing="0" >
<tr> <td align="left">+ property</td> </tr>
<tr> <td align="left" port="r1">- resource</td> </tr>
<tr> <td align="left">...</td> </tr>
</table>
</td> </tr>
<tr> <td align="left">
+ method<br/>
...<br align="left"/>
</td> </tr>
</table>>
]
Subsystem_3 [
shape=plain
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr> <td> <b>Subsystem 3</b> </td> </tr>
<tr> <td>
<table border="0" cellborder="0" cellspacing="0" >
<tr> <td align="left">+ property</td> </tr>
<tr> <td align="left" port="r1">- resource</td> </tr>
<tr> <td align="left">...</td> </tr>
</table>
</td> </tr>
<tr> <td align="left">
+ method<br/>
...<br align="left"/>
</td> </tr>
</table>>
]
System_1:ss2 -> Subsystem_2;
System_1:ss3 -> Subsystem_3;
edge [xdir=back arrowtail=odiamond]
Subsystem_1:r1 -> "Shared resource" [label=aggregation]
Subsystem_2:r1 -> "Shared resource"
Subsystem_3:r1 -> "Shared resource"
"Shared resource" [
label = <{
<b>Shared resource</b>
|
+ property<br align="left"/>
...<br align="left"/>
|
+ method<br align="left"/>
...<br align="left"/>
}>
]
}

81
examples/kontrolnibod.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "../src/code/rational.cpp"
#include <assert.h>
int main( void ) {
{
// CBigInt a(3), b(5);
// auto c = b.lcm(&a);
// delete c;
// delete c;
// CBigInt *c = a.add(&b);
// assert( CBigInt(1111111110).cmp(c) == 0);
// delete c;
// c = a.sub(&b);
// assert( CBigInt(-864197532).cmp(c) == 0);
// delete c;
}
// {
// CBigInt a(123456789), b(987654321);
// auto c = b.divmod(&a);
// assert( CBigInt(8).cmp(c.first) == 0 );
// assert( CBigInt(9).cmp(c.second) == 0 );
// delete c.first;
// delete c.second;
// }
// //! leak
// {
// CRational a( new CBigInt(1), new CBigInt(6) );
// CRational b( new CBigInt(2), new CBigInt(6) );
// CRational d( new CBigInt(11),new CBigInt(15) );
// a.setSameDenominator(&b);
// CRational *c = a.add(&b);
// c->simplify();
// // assert( d.toString() == c->toString());
// delete c;
// }
// {
// CRational a( new CBigInt(123), new CBigInt(456) );
// CRational b( new CBigInt(789), new CBigInt(123) );
// CRational d( new CBigInt(41657),new CBigInt(6232) );
// CRational *c = a.add(&b);
// c->simplify();
// // assert( d.toString() == c->toString());
// delete c;
// }
// //! leak
// {
// CRational a( new CBigInt(1), new CBigInt(3) );
// CRational b( new CBigInt(2), new CBigInt(5) );
// CRational d( new CBigInt(2),new CBigInt(15) );
// CRational *c = a.mul(&b);
// c->simplify();
// // assert( d.toString() == c->toString());
// delete c;
// }
// {
// CBigInt a(12), b(-4);
// CBigInt *c = a.div(&b);
// // c->debugPrint();
// delete c;
// }
// {
// CRational a( new CBigInt(-1), new CBigInt(3) );
// CRational b( new CBigInt(-1), new CBigInt(2) );
// CRational *c = a.div(&b);
// c->simplify();
// auto str = c->toString();
// // std::cout << str << '\n';
// delete c;
// }
{
CRational a( new CBigInt(3), new CBigInt(7));
CRational *c = a.exp();
auto str = c->toString();
std::cout << c->debugDump() << '\n';
delete c;
}
return 0;
}

93
examples/test.cpp Normal file
View File

@ -0,0 +1,93 @@
#include "../src/include/parser.hpp"
#include <assert.h>
#include <filesystem>
#include <iostream>
int main() {
{
std::cout << "running testset 1\n";
CParser a;
a.convert("123456789 + 987654321");
assert( a.m_output == "1111111110" );
a.convert("(12/50 + 1/8)/4 + 2/7");
assert( a.m_output == "2111 / 5600" );
a.convert("velkeCislo=100000000000000000000000000000000000000000000");
assert( a.m_output == "100000000000000000000000000000000000000000000");
a.convert("$velkeCislo*$velkeCislo");
assert( a.m_output == "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" );
a.convert("$ans / $velkeCislo");
assert( a.m_output == "100000000000000000000000000000000000000000000" );
a.convert("!save sav");
CParser b;
b.convert("!load sav");
b.convert("$velkeCislo");
assert( b.m_output == "100000000000000000000000000000000000000000000" );
std::filesystem::path pth = std::filesystem::current_path();
std::filesystem::remove( pth / "sav" );
b.convert("!load sav");
assert( b.m_output == "load failed" );
b.convert("1 )+ 2");
assert( b.m_output == "bad parenthesis parity" );
b.convert("1 + ()");
assert( b.m_output == "Caught exception: parsing failed" );
b.convert(".3");
assert( b.m_output == "3 / 10" );
b.convert( "(.1 + 9.9) % (3 * 2)" );
assert( b.m_output == "4" );
}
{
std::cout << "running testset 2\n";
CParser a;
// Test case 1: Complex arithmetic expression
a.convert("((3.14 + 2) * 5 - 8) / 2");
assert(a.m_output == "177 / 20");
// Test case 2: Division by zero
a.convert("1 / 0");
assert(a.m_output == "Caught exception: division by zero");
// Test case 3: Mixed fraction and decimal calculations
a.convert("(1/4 + 0.75) * 2");
assert(a.m_output == "2");
// Test case 4: Negative numbers and subtraction
a.convert("-5 + 7 - (-2) * (-3)");
assert(a.m_output == "-4");
// Test case 5: Exponentiation
a.convert("(2/1) ^ 16");
assert(a.m_output == "65536");
// Test case 6: Order of operations
a.convert("2 * 3 + 4 / 2 - 1");
assert(a.m_output == "7");
// Test case 7: Large numbers
a.convert("1234567890 + 9876543210");
assert(a.m_output == "11111111100");
// Test case 8: Error handling for invalid input
a.convert("1 + 2 *");
assert(a.m_output == "Caught exception: parsing failed");
// Test case 9: Variable assignment and retrieval
a.convert("x = 5");
assert(a.m_output == "5");
a.convert("$x + 3");
assert(a.m_output == "8");
}
std::cout << "all tests successful!" << std::endl;
return 0;
}

9
src/code/addition.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "../include/rational.hpp"
#include "../include/addition.hpp"
CAddition::CAddition( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CAddition::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
return a->add(b.get());
}

432
src/code/bigint.cpp Normal file
View File

@ -0,0 +1,432 @@
#include "../include/bigint.hpp"
CBigInt::CBigInt() : m_negative(false) {}
CBigInt::CBigInt( int64_t parent ) : m_negative(parent < 0) {
m_data.reserve( CHUNK_BYTES / sizeof(parent) );
uint64_t par = std::abs(parent);
if (CHUNK_BITS == 64) {
m_data.push_back(par);
return;
}
while (par) {
m_data.push_back(par);
par >>= CHUNK_BITS;
}
}
CBigInt::CBigInt( const CBigInt &oth ) : m_data(oth.m_data), m_negative(oth.m_negative) {}
CBigInt::~CBigInt() {}
std::string CBigInt::toString() const {
std::string retStr;
if (m_negative)
retStr = '-';
if (m_data.size() <= 1) {
// If the number fits within a single word, use sts conversion
uint64_t ret = 0;
for (uint64_t i = 0; i < sizeof(ret) / CHUNK_BYTES && i < m_data.size(); ++i)
ret += m_data[i] << (8 * i);
return retStr + std::to_string(ret);
}
std::unique_ptr<CBigInt> divNum (new CBigInt(*this));
CBigInt ten(10);
while (!divNum->isZero()) {
auto tmp = divNum->divmod(&ten);
divNum.reset(tmp.first);
retStr += std::to_string(tmp.second->m_data[0]);
delete tmp.second;
}
// Reverse the string representation of the number
for (uint64_t i = 0; i < retStr.size() / 2; i++)
std::swap(retStr[i], retStr[retStr.size() - i - 1]);
return retStr;
}
void CBigInt::debugPrint() {
uint64_t tmp = 0;
if (m_negative)
std::cout << '-';
for (uint64_t i = 0; i < sizeof(tmp) / CHUNK_BYTES && i < m_data.size(); ++i)
tmp += (uint64_t)m_data[i] << (CHUNK_BITS * i);
std::cout << tmp << '\n';
}
int64_t CBigInt::debugDump() {
int64_t tmp = 0;
for (uint64_t i = 0; i < sizeof(tmp) / CHUNK_BYTES && i < m_data.size(); ++i)
tmp += (uint64_t)m_data[i] << (CHUNK_BITS * i);
if (m_negative)
tmp *= -1;
return tmp;
}
CBigInt *CBigInt::add( const CBigInt *oth ) const {
if (isZero())
return new CBigInt(*oth);
if (oth->isZero())
return new CBigInt(*this);
if (!(m_negative ^ oth->m_negative)) {
CBigInt *ret = new CBigInt();
ret->m_data.reserve( std::max(m_data.size() + 1, oth->m_data.size() + 1) );
bool carry = 0;
for (uint64_t i = 0; i < std::max(m_data.size(), oth->m_data.size()); ++i) {
CHUNK_TYPE tmp = ( i < m_data.size() ? m_data[i] : 0 ) + ( i < oth->m_data.size() ? oth->m_data[i] : 0 ) + carry;
carry = (tmp < ((i < m_data.size()) ? m_data[i] : oth->m_data[i]));
ret->m_data.push_back(tmp);
}
if (carry)
ret->m_data.push_back(1);
ret->m_negative = m_negative;
return ret;
}
// If the numbers have different signs
int comp = cmp(oth);
CBigInt *bigger = (CBigInt *)((comp >= 0) * (size_t)this + (comp < 0) * (size_t) oth);
CBigInt *smaller = (CBigInt *)((comp >= 0) * (size_t)oth + (comp < 0) * (size_t) this);
bool tmpB = bigger->m_negative;
bigger->m_negative = false;
bool tmpS = smaller->m_negative;
smaller->m_negative = false;
CBigInt *ret = bigger->sub(smaller);
ret->m_negative = tmpB;
bigger->m_negative = tmpB;
smaller->m_negative = tmpS;
return ret;
}
CBigInt *CBigInt::sub( const CBigInt *oth ) const {
int comp = cmp(oth);
CBigInt *bigger = (CBigInt *)((comp >= 0) * (size_t)this + (comp < 0) * (size_t) oth);
CBigInt *smaller = (CBigInt *)((comp >= 0) * (size_t)oth + (comp < 0) * (size_t) this);
if (!(m_negative ^ oth->m_negative)) {
CBigInt *ret = new CBigInt();
ret->m_data.reserve( bigger->m_data.size() );
bool borrow = 0;
for (; smaller->m_data.size() < bigger->m_data.size(); smaller->m_data.push_back(0));
for (uint64_t i = 0; i < smaller->m_data.size(); ++i) {
CHUNK_TYPE tmp = bigger->m_data[i];
if (borrow) {
if (bigger->m_data[i] == 0) {
tmp = CHUNK_MAX;
} else {
tmp = bigger->m_data[i] - 1;
borrow = false;
}
}
borrow |= bigger->m_data[i] < smaller->m_data[i];
tmp -= smaller->m_data[i];
ret->m_data.push_back(tmp);
}
ret->m_negative = m_negative;
if (this == smaller)
ret->m_negative = !ret->m_negative;
return ret;
}
// If the numbers have different signs
bool tmpB = bigger->m_negative;
bigger->m_negative = false;
bool tmpS = smaller->m_negative;
smaller->m_negative = false;
CBigInt *ret = bigger->add(smaller);
ret->m_negative = tmpB;
bigger->m_negative = tmpB;
smaller->m_negative = tmpS;
return ret;
}
CBigInt *CBigInt::mul( const CBigInt *oth ) const {
std::unique_ptr<CBigInt> ret( new CBigInt() );
std::unique_ptr<CBigInt> shifted( new CBigInt(*this) );
for (uint64_t i = 0; i < (oth->m_data.size() * CHUNK_BITS); ++i) {
if (oth->getBit(i)) {
ret->addDestructive( shifted.get() );
}
shifted->pushLeft(0);
}
ret->m_negative = m_negative ^ oth->m_negative;
return ret.release();
}
CBigInt *CBigInt::div( const CBigInt *oth ) const {
auto ret = divmod(oth);
delete ret.second;
return ret.first;
}
CBigInt *CBigInt::mod( const CBigInt *oth ) const {
auto ret = divmod(oth);
delete ret.first;
return ret.second;
}
CBigInt *CBigInt::gcd( const CBigInt *oth ) const {
auto ret = rea(oth);
delete ret.second;
return ret.first;
}
CBigInt *CBigInt::lcm( const CBigInt *oth ) const {
auto ret = rea(oth);
delete ret.first;
return ret.second;
}
CBigInt *CBigInt::shf( int64_t shift ) const {
CBigInt *ret = new CBigInt();
if (shift == 0) {
delete ret;
ret = new CBigInt(*this);
}
if (shift < 0) {
throw std::logic_error("invalid bitshift");
}
if (shift > 0) {
size_t bitOffset = shift % CHUNK_BITS;
size_t chunkOffset = shift / CHUNK_BITS;
size_t numNewChunks = m_data.size() + ((shift + CHUNK_BITS - 1) / CHUNK_BITS);
ret->m_data = std::vector<CHUNK_TYPE>( chunkOffset, 0 );
ret->m_data.reserve( numNewChunks );
CHUNK_TYPE carry = 0;
for (uint64_t i = 0; i < m_data.size(); ++i) {
ret->m_data.push_back(carry | (m_data[i] << bitOffset));
carry = m_data[i] >> (CHUNK_BITS - bitOffset);
}
if (carry)
ret->m_data.push_back(carry);
}
return ret;
}
CBigInt* CBigInt::power(uint64_t pow) const {
if (pow == 0)
return new CBigInt(0);
if (pow == 1)
return new CBigInt(*this);
if (!(pow % 2)) { // a^(2b) = 2(a^b)
CBigInt* halfPower = power(pow / 2);
CBigInt* ret = halfPower->mul(halfPower);
delete halfPower;
return ret;
}
CBigInt* ret = new CBigInt(*this);
for (uint64_t i = 1; i < pow; ++i) {
CBigInt* tmp = ret->mul(this);
delete ret;
ret = tmp;
}
ret->m_negative = (pow % 2 && m_negative);
return ret;
}
bool CBigInt::addDestructive( CBigInt *oth ) {
m_data.reserve( std::max(m_data.size() + 1, oth->m_data.size() + 1) );
bool carry = 0;
for (uint64_t i = 0; i < std::max(m_data.size(), oth->m_data.size()); ++i) {
CHUNK_TYPE tmp = ( i < m_data.size() ? m_data[i] : 0 ) + ( i < oth->m_data.size() ? oth->m_data[i] : 0 ) + carry;
carry = (tmp < ((i < m_data.size()) ? m_data[i] : oth->m_data[i]));
if (m_data.size() > i)
m_data[i] = tmp;
else
m_data.push_back(tmp);
}
if (carry)
m_data.push_back(1);
return true;
}
bool CBigInt::subDestructive( CBigInt *oth ) {
bool borrow = 0;
size_t len = oth->m_data.size();
for (size_t i = 0; i < len; ++i) {
CHUNK_TYPE tmp = m_data[i];
if (borrow) {
if (m_data[i] == 0) {
tmp = CHUNK_MAX;
} else {
tmp = m_data[i] - 1;
borrow = false;
}
}
borrow = m_data[i] < oth->m_data[i];
tmp -= oth->m_data[i];
m_data[i] = tmp;
}
return true;
}
bool CBigInt::isZero( void ) const {
if (!m_data.size())
return true;
for (uint64_t i = 0; i < m_data.size(); ++i)
if (m_data[i])
return false;
return true;
}
std::pair<CBigInt*, CBigInt*> CBigInt::divmod( const CBigInt* oth) const {
if (oth->isZero()) {
throw std::logic_error("Division by zero");
}
std::unique_ptr<CBigInt> quotient( new CBigInt());
std::unique_ptr<CBigInt> remainder( new CBigInt());
const CBigInt *numerator(this);
std::unique_ptr<CBigInt> divisor( new CBigInt(*oth));
// Calculate the number of bits in the numerator and divisor
int64_t numeratorBits = ((numerator->m_data.size() - 1) * CHUNK_BITS );
if (numerator->m_data.size())
numeratorBits += ( 64 - __builtin_clzll(numerator->m_data[numerator->m_data.size() - 1]) );
int64_t divisorBits = ((divisor->m_data.size() - 1) * CHUNK_BITS);
if (divisor->m_data.size())
divisorBits += ( 64 - __builtin_clzll(divisor->m_data[divisor->m_data.size() - 1]) );
int64_t shift = numeratorBits - divisorBits;
if (shift < 0) {
return { new CBigInt(), new CBigInt(*this) };
}
for (int64_t i = numeratorBits - 1; i >= 0; --i) {
remainder->pushLeft( getBit(i) );
if (remainder->cmp(divisor.get()) >= 0) {
remainder->subDestructive(divisor.get());
quotient->setBit(i, 1);
}
}
quotient->m_negative = (m_negative ^ oth->m_negative);
remainder->m_negative = false; // Ensure the remainder is positive
quotient->optimize();
remainder->optimize();
return { quotient.release(), remainder.release() };
}
std::pair<CBigInt*, CBigInt*> CBigInt::rea( const CBigInt* oth) const {
//* GCD LCM
std::unique_ptr<CBigInt> a(new CBigInt(*this));
std::unique_ptr<CBigInt> b(new CBigInt(*oth));
if (a->isZero() || b->isZero()) {
return { a->add(b.get()), new CBigInt(0) };
}
while (!b->isZero()) {
auto tmp = a->divmod(b.get());
a.reset(b.release());
b.reset(tmp.second);
delete tmp.first;
}
std::unique_ptr<CBigInt> lcm(mul(oth));
lcm.reset( lcm->div(a.get()) );
return { a.release(), lcm.release() };
}
bool CBigInt::getBit( uint64_t i) const {
if (i > m_data.size() * CHUNK_BITS)
return false;
return (m_data[ i / CHUNK_BITS ] & ( 1u << ( i % CHUNK_BITS )));
}
bool CBigInt::setBit( uint64_t i, bool b) {
while (i >= m_data.size() * CHUNK_BITS) {
m_data.push_back(0);
}
if (b)
m_data[ i / CHUNK_BITS ] |= ( (CHUNK_TYPE)1 << (( i % CHUNK_BITS )));
else
m_data[ i / CHUNK_BITS ] &= ~( (CHUNK_TYPE)1 << (( i % CHUNK_BITS )));
return true;
}
int CBigInt::cmp( const CBigInt *oth ) const {
if (m_data.size() != oth->m_data.size())
return m_data.size() - oth->m_data.size();
for (int64_t i = m_data.size() - 1; i >= 0; --i) {
if (m_data[i] != oth->m_data[i])
return 1 + ( -2 * (m_data[i] < oth->m_data[i]));
}
return 0;
}
bool CBigInt::pushLeft( bool i ) {
bool carry = i;
CHUNK_TYPE mask = (uint64_t)1 << (CHUNK_BITS - 1);
for (uint64_t j = 0; j < m_data.size(); ++j) {
bool tmp = m_data[j] & mask;
m_data[j] <<= 1;
m_data[j] |= carry;
carry = tmp;
}
if (carry)
m_data.push_back(1);
return true;
}
bool CBigInt::optimize( void ) {
if (m_data.size() < 1) {
m_negative = false;
return true;
}
// Delete trailing zeros
for (int i = m_data.size() - 1; !m_data[i] && i ; m_data.pop_back(), --i);
// Shrink the capacity if it's significantly larger than the size
if (m_data.capacity() >= 2 * m_data.size())
m_data.shrink_to_fit();
// remove negative flag if its zero
if (isZero())
m_negative = false;
return true;
} // delete zeros + shrink to fit

143
src/code/binsaver.cpp Normal file
View File

@ -0,0 +1,143 @@
#include "../include/binsaver.hpp"
CBinsaver::CBinsaver(std::map<std::string, CRational> &variablesMap) : CSaver(variablesMap) {}
CBinsaver::~CBinsaver() {}
bool CBinsaver::save(const std::string& fileName) {
ssize_t variableCount = m_variables.size();
// ssize_t fileHash = 0;
// uint64_t hashWord;
std::ofstream ofs(fileName);
if (!ofs || !ofs.good())
return false;
std::stringstream ss;
// write variable count
ss << std::to_string(variableCount) + "\n";
// write variables
for (auto& i : m_variables) {
ss << i.first + " " + i.second.toString() + "\n";
}
// calculate file hash
// std::stringstream hashSS(ss.str());
// while (hashSS.read(reinterpret_cast<char*>(&hashWord), sizeof(hashWord))) {
// fileHash ^= hashWord;
// }
// ss << "hash " + std::to_string(fileHash);
ofs << ss.rdbuf();
ofs.close();
return true;
}
bool CBinsaver::load( const std::string &fileName ) {
ssize_t variableCount;
std::ifstream ifs(fileName);
if (!ifs || !ifs.good()) {
m_log = "file reading error";
return false;
}
std::string buffer;
std::getline( ifs, buffer );
// check if variable count is valid
if (buffer.size() < 1 || buffer.size() > 10 || !(std::all_of(buffer.begin(), buffer.end(),
[](unsigned char ch) { return std::isdigit(ch); }) )) {
ifs.close();
m_log = "invalid argument count";
return false;
}
variableCount = std::stoll(buffer);
if (variableCount < 0) {
ifs.close();
m_log = "invalid argument count";
return false;
}
for (;variableCount && std::getline(ifs, buffer); --variableCount) {
std::istringstream iss(buffer);
std::string varName;
std::string numStr;
std::string denStr;
std::string slash;
bool numSign = 0;
bool denSign = 0;
if (!(iss >> varName >> numStr >> slash >> denStr) || slash != "/") {
ifs.close();
m_log = "wrong format";
return false;
}
if (numStr.at(0) == '-') {
numSign = 1;
numStr = numStr.substr(1);
}
if (denStr.at(0) == '-') {
denSign = 1;
denStr = denStr.substr(1);
}
if (!(std::all_of(numStr.begin(), numStr.end(), [](unsigned char ch) { return std::isdigit(ch); }) )
|| !(std::all_of(denStr.begin(), denStr.end(), [](unsigned char ch) { return std::isdigit(ch); }) )) {
ifs.close();
m_log = "invalid number";
return false;
}
for ( auto &c : numStr)
c -= 48;
for ( auto &c : denStr)
c -= 48;
CBigInt *num = new CBigInt(0);
uint64_t insertPosition = 0;
while (!std::all_of(numStr.begin(), numStr.end(), [](char c) { return c == 0; })) {
num->setBit(insertPosition++, CParser::stringDivideDecimal(numStr));
}
CBigInt *den = new CBigInt(0);
insertPosition = 0;
while (!std::all_of(denStr.begin(), denStr.end(), [](char c) { return c == 0; })) {
den->setBit(insertPosition++, CParser::stringDivideDecimal(denStr));
}
num->m_negative = numSign;
den->m_negative = denSign;
if (!m_variables.emplace(varName, CRational(num, den)).second) {
// delete num;
// delete den;
// ifs.close();
// m_log = "varable already exists";
// return false;
}
}
ifs.close();
return true;
}
/*
#binsaveformat
##header
[64bit varible count]
##one variable
[64bit identifier len] [identif]
[64bit datalen] [data]
##padding zeros to 8byte multiply
[0x0000]
[64bit hash]
##hash
xor all 64bit chunks
*/

11
src/code/division.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "../include/rational.hpp"
#include "../include/division.hpp"
CDivision::CDivision( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CDivision::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
if (b->m_numerator->isZero())
throw std::logic_error("division by zero");
return a->div(b.get());
}

25
src/code/exponencial.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "../include/rational.hpp"
#include "../include/exponencial.hpp"
CExponencial::CExponencial( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CExponencial::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
CBigInt one(1);
if (b->m_denominator->cmp(&one)) {
std::unique_ptr<CRational> tmp (a->ln());
tmp.reset( tmp->mul(b.get()) );
return tmp->exp();
}
if (b->isNegative()) {
b->negate();
auto tmp = new CRational( a->m_denominator->power(b->m_numerator->debugDump()),
a->m_numerator ->power(b->m_numerator->debugDump()) );
b->negate();
return tmp;
}
return new CRational( a->m_numerator ->power(b->m_numerator->debugDump()),
a->m_denominator->power(b->m_numerator->debugDump()) );
}

18
src/code/logarithm.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "../include/rational.hpp"
#include "../include/logarithm.hpp"
CLogarith::CLogarith( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CLogarith::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
if (a->m_numerator->isZero()) { // base zero -> neturtal log
return b->ln();
}
if (a->isNegative() || b->isNegative())
throw std::logic_error("logarith on negative numbers");
a.reset(a->ln());
b.reset(b->ln());
return b->div(a.get());
}

27
src/code/main.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "../include/parser.hpp"
#include <cstdio>
int main() {
std::string inputCursor = "> ";
std::string outputCursor = "= ";
std::cout <<
"█▀▀ ▄▀█ █ █▀▀" << '\n' <<
"█▄▄ █▀█ █▄▄ █▄▄" << '\n' <<
"version 1.0.a" << std::endl;
CParser a;
while (a.m_continue) {
std::string input;
std::cout << inputCursor;
if (!std::getline( std::cin, input))
break;
if (input.empty())
continue;
a.convert(input);
std::cout << outputCursor << a.m_output << std::endl;
}
return 0;
}

12
src/code/modulo.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "../include/rational.hpp"
#include "../include/modulo.hpp"
CModulo::CModulo( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CModulo::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
CBigInt one(1);
if (a->m_denominator.get()->cmp(&one) || b->m_denominator.get()->cmp(&one))
throw "mod on racional not supported";
return new CRational( a->m_numerator.get()->mod(b->m_numerator.get()));
}

View File

@ -0,0 +1,9 @@
#include "../include/rational.hpp"
#include "../include/multiplication.hpp"
CMultiplication::CMultiplication( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CMultiplication::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
return a->mul(b.get());
}

3
src/code/operator.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "../include/operator.hpp"
COperator::COperator( CPrintable *a, CPrintable *b ) : m_firstArgument(a), m_secondArgument(b) {}

677
src/code/parser.cpp Normal file
View File

@ -0,0 +1,677 @@
#include "../include/parser.hpp"
CParser::CParser() {
m_variables.emplace("ans", CRational(new CBigInt(0)));
m_continue = 1;
m_saver.reset(new CTextsaver(m_variables));
// CRational::termPrecision = 4;
}
CParser::~CParser() { clear(); }
uint64_t CParser::getOperatorPriority( TOKEN t ) const {
uint64_t ret = 0;
switch (t)
{
case DEREFERENCE:
ret = 8;
break;
case LN:
ret = 7;
break;
case EXP:
ret = 6;
break;
case DIVIDE:
ret = 5;
break;
case MULTIPLY:
ret = 4;
break;
case MOD:
ret = 3;
break;
case ADD:
ret = 2;
break;
case SUBTRACT:
ret = 1;
break;
default:
// throw "unresolvable syntax error";
break;
}
return ret;
}
CParser::CMD CParser::recognizeCommand( uint64_t commandPos ) const {
// position in raw lexems
CParser::CMD ret = UNKNOWN_CMD;
char c = m_input[m_rawLexems[commandPos].second];
// std::cout << c;
if (!std::islower(c))
return ret;
switch (c) {
case 'q':
ret = QUIT_CMD;
break;
case 'h':
ret = HELP_CMD;
break;
case 'p':
ret = PRECISSION_CMD;
break;
case 's':
ret = SAVE_CMD;
break;
case 'l':
ret = LOAD_CMD;
break;
case 'r':
ret = ROUND_CMD;
break;
default:
return ret;
break;
}
char nextChar = m_input[m_rawLexems[commandPos].second + 1];
if (m_input.size() == (uint64_t)m_rawLexems[commandPos].second
|| nextChar == ' '
|| nextChar == '\0') // short command
return ret;
std::string comStr;
switch (ret) {
case QUIT_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("quit") - 1);
ret = comStr == "quit" ? QUIT_CMD : UNKNOWN_CMD;
break;
case HELP_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("help") - 1);
ret = comStr == "help" ? HELP_CMD : UNKNOWN_CMD;
break;
case PRECISSION_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("precision") - 1);
ret = comStr == "precision" ? PRECISSION_CMD : UNKNOWN_CMD;
break;
case SAVE_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("save") - 1);
ret = comStr == "save" ? SAVE_CMD : UNKNOWN_CMD;
break;
case LOAD_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("load") - 1);
ret = comStr == "load" ? LOAD_CMD : UNKNOWN_CMD;
break;
case ROUND_CMD:
comStr = m_input.substr( m_rawLexems[commandPos].second, sizeof("round") - 1);
ret = comStr == "round" ? ROUND_CMD : UNKNOWN_CMD;
break;
default:
break;
}
return ret;
}
CParser::CMD CParser::parseCommand( uint64_t &startPos ) const {
// position in raw lexems
if (m_rawLexems[startPos].first == COMMAND) {
return recognizeCommand(startPos + 1);
} else {
startPos = 1;
return NONE_CMD;
}
}
bool CParser::executeCommand( CParser::CMD cmd ) {
switch (cmd) {
case QUIT_CMD:
m_continue = 0;
m_output = "quitting..";
return false;
break;
case HELP_CMD:
helpCommand();
return false;
break;
case PRECISSION_CMD:
if (m_processedLexems.size() != 7)
return false;
try {
CRational::termPrecision = std::stoull(m_input.substr(m_rawLexems[4].second));
m_output = "precision set";
if (CRational::termPrecision == 0) {
m_output += ", zero will propably break exp and ln calculations!";
}
} catch ( std::exception &e ) {
m_output = "invalid precision, recommended values are 1-10";
}
return false;
break;
case SAVE_CMD:
if (m_processedLexems.size() != 7)
return false;
if (m_saver->save( m_input.substr(m_rawLexems[4].second) ))
m_output = "save successful";
else
m_output = "save failed";
return false;
break;
case LOAD_CMD:
if (m_processedLexems.size() != 7)
return false;
if (m_saver->load( m_input.substr(m_rawLexems[4].second) ))
m_output = "load successful";
else
m_output = "load failed";
return false;
break;
case ROUND_CMD:
if (m_processedLexems.size() != 8 ||
(m_processedLexems[4].first != NUMBER && m_processedLexems[5].first != NUMBER))
return false;
try {
uint64_t decimals = std::stoull( m_input.substr(m_rawLexems[4].second) );
//todo check
m_output = m_processedLexems[5].second->round(decimals);
} catch (std::exception &e) {
m_output = "rounding failed";
}
return false;
break;
case UNKNOWN_CMD:
m_output = "unknown command. try !help";
return false;
break;
case NONE_CMD:
break;
}
return true;
}
bool CParser::helpCommand() {
m_output = "!h !help - for this quick help\n";
m_output += "!q !quit - to quit\n";
m_output += "!p !precision x - to set number of terms for aproximating logarith and exponencial (positive int or LOW=4/HIGH=8, values below 1 or above 10 are not recommended)\n";
m_output += "!s !save - to save variables (this is not automatic on quit!)\n";
m_output += "!l !load - to load them back (this is not automatic on quit!)\n\n";
m_output += "!r !round x y - to round y to x decimals\n";
m_output += "start by typing numbers and operators\n";
m_output += "exact operation supported for racional numbers:\n";
m_output += "addition (+), subtraction (-), multiplication (*), division (/)\n";
m_output += "example usage:\n";
m_output += "3.25 * ( -4/3 + 3/4 )\n\n";
m_output += "aproximation of logarith (&) and exponencial (^)\n";
m_output += "example usage:\n";
m_output += "(3)&(27) represents logartih with base 3 out of 27\n\n";
m_output += "(everything is case sensitive!)\n";
m_output += "run \"dot ./doc/grammar.dot -T x11\" to see grammar guide\n";
return true;
}
void CParser::clear() {
m_rawLexems.clear();
for( auto &i : m_processedLexems )
if (i.second)
delete i.second;
m_processedLexems.clear();
m_output = "SYNTAX ERROR try !help";
}
CParser::TOKEN CParser::recognize( char c ) const {
if (c == ' ')
return CParser::TOKEN::WHITESPACE;
if (c >= '0' && c <= '9')
return CParser::TOKEN::NUMBER;
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
return CParser::TOKEN::LABEL;
CParser::TOKEN ret;
switch (c) {
case '!':
ret = COMMAND;
break;
case '$':
ret = DEREFERENCE;
break;
case '(':
ret = OPEN;
break;
case ')':
ret = CLOSE;
break;
case '&':
ret = LN;
break;
case '^':
ret = EXP;
break;
case '/':
ret = DIVIDE;
break;
case '*':
ret = MULTIPLY;
break;
case '%':
ret = MOD;
break;
case '+':
ret = ADD;
break;
case '-':
ret = SUBTRACT;
break;
case '.':
ret = DOT;
break;
case '=':
ret = SAVE;
break;
default:
ret = SYNTAX_ERROR;
break;
}
return ret;
}
CRational *CParser::parseNumber( uint64_t i, bool isDecimal = false, bool hasSign = false ) const {
CBigInt *num = new CBigInt(0);
CBigInt *den;
std::string rawNumber = readToken(i);
if (isDecimal) {
rawNumber += readToken(i+2);
int64_t decimalLen = m_rawLexems[i+2].second;
for (;decimalLen < m_rawLexems[i+3].second && m_input[decimalLen] != ' '; ++decimalLen);
decimalLen -= m_rawLexems[i+2].second;
if (decimalLen <= 10)
den = new CBigInt(std::pow(10, decimalLen));
else {
CBigInt *tmp = new CBigInt( 10 );
den = tmp->power(decimalLen);
delete tmp;
}
} else {
den = new CBigInt(1);
}
hasSign ^= 1;
hasSign ^= 1;
// convert ascii
for ( auto &c : rawNumber) {
c -= 48;
}
uint64_t insertPosition = 0;
while (!std::all_of(rawNumber.begin(), rawNumber.end(), [](char c) { return c == 0; })) {
num->setBit(insertPosition++, stringDivideDecimal(rawNumber));
}
return new CRational(num, den);
}
bool CParser::stringDivideDecimal( std::string &s ) { // returns next bit for binary conversion
uint16_t additive = 0;
uint16_t nextAdditive;
for (char &c : s) {
nextAdditive = (c % 2) * 5;
c = c / 2 + additive;
std::swap(additive, nextAdditive);
}
return additive;
}
bool CParser::tokenize() {
m_rawLexems.emplace_back(START, -1);
m_rawLexems.emplace_back(OPEN, -1);
for (uint64_t i = 0; i < m_input.size(); ++i) {
TOKEN type = recognize(m_input[i]);
if (type == SYNTAX_ERROR)
return false;
if (type == WHITESPACE) {
if (m_rawLexems.back().first != WHITESPACE)
m_rawLexems.emplace_back(WHITESPACE, i);
continue;
}
if (type == NUMBER) {
if (m_rawLexems.back().first != NUMBER)
m_rawLexems.emplace_back(NUMBER, i);
continue;
}
if (type == LABEL) {
if (m_rawLexems.back().first != LABEL)
m_rawLexems.emplace_back(LABEL, i);
continue;
}
if (type == COMMAND || type == SAVE) {
// if (m_rawLexems.size() == 2)
m_rawLexems.emplace_back(type, i);
// else
// return false;
continue;
}
m_rawLexems.emplace_back(type, i);
}
m_rawLexems.emplace_back(CLOSE, m_input.size());
m_rawLexems.emplace_back(END, m_input.size());
return m_rawLexems.size() > 4;
}
std::string CParser::readToken( uint64_t i ) const {
std::string ret;
for ( int64_t j = m_rawLexems[i].second; j < m_rawLexems[i+1].second && m_input[j] != ' '; ++j ) {
ret.push_back(m_input[j]);
}
return ret;
}
bool CParser::grammarPreprocess() {
// strip whitespaces
m_rawLexems.erase(
std::remove_if(
m_rawLexems.begin(),
m_rawLexems.end(),
[](const std::pair<CParser::TOKEN, int64_t>& lexem) {
return lexem.first == WHITESPACE;
}
),
m_rawLexems.end()
);
m_saveInto.clear();
if (m_rawLexems[2].first == LABEL) {
uint64_t varLen = m_rawLexems[2].second;
for (; (m_input[varLen] >= 'a' && m_input[varLen] <= 'z') || (m_input[varLen] >= 'A' && m_input[varLen] <= 'Z'); ++ varLen);
varLen -= m_rawLexems[2].second;
m_saveInto = m_input.substr(m_rawLexems[2].second, varLen);
if (m_rawLexems[3].first != SAVE) {
m_output = "SYNTAX ERROR try " + m_saveInto + " = " +
(m_input.substr(m_rawLexems[3].second).size() ? m_input.substr(m_rawLexems[3].second) : "(1 + 2) * 3");
return false;
}
m_rawLexems.erase( m_rawLexems.begin() + 2, m_rawLexems.begin() + 4);
}
// process numbers
uint64_t i = 0;
for (; i < m_rawLexems.size(); ++i) {
bool numberHasDecimals = false;
bool numberHasDot = false;
bool numberHasSign = false;
bool numberNegative = false;
if (m_rawLexems[i].first == WHITESPACE)
continue;
if (m_rawLexems[i].first == DOT) {
if (m_rawLexems[i+1].first == NUMBER) {
m_processedLexems.emplace_back(NUMBER, parseNumber(i+1, false, false));
uint64_t decimalLen = m_rawLexems[i+1].second;
for (; m_input[decimalLen] >= '0' && m_input[decimalLen] <= '9'; ++decimalLen);
decimalLen -= m_rawLexems[i+1].second;
if (decimalLen <= 10)
m_processedLexems.back().second->m_denominator.reset( new CBigInt(std::pow(10, decimalLen)));
else {
CBigInt *tmp = new CBigInt( 10 );
m_processedLexems.back().second->m_denominator.reset( tmp->power(decimalLen) );
delete tmp;
}
++i;
continue;
}
return false;
}
if (m_rawLexems[i].first == NUMBER) {
if (m_processedLexems.back().first == NUMBER)
return false;
numberHasDot = (m_rawLexems[i+1].first == DOT);
numberHasDecimals = numberHasDot && m_rawLexems[i+2].first == NUMBER;
numberHasSign = ((m_rawLexems[i-1].first == ADD || m_rawLexems[i-1].first == SUBTRACT)
&&
(m_processedLexems[m_processedLexems.size() -2].first != NUMBER
&& m_processedLexems[m_processedLexems.size() -2].first != CLOSE));
if (numberHasSign && !m_processedLexems.empty()) {
numberNegative = m_processedLexems.back().first == SUBTRACT;
m_processedLexems.pop_back();
}
m_processedLexems.emplace_back(NUMBER, parseNumber(i, numberHasDecimals, numberHasSign));
m_processedLexems.back().second->m_numerator->m_negative = numberNegative;
i += numberHasDot;
i += numberHasDecimals;
continue;
}
if (m_rawLexems[i].first == DEREFERENCE) {
if (m_rawLexems[i+1].first != LABEL) {
m_output = "invalid dereference";
return false;
}
uint64_t varLen = m_rawLexems[i+1].second;
for (; (m_input[varLen] >= 'a' && m_input[varLen] <= 'z') || (m_input[varLen] >= 'A' && m_input[varLen] <= 'Z'); ++ varLen);
varLen -= m_rawLexems[i+1].second;
std::string variableName = m_input.substr(m_rawLexems[i+1].second, varLen);
auto it = m_variables.find(variableName);
if (it == m_variables.end()) {
m_output = "unknown variable \"" + variableName + "\"";
return false;
}
CRational *value = new CRational((*it).second);
m_processedLexems.emplace_back(NUMBER, value);
++i;
continue;
}
m_processedLexems.emplace_back(m_rawLexems[i].first, nullptr);
}
return true;
}
std::string CParser::numberToString( CRational *r ) const {
std::string ret = r->m_numerator->toString();
CBigInt one(1);
if (r->m_denominator->cmp(&one))
ret += " / " + r->m_denominator->toString();
return ret;
}
COperator *CParser::spawnOperator( CParser::TOKEN t ) const {
COperator *ret = nullptr;
switch (t) {
case ADD:
ret = new CAddition();
break;
case SUBTRACT:
ret = new CSubtraction();
break;
case MULTIPLY:
ret = new CMultiplication();
break;
case DIVIDE:
ret = new CDivision();
break;
case MOD:
ret = new CModulo();
break;
case EXP:
ret = new CExponencial();
break;
case LN:
ret = new CLogarith();
break;
default:
break;
}
return ret;
}
CPrintable *CParser::parse( uint64_t &readIndex ) {
enum STATE {
FIRST_OPERAND,
OPERATOR,
SECOND_OPERAND
};
STATE parserState = FIRST_OPERAND;
CPrintable *firstOperand = nullptr;
// CPrintable *secondOperand = nullptr;
COperator *currentOperator = nullptr;
COperator *lastOperator = nullptr;
uint64_t lastPriority = 0;
++readIndex; // skip first bracket
while (m_processedLexems[readIndex].first != CLOSE) {
if (m_processedLexems[readIndex].first == OPEN) {
if (parserState == FIRST_OPERAND) {
firstOperand = parse(readIndex);
} else if (parserState == SECOND_OPERAND) {
currentOperator->m_secondArgument.reset( parse(readIndex) );
} else {
goto fail;
}
parserState = OPERATOR;
++readIndex;
continue;
}
switch (parserState) {
case FIRST_OPERAND:
if (m_processedLexems[readIndex].first == OPEN) {
firstOperand = parse(readIndex);
parserState = OPERATOR;
break;
}
if (m_processedLexems[readIndex].first == NUMBER) {
firstOperand = m_processedLexems[readIndex].second;
m_processedLexems[readIndex].second = nullptr;
parserState = OPERATOR;
break;
}
goto fail;
case OPERATOR:
if (m_processedLexems[readIndex].first == CLOSE)
goto sucess;
currentOperator = spawnOperator(m_processedLexems[readIndex].first);
if (!currentOperator)
goto fail;
if (lastOperator) {
uint64_t thisPriority = getOperatorPriority(m_processedLexems[readIndex].first);
if (lastPriority <= thisPriority) {
currentOperator->m_firstArgument.reset( lastOperator->m_secondArgument.release() );
lastOperator->m_secondArgument.reset( currentOperator );
} else {
currentOperator->m_firstArgument.reset( lastOperator );
lastOperator = currentOperator;
}
} else {
lastPriority = getOperatorPriority(m_processedLexems[readIndex].first);
lastOperator = currentOperator;
lastOperator->m_firstArgument.reset( firstOperand );
}
parserState = SECOND_OPERAND;
break;
case SECOND_OPERAND:
if (m_processedLexems[readIndex].first == OPEN) {
currentOperator->m_secondArgument.reset( parse(readIndex) );
parserState = OPERATOR;
break;
}
if (m_processedLexems[readIndex].first == NUMBER) {
currentOperator->m_secondArgument.reset( m_processedLexems[readIndex].second );
m_processedLexems[readIndex].second = nullptr;
parserState = OPERATOR;
break;
}
goto fail;
break;
}
++readIndex;
}
sucess:
if ((currentOperator && (!currentOperator->m_firstArgument.get() || !currentOperator->m_secondArgument.get())) ||
(lastOperator && (!lastOperator->m_firstArgument.get() || !lastOperator->m_secondArgument.get())))
throw std::logic_error("parsing failed");
if (lastOperator)
return lastOperator;
if (firstOperand)
return firstOperand;
fail:
throw std::logic_error("parsing failed");
}
void CParser::convert( const std::string &s ) {
clear();
m_input = s;
if (!tokenize())
return;
if (!grammarPreprocess())
return;
uint64_t readPos = 2;
CMD command = parseCommand(readPos);
if (command != NONE_CMD && !m_saveInto.empty())
return;
if (!executeCommand( command ))
return;
try {
m_head.reset( parse( readPos ) );
} catch (const std::exception &e) {
m_output = "Caught exception: " + std::string(e.what());
return;
} catch (...) {
m_output = "Caught unknown exception";
return;
}
if (m_processedLexems[++readPos].first != END) {
m_output = "bad parenthesis parity";
return;
}
if (!m_head.get()) {
m_output = "parsing failed";
return;
}
try {
auto evaluated = m_head->evaluate();
m_output = numberToString(evaluated);
auto ans = m_variables.find("ans");
(*ans).second.m_numerator.reset( evaluated->m_numerator.release() );
(*ans).second.m_denominator.reset( evaluated->m_denominator.release() );
if (!m_saveInto.empty()) {
auto var = m_variables.find(m_saveInto);
if (var == m_variables.end()) {
var = m_variables.emplace(m_saveInto, CRational( new CBigInt(0) )).first;
}
(*var).second.m_numerator.reset( new CBigInt( *(*ans).second.m_numerator.get() ));
(*var).second.m_denominator.reset( new CBigInt( *(*ans).second.m_denominator.get() ));
}
delete evaluated;
} catch (const std::exception &e) {
m_output = "Caught exception: " + std::string(e.what());
} catch (...) {
m_output = "Caught unknown exception";
}
}

237
src/code/rational.cpp Normal file
View File

@ -0,0 +1,237 @@
#include "../include/rational.hpp"
// #include "bigint.cpp"
uint64_t CRational::termPrecision = 4;
CRational::CRational( CBigInt *num, CBigInt *den )
: m_numerator(num), m_denominator(den) {}
CRational::CRational( CBigInt *num )
: m_numerator(num), m_denominator( new CBigInt(1) ) {}
CRational::CRational( const CRational &parent )
: m_numerator( std::make_unique<CBigInt>( *parent.m_numerator.get() )),
m_denominator( std::make_unique<CBigInt>( *parent.m_denominator.get() )) {}
CRational::~CRational() {}
std::string CRational::toString() {
simplify();
return /*((m_denominator->m_negative ^ m_numerator->m_negative) ? "-" : "") + */
(m_numerator->toString() + " / " + m_denominator->toString());
}
CRational *CRational::evaluate() {
simplify();
if (m_denominator->isZero())
throw std::logic_error("division by zero");
auto ret = new CRational(*this);
return ret;
}
CRational *CRational::add( CRational *oth ) {
setSameDenominator(oth);
CRational *ret = new CRational( m_numerator->add(oth->m_numerator.get()), new CBigInt(*m_denominator.get()) );
ret->simplify();
return ret;
}
CRational *CRational::sub( CRational *oth ) {
setSameDenominator(oth);
auto tmp = m_numerator->sub(oth->m_numerator.get());
CRational *ret = new CRational( tmp, new CBigInt(*m_denominator.get()) );
ret->simplify();
return ret;
}
CRational *CRational::mul( CRational *oth ) {
auto ret = new CRational(m_numerator->mul(oth->m_numerator.get()), m_denominator->mul(oth->m_denominator.get()));
ret->simplify();
return ret;
}
CRational *CRational::div( CRational *oth ) {
std::unique_ptr<CRational> inverse( new CRational( new CBigInt(*oth->m_denominator.get()), new CBigInt(*oth->m_numerator.get()) ) );
auto ret = mul( inverse.get() );
ret->simplify();
return ret;
}
CRational *CRational::power( uint64_t pow ) {
CRational *ret = new CRational( m_numerator->power(pow), m_denominator->power(pow) );
ret->simplify();
return ret;
}
CRational *CRational::term( uint64_t pow ) { // term for taylor series
CRational *numerator = power(pow);
CRational denominator( new CBigInt(pow), new CBigInt(1) );
CRational *ret = numerator->div(&denominator);
delete numerator;
return ret;
}
CRational *CRational::ln() {
if (m_numerator->isZero() || isNegative())
throw "ln of nonpositive number";
if (m_numerator->cmp(m_denominator.get()) < 1) {
std::unique_ptr<CRational> ret( new CRational( new CBigInt(0) ));
CRational *tmp = new CRational(new CBigInt(1));
std::unique_ptr<CRational> numerator( sub( tmp ));
delete tmp;
bool sign = false; // false: +, true: -
for (uint64_t i = 1; i <= termPrecision; ++i) {
std::unique_ptr<CRational> currentTerm( numerator->power(i) );
CRational *tmp = new CRational( new CBigInt(i) );
currentTerm.reset( currentTerm->div( tmp ));
delete tmp;
if (sign)
currentTerm->negate();
ret.reset( ret->add(currentTerm.get()) );
ret->simplify();
sign ^= 1;
}
return ret.release();
} else {
// Calculate the base for logarithm
CRational base( m_numerator->mod(m_denominator.get()), new CBigInt( *m_denominator.get() ));
CRational ln2( new CBigInt(3552463), new CBigInt(5125120) ); //https://scipp.ucsc.edu/~haber/webpage/Log2.pdf
// find highest numerator bit
m_numerator->optimize();
int64_t numeratorBits = ((m_numerator->m_data.size() - 1) * CHUNK_BITS );
if (m_numerator->m_data.size())
numeratorBits += ( 64 - __builtin_clzll(m_numerator->m_data[m_numerator->m_data.size() - 1]) );
CRational expoN( new CBigInt(numeratorBits), new CBigInt(1));
std::unique_ptr<CRational> ret(new CRational(new CBigInt(0)));
std::unique_ptr<CRational> numerator(new CRational(base));
//taylor series for |x-1| <= 1
bool sign = false; // false: +, true: -
// Taylor series expansion
for (uint64_t i = 1; i <= termPrecision; ++i) {
std::unique_ptr<CRational> currentTerm( numerator->power(i) );
CRational *tmp = new CRational(new CBigInt(i));
currentTerm.reset( currentTerm->div(tmp) );
delete tmp;
if (sign)
currentTerm->negate();
ret.reset(ret->add(currentTerm.get()));
ret->simplify();
sign ^= 1;
}
CRational *tmp = ln2.mul( &expoN );
ret.reset( ret->add( tmp ));
delete tmp;
return ret.release();
}
}
CRational *CRational::exp() {
// e^(a/b) = lim n->inf (1+(a/b)/n)^n = lim n->inf ((bn*a)/bn)^n
CBigInt *num = m_denominator->mul( new CBigInt(termPrecision) );
CBigInt *den = new CBigInt(*num);
num->addDestructive(m_numerator.get());
std::unique_ptr<CRational> ret (new CRational( num, den ));
ret->simplify();
ret.reset( ret->power(termPrecision) );
return ret.release();
}
std::string CRational::round( uint64_t decimals ) {
std::string ret;
if (m_denominator->m_data.size() <= 2 &&
m_numerator->m_data.size() <= 2) {
m_numerator->m_data.push_back(0);
m_denominator->m_data.push_back(0);
ret = std::to_string( *((double *)m_numerator->m_data.data())
/ *((double *)m_denominator->m_data.data()));
size_t dotPosition = ret.find('.');
if (dotPosition != std::string::npos)
ret = ret.substr(0, dotPosition + decimals + 1);
m_numerator->m_data.pop_back();
m_denominator->m_data.pop_back();
return ret;
}
throw std::length_error("too long to round");
CRational tmp(*this);
CBigInt newDen(std::pow(10, decimals));
int dif = m_denominator->cmp(&newDen);
if (dif > 0) {
CBigInt *factor = tmp.m_denominator->div(&newDen);
tmp.m_numerator.reset( tmp.m_numerator->div(factor) );
delete factor;
}
if (dif < 0) {
CBigInt *factor = newDen.div(tmp.m_denominator.get());
tmp.m_numerator.reset( tmp.m_numerator->mul(factor) );
delete factor;
}
ret = tmp.m_numerator->toString();
if ((ret.size() - decimals) > 0)
ret.insert( ret.size() - decimals, 1, ',' );
return ret;
}
bool CRational::setSameDenominator( CRational *oth ) {
if (m_denominator->cmp(oth->m_denominator.get()) == 0)
return true;
if ( m_numerator == 0 ) {
m_denominator.reset( new CBigInt( *oth->m_denominator.get() ));
return true;
}
if ( oth->m_numerator == 0 ) {
oth->m_denominator.reset( new CBigInt( *m_denominator.get() ));
return true;
}
std::unique_ptr<CBigInt> lcm( m_denominator->lcm(oth->m_denominator.get()) );
std::unique_ptr<CBigInt> thisCoef( lcm->div(m_denominator.get()));
std::unique_ptr<CBigInt> othCoef( lcm->div(oth->m_denominator.get()));
m_denominator.reset( m_denominator->mul(thisCoef.get()) );
m_numerator.reset( m_numerator ->mul(thisCoef.get()) );
oth->m_denominator.reset( oth->m_denominator->mul(othCoef.get()) );
oth->m_numerator.reset( oth->m_numerator ->mul(othCoef.get()) );
return true;
}
void CRational::simplify() {
if (m_numerator == nullptr)
return;
std::unique_ptr<CBigInt> gcd( m_numerator->gcd(m_denominator.get()) );
m_numerator.reset( m_numerator->div(gcd.get()) );
m_denominator.reset( m_denominator->div(gcd.get()) );
m_numerator->m_negative = isNegative();
m_denominator->m_negative = false;
return;
}
inline int64_t CRational::intfactorial( uint64_t i ) {
int64_t ret = 1;
for ( uint64_t j = 0; j < i; ret *= ++j);
return ret;
}
bool CRational::isNegative() const {
return m_numerator->m_negative ^ m_denominator->m_negative;
}
void CRational::negate() {
m_numerator->m_negative = !m_numerator->m_negative;
}
double CRational::debugDump() {
return (double) m_numerator->debugDump() / (double) m_denominator->debugDump();
}

4
src/code/saver.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "../include/saver.hpp"
CSaver::CSaver(std::map<std::string, CRational> &variablesMap)
: m_variables(variablesMap) {}

9
src/code/subtraction.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "../include/rational.hpp"
#include "../include/subtraction.hpp"
CSubtraction::CSubtraction( CPrintable *a, CPrintable *b ) : COperator(a, b) {}
CRational *CSubtraction::evaluate() {
std::unique_ptr<CRational> a (m_firstArgument->evaluate());
std::unique_ptr<CRational> b (m_secondArgument->evaluate());
return a->sub(b.get());
}

144
src/code/textsaver.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "../include/textsaver.hpp"
#include <iostream>
#include <sstream>
#include <cctype>
CTextsaver::CTextsaver(std::map<std::string, CRational> &variablesMap) : CSaver(variablesMap) {}
CTextsaver::~CTextsaver() {}
bool CTextsaver::save(const std::string& fileName) {
ssize_t variableCount = m_variables.size();
// ssize_t fileHash = 0;
// uint64_t hashWord;
std::ofstream ofs(fileName);
if (!ofs || !ofs.good())
return false;
std::stringstream ss;
// write variable count
ss << std::to_string(variableCount) + "\n";
// write variables
for (auto& i : m_variables) {
ss << i.first + " " + i.second.toString() + "\n";
}
// calculate file hash
// std::stringstream hashSS(ss.str());
// while (hashSS.read(reinterpret_cast<char*>(&hashWord), sizeof(hashWord))) {
// fileHash ^= hashWord;
// }
// ss << "hash " + std::to_string(fileHash);
ofs << ss.rdbuf();
ofs.close();
return true;
}
bool CTextsaver::load( const std::string &fileName ) {
ssize_t variableCount;
std::ifstream ifs(fileName);
if (!ifs || !ifs.good()) {
m_log = "file reading error";
return false;
}
std::string buffer;
std::getline( ifs, buffer );
// check if variable count is valid
if (buffer.size() < 1 || buffer.size() > 10 || !(std::all_of(buffer.begin(), buffer.end(),
[](unsigned char ch) { return std::isdigit(ch); }) )) {
ifs.close();
m_log = "invalid argument count";
return false;
}
variableCount = std::stoll(buffer);
if (variableCount < 0) {
ifs.close();
m_log = "invalid argument count";
return false;
}
for (;variableCount && std::getline(ifs, buffer); --variableCount) {
std::istringstream iss(buffer);
std::string varName;
std::string numStr;
std::string denStr;
std::string slash;
bool numSign = 0;
bool denSign = 0;
if (!(iss >> varName >> numStr >> slash >> denStr) || slash != "/") {
ifs.close();
m_log = "wrong format";
return false;
}
if (numStr.at(0) == '-') {
numSign = 1;
numStr = numStr.substr(1);
}
if (denStr.at(0) == '-') {
denSign = 1;
denStr = denStr.substr(1);
}
if (!(std::all_of(numStr.begin(), numStr.end(), [](unsigned char ch) { return std::isdigit(ch); }) )
|| !(std::all_of(denStr.begin(), denStr.end(), [](unsigned char ch) { return std::isdigit(ch); }) )) {
ifs.close();
m_log = "invalid number";
return false;
}
for ( auto &c : numStr)
c -= 48;
for ( auto &c : denStr)
c -= 48;
CBigInt *num = new CBigInt(0);
uint64_t insertPosition = 0;
while (!std::all_of(numStr.begin(), numStr.end(), [](char c) { return c == 0; })) {
num->setBit(insertPosition++, CParser::stringDivideDecimal(numStr));
}
CBigInt *den = new CBigInt(0);
insertPosition = 0;
while (!std::all_of(denStr.begin(), denStr.end(), [](char c) { return c == 0; })) {
den->setBit(insertPosition++, CParser::stringDivideDecimal(denStr));
}
num->m_negative = numSign;
den->m_negative = denSign;
if (!m_variables.emplace(varName, CRational(num, den)).second) {
// delete num;
// delete den;
// ifs.close();
// m_log = "varable already exists";
// return false;
}
}
ifs.close();
return true;
}
/*
#textsaveformat
##header
[varible count]
##one variable
[identif] [whitespace] [textdata]
##padding whitespaces to 8byte multiply
[0x0000]
// [64bit hash in text]
// ##hash
// xor all 64bit chunks
*/

35
src/include/addition.hpp Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @class CAddition
* @brief The CAddition class represents the addition operator.
* It inherits from the COperator class.
*/
class CAddition : public COperator {
public:
/**
* @brief Constructor for CAddition class.
* @param a The first argument of the addition operation.
* @param b The second argument of the addition operation.
*/
CAddition( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CAddition class.
*/
CAddition() = default;
/**
* @brief Destructor for CAddition class.
*/
~CAddition() = default;
/**
* @brief Evaluates the addition operation.
* @return A CRational pointer representing the result of the addition.
*/
virtual CRational *evaluate() override;
private:
};

202
src/include/bigint.hpp Normal file
View File

@ -0,0 +1,202 @@
#pragma once
#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>
#define CHUNK_TYPE uint32_t
#define CHUNK_BYTES sizeof(CHUNK_TYPE)
#define CHUNK_BITS (CHUNK_BYTES * 8)
#define CHUNK_MAX UINT32_MAX
/**
* @class CBigInt
* @brief Class representing a big integer.
*/
class CBigInt {
public:
/**
* @brief Default constructor.
*/
CBigInt();
/**
* @brief Constructor that initializes the integer with a given value.
* @param parent The value to initialize the integer with.
*/
CBigInt( int64_t parent );
/**
* @brief Copy constructor.
* @param oth The CBigInt object to copy.
*/
CBigInt( const CBigInt &oth );
/**
* @brief Destructor.
*/
~CBigInt();
/**
* @brief Prints the big integer for debugging purposes.
*/
void debugPrint();
/**
* @brief Dumps first 63bits and sign for debugging purposes.
* @return The dumped value of the big integer.
*/
int64_t debugDump();
/**
* @brief Converts the big integer to a string representation.
* @return The string representation of the big integer.
*/
std::string toString() const;
/**
* @brief Adds another big integer to this big integer.
* @param oth The big integer to add.
* @return A new big integer representing the sum.
*/
CBigInt *add( const CBigInt *oth ) const;
/**
* @brief Subtracts another big integer from this big integer.
* @param oth The big integer to subtract.
* @return A new big integer representing the difference.
*/
CBigInt *sub( const CBigInt *oth ) const;
/**
* @brief Multiplies another big integer with this big integer.
* @param oth The big integer to multiply.
* @return A new big integer representing the product.
*/
CBigInt *mul( const CBigInt *oth ) const;
/**
* @brief Divides this big integer by another big integer.
* uses divmod()
* @param oth The divisor big integer.
* @return A new big integer representing the quotient.
*/
CBigInt *div( const CBigInt *oth ) const;
/**
* @brief Divides this big integer by another big integer.
* uses divmod()
* @param oth The divisor big integer.
* @return A new big integer representing the quotient.
*/
CBigInt *mod( const CBigInt *oth ) const;
/**
* @brief Calculates the greatest common divisor of the current big integer and another big integer.
* uses rea()
* @param oth The other big integer.
* @return A new CBigInt object representing the greatest common divisor.
*/
CBigInt *gcd( const CBigInt *oth ) const;
/**
* @brief Calculates the least common multiple of the current big integer and another big integer.
* uses rea()
* @param oth The other big integer.
* @return A new CBigInt object representing the least common multiple.
*/
CBigInt *lcm( const CBigInt *oth ) const;
/**
* @brief Performs a left shift operation on the current big integer.
* @param shift The number of bits to shift.
* @return A new CBigInt object representing the shifted big integer.
*/
CBigInt *shf( int64_t shift ) const;
/**
* @brief Calculates the power of the current big integer raised to a given exponent.
* @param pow The exponent.
* @return A new CBigInt object representing the result of the power operation.
*/
CBigInt *power( uint64_t pow ) const;
/**
* @brief Adds another big integer destructively to the current big integer.
* @param oth The other big integer to be added.
* @return A boolean indicating the success of the addition operation.
*/
bool addDestructive( CBigInt *oth );
/**
* @brief Subtracts another big integer destructively from the current big integer.
* @param oth The other big integer to be subtracted.
* @return A boolean indicating the success of the subtraction operation.
*/
bool subDestructive( CBigInt *oth );
/**
* @brief Pushes a bit to the left of the big integer.
* @param i The bit to push to the left.
* @return A boolean indicating the success of the push operation.
*/
bool pushLeft( bool i );
/**
* @brief Gets the value of a specific bit at index i in the big integer.
* @param i The index of the bit to retrieve.
* @return The value of the bit at index i, or false if the index is out of range.
*/
bool getBit( uint64_t i) const ;
/**
* @brief Sets the value of a specific bit at index i in the big integer.
* @param i The index of the bit to set.
* @param b The value to set the bit to (true or false).
* @return A boolean indicating the success of the bit setting operation.
*/
bool setBit( uint64_t i, bool b);
/**
* @brief Compares the current big integer with another big integer.
* @param oth The other big integer to compare with.
* @return An integer representing the comparison result: 0 if the numbers are equal, a negative value if the current number is smaller, and a positive value if the current number is larger.
*/
int cmp( const CBigInt *oth ) const ;
/**
* @brief Optimizes the big integer by removing leading zero chunks.
* @return A boolean indicating the success of the optimization operation.
*/
bool optimize( void );
/**
* @brief Checks if the big integer is zero.
* @return A boolean indicating whether the big integer is zero (true) or not (false).
*/
bool isZero( void ) const ;
/**
* @brief Divides the current big integer by another big integer and calculates the remainder.
* @param oth The divisor big integer.
* @return A pair of CBigInt objects representing the quotient and remainder of the division.
*/
std::pair<CBigInt *, CBigInt *> divmod( const CBigInt *oth ) const;
/**
* @brief Computes the GCD and LCM of two big integers.
* @param oth The big integer to compute GCD and LCM with.
* @return A pair of CBigInt pointers, representing the GCD and LCM respectively.
*/
std::pair<CBigInt *, CBigInt *> rea( const CBigInt *oth ) const;
/**
* @brief Holds the chunks of the big integer's data.
*/
std::vector<CHUNK_TYPE> m_data;
/**
* @brief Indicates whether the big integer is negative.
*/
bool m_negative;
};

18
src/include/binsaver.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "../include/saver.hpp"
#include "../include/parser.hpp"
#include <iostream>
#include <sstream>
#include <cctype>
class CBinsaver : public CSaver {
private:
/* data */
public:
CBinsaver(std::map<std::string, CRational> &variablesMap);
~CBinsaver();
virtual bool save( const std::string &s ) override;
virtual bool load( const std::string &s ) override;
};

35
src/include/division.hpp Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @class CDivision
* @brief The CDivision class represents the division operator.
* It inherits from the COperator class.
*/
class CDivision : public COperator {
public:
/**
* @brief Constructor for CDivision class.
* @param a The numerator of the division operation.
* @param b The denominator of the division operation.
*/
CDivision( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CDivision class.
*/
CDivision() = default;
/**
* @brief Destructor for CDivision class.
*/
~CDivision() = default;
/**
* @brief Evaluates the division operation.
* @return A CRational pointer representing the result of the division.
*/
virtual CRational *evaluate() override;
private:
};

View File

@ -0,0 +1,35 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @class CExponencial
* @brief The CExponencial class represents an exponential operation.
* It inherits from the COperator class.
*/
class CExponencial : public COperator {
public:
/**
* @brief Constructor for CExponencial class.
* @param a The base of the exponential operation.
* @param b The exponent of the exponential operation.
*/
CExponencial( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CExponencial class.
*/
CExponencial() = default;
/**
* @brief Destructor for CExponencial class.
*/
~CExponencial() = default;
/**
* @brief Evaluates the exponential operation.
* @return A CRational pointer representing the result of the exponential operation.
*/
virtual CRational *evaluate() override;
private:
};

34
src/include/logarithm.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @brief The CLogarith class represents a logarithm operation.
* It inherits from the COperator class.
*/
class CLogarith : public COperator {
public:
/**
* @brief Constructor for CLogarith class.
* @param a The base of the logarithm operation.
* @param b The argument of the logarithm operation.
*/
CLogarith( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CLogarith class.
*/
CLogarith() = default;
/**
* @brief Destructor for CLogarith class.
*/
~CLogarith() = default;
/**
* @brief Evaluates the logarithm operation.
* @return A CRational pointer representing the result of the logarithm operation.
*/
virtual CRational *evaluate() override;
private:
};

35
src/include/modulo.hpp Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @brief The CModulo class represents a modulo operation.
* It inherits from the COperator class.
*/
class CModulo : public COperator {
public:
/**
* @brief Constructor for CModulo class.
* @param a The first argument of the modulo operation.
* @param b The second argument of the modulo operation.
*/
CModulo( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CModulo class.
*/
CModulo() = default;
/**
* @brief Destructor for CModulo class.
*/
~CModulo() = default;
/**
* @brief Evaluates the modulo operation.
* @return A CRational pointer representing the result of the modulo operation.
* @throws An exception if the modulo operation is applied to rationals.
*/
virtual CRational *evaluate() override;
private:
};

View File

@ -0,0 +1,36 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @brief The CMultiplication class represents a multiplication operation.
* It inherits from the COperator class.
*/
class CMultiplication : public COperator {
public:
/**
* @brief Constructor for CMultiplication class.
* @param a The first argument of the multiplication operation.
* @param b The second argument of the multiplication operation.
*/
CMultiplication( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for CMultiplication class.
*/
CMultiplication() = default;
/**
* @brief Destructor for CMultiplication class.
*/
~CMultiplication() = default;
/**
* @brief Evaluates the multiplication operation.
* @return A CRational pointer representing the result of the multiplication operation.
*/
virtual CRational *evaluate() override;
private:
};

43
src/include/operator.hpp Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "printable.hpp"
/**
* @brief The COperator class represents an abstract operator.
* It inherits from the CPrintable class.
*/
class COperator : public CPrintable {
public:
/**
* @brief Constructor for COperator class.
* @param a The first argument of the operator.
* @param b The second argument of the operator.
*/
COperator( CPrintable *a, CPrintable *b );
/**
* @brief Default constructor for COperator class.
*/
COperator() = default;
/**
* @brief Default destructor for COperator class.
*/
virtual ~COperator() = default;
/**
* @brief Evaluates the operands and then itself.
* @return A CRational pointer representing the result of the operator.
*/
virtual CRational *evaluate() = 0;
// protected:
/**
* @brief The first argument of the operator.
*/
std::unique_ptr<CPrintable> m_firstArgument;
/**
* @brief The second argument of the operator.
*/
std::unique_ptr<CPrintable> m_secondArgument;
};

217
src/include/parser.hpp Normal file
View File

@ -0,0 +1,217 @@
#pragma once
#include "../include/rational.hpp"
#include "../include/operator.hpp"
#include "../include/binsaver.hpp"
#include "../include/textsaver.hpp"
#include "../include/addition.hpp"
#include "../include/subtraction.hpp"
#include "../include/multiplication.hpp"
#include "../include/division.hpp"
#include "../include/modulo.hpp"
#include "../include/exponencial.hpp"
#include "../include/logarithm.hpp"
#include <map>
#include <cmath>
#include <algorithm>
#include <sstream>
#include <iterator>
#include <cctype>
//https://www.wikiwand.com/en/Lexical_analysis
//https://courses.fit.cvut.cz/BI-AAG/lectures/bi-aag-07-bezkontextove_gramatiky.pdf
/**
* @class CParser
* @brief Singleton class responsible for converting input into results.
*/
class CParser {
public:
/**
* @brief Default constructor for CParser class.
*/
CParser ();
/**
* @brief Destructor for CParser class. Clears the parser data.
*/
~CParser();
void reset();
/**
* @brief Clear the parser data.
*/
void clear();
void convert( const std::string &s );
/**
* @brief Divide a decimal string by 2 and return the next bit for binary conversion.
*
* @param s The decimal string to be divided.
* @return The next bit for binary conversion.
*/
static bool stringDivideDecimal( std::string &s );
bool m_continue;
std::string m_output;
private:
enum TOKEN { //by priority
START,
END,
NUMBER,
LABEL,
COMMAND, //.!
DEREFERENCE, // $
OPEN, // (
CLOSE, // )
LN, // &
EXP, // ^
DIVIDE, // /
MULTIPLY, //.*
MOD, // %
ADD, // +
SUBTRACT, // -
SAVE, // =
DOT,
WHITESPACE,
SYNTAX_ERROR
};
enum CMD {
QUIT_CMD,
LOAD_CMD,
SAVE_CMD,
PRECISSION_CMD,
HELP_CMD,
ROUND_CMD,
UNKNOWN_CMD,
NONE_CMD
};
CParser::CMD recognizeCommand( uint64_t commandPos ) const;
CParser::CMD parseCommand( uint64_t &startPos ) const;
bool executeCommand( CParser::CMD cmd );
/**
* @brief Tokenize the input string.
*
* This function analyzes the input string and breaks it down into lexemes,
* classifying each lexeme into different types such as whitespace, number, label,
* command, or operators.
*
* @return True if tokenization is successful, false otherwise.
*/
bool tokenize();
/**
* @brief Perform grammar preprocessing on the raw lexemes.
*
* This function strips whitespaces from the raw lexemes and processes numbers
* by creating rational numbers and handling various cases such as decimals,
* signs, and dereferencing variables.
*
* @return True if grammar preprocessing is successful, false otherwise.
*/
bool grammarPreprocess(); // add bracket based on priority
/**
* @brief Parses the input expression and constructs a syntax tree.
*
* This function recursively parses the input expression and constructs a syntax tree
* based on the processed lexemes. It handles invalid semantics and builds the
* tree of operators and operands.
*
* @param readIndex Index of opening bracket.
* @return A pointer to the root of the syntax tree.
*/
CPrintable *parse( uint64_t &readPos );
/**
* @brief Get the priority of an operator.
*
* @param t The operator token.
* @return The priority of the operator.
*/
uint64_t getOperatorPriority( TOKEN t ) const;
/**
* @brief Show the help command and return true.
*
* @return True.
*/
bool helpCommand();
/**
* @brief Reads a token from the input string.
*
* This function reads a token from the input string based on the provided index. It starts
* from the position indicated by the index and continues until it encounters a space character
* or reaches the end of the token.
*
* @param i The index of the token in the raw lexemes.
* @return The extracted token as a string.
*/
std::string readToken( uint64_t i ) const; // position in raw tokens
/**
* @brief Converts a CRational number to a string representation.
*
* This function converts a CRational number to its string representation. The string
* representation includes the numerator and, if the denominator is not equal to 1, the
* fraction part in the format "numerator / denominator".
*
* @param r A pointer to the CRational number to convert.
* @return The string representation of the CRational number.
*/
std::string numberToString( CRational *r ) const;
/**
* @brief Parse a number from the input string and convert it to a CRational object.
*
* @param i The index of the token in the raw lexems vector.
* @param isDecimal Flag indicating if the number is a decimal.
* @param hasSign Flag indicating if the number has a sign.
* @return A pointer to the parsed CRational object.
*/
CRational *parseNumber( uint64_t i, bool isDecimal, bool hasSign ) const;
/**
* @brief Creates a new instance of an operator based on the given token.
*
* This function creates a new instance of a COperator-based class based on the provided token.
* The specific type of the operator is determined by the token value.
*
* @param t The token representing the operator.
* @return A pointer to the newly created operator instance, or nullptr if the token is not a valid operator.
*/
COperator *spawnOperator( TOKEN t ) const;
/**
* @brief Recognize the type of a character token.
*
* @param c The character token.
* @return The type of the token.
*/
TOKEN recognize( char c ) const;
/**
* @brief Container for recognised lexems and their position in input.
*/
std::vector<std::pair<CParser::TOKEN, int64_t>> m_rawLexems;
/**
* @brief Container for relevant lexems and parsed numbers
*/
std::vector<std::pair<CParser::TOKEN, CRational *>> m_processedLexems;
/**
* @brief Copy of input string
*/
std::string m_input;
std::string m_saveInto;
std::unique_ptr<CPrintable> m_head;
std::map<std::string, CRational> m_variables;
std::unique_ptr<CSaver> m_saver;
};

27
src/include/printable.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <memory>
#include <string>
class CRational;
/**
* @brief The CPrintable class represents an abstract printable object.
*/
class CPrintable {
public:
/**
* @brief Default constructor for CPrintable class.
*/
CPrintable() = default;
/**
* @brief Default destructor for CPrintable class.
*/
virtual ~CPrintable() = default;
/**
* @brief Evaluates the printable object.
* @return A CRational pointer representing the evaluated result.
*/
virtual CRational *evaluate() = 0;
};

168
src/include/rational.hpp Normal file
View File

@ -0,0 +1,168 @@
#pragma once
#include "printable.hpp"
#include "bigint.hpp"
#include <cmath>
/**
* @brief The CRational class represents the rational number in fraction format.
* Responsible for controlling BigInts
* It inherits from the CPrintable class.
*/
class CRational : public CPrintable {
public:
/**
* @brief Constructs a CRational object with a numerator and denominator.
*
* @param num Pointer to the numerator as a CBigInt object.
* @param den Pointer to the denominator as a CBigInt object.
*/
CRational( CBigInt *num, CBigInt *den);
/**
* @brief Constructs a CRational object with a numerator and a default denominator of 1.
*
* @param num Pointer to the numerator as a CBigInt object.
*/
CRational( CBigInt *num );
/**
* @brief Constructs a CRational object as a copy of another CRational object.
*
* @param parent The CRational object to be copied.
*/
CRational( const CRational &parent );
/**
* @brief Destroys the CRational object.
*/
virtual ~CRational();
/**
* @brief Converts the CRational object to a string representation.
*/
virtual CRational *evaluate() override;
/**
* @brief Converts the CRational object to a string representation.
*
*/
std::string toString();
/**
* @brief Simplifies the CRational object by dividing the numerator and denominator by their GCD.
* Adjusts the signs of the numerator and denominator accordingly to just nominator.
*/
void simplify();
/**
* @brief Adds another CRational object to the current CRational object.
*
* @param oth Pointer to the CRational object to be added.
* @return A new CRational object representing the sum of the two CRational objects.
*/
CRational *add( CRational *oth );
/**
* @brief Subtracts another CRational object from the current CRational object.
*
* @param oth Pointer to the CRational object to be subtracted.
* @return A new CRational object representing the difference between the two CRational objects.
*/
CRational *sub( CRational *oth );
/**
* @brief Multiplies the current CRational object with another CRational object.
*
* @param oth Pointer to the CRational object to be multiplied.
* @return A new CRational object representing the product of the two CRational objects.
*/
CRational *mul( CRational *oth );
/**
* @brief Divides the current CRational object by another CRational object.
*
* @param oth Pointer to the CRational object to be divided by.
* @return A new CRational object representing the division of the two CRational objects.
*/
CRational *div( CRational *oth );
/**
* @brief Raises the current CRational object to a specified power.
*
* @param pow The exponent to raise the CRational object to.
* @return A new CRational object representing the result of the exponentiation.
*/
CRational *power( uint64_t pow );
/**
* @brief Computes a term of the CRational object raised to the specified power.
* @param pow The power to raise the CRational object to.
* @return A new CRational object representing the term raised to the specified power.
*/
CRational *term( uint64_t pow );
/**
* @brief Computes the natural logarithm of the CRational object.
* @note Throws an exception if the CRational object is non-positive.
* @return A new CRational object representing the natural logarithm of the CRational object.
*/
CRational *ln();
/**
* @brief Computes the exponential function of the CRational object.
* @return A new CRational object representing the exponential function of the CRational object.
*/
CRational *exp();
std::string round( uint64_t decimal );
/**
* @brief Sets the same denominator for the current CRational object and another CRational object.
* @param oth The other CRational object to set the same denominator with.
* @return `true` if the operation is successful, `false` otherwise.
*/
bool setSameDenominator( CRational *oth );
/**
* @brief Checks if the CRational number is negative.
* @return `true` if the number is negative, `false` otherwise.
*/
bool isNegative() const;
/**
* @brief Negates the CRational number by changing its sign.
*/
void negate();
/**
* @brief Returns the debug dump of the CRational number as a double.
* @return The debug dump value of the CRational number as a double.
*/
double debugDump();
// private:
/**
* @brief Calculates the factorial of an unsigned integer.
* @param i The unsigned integer to calculate the factorial for.
* @return The factorial of the unsigned integer as a signed 64-bit integer.
*/
inline static int64_t intfactorial( uint64_t i );
/**
* @brief The precision used for the term calculations.
* It specifies the number of terms used in certain calculations.
*/
static uint64_t termPrecision;
/**
* @brief The numerator of the CRational number.
* It is stored as a unique pointer to a CBigInt object.
*/
std::unique_ptr<CBigInt> m_numerator;
/**
* @brief The denominator of the CRational number.
* It is stored as a unique pointer to a CBigInt object.
*/
std::unique_ptr<CBigInt> m_denominator;
};

20
src/include/saver.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "../include/rational.hpp"
#include <string>
#include <fstream>
#include <map>
#include <functional>
class CSaver {
protected:
std::string fileName;
std::map<std::string, CRational> &m_variables;
public:
CSaver(std::map<std::string, CRational> &variablesMap);
~CSaver() = default;
std::string m_log;
virtual bool save( const std::string &s ) = 0;
virtual bool load( const std::string &s ) = 0;
};

View File

@ -0,0 +1,34 @@
#pragma once
#include "rational.hpp"
#include "operator.hpp"
/**
* @brief The CSubtraction class represents the subtraction operator.
*/
class CSubtraction : public COperator {
public:
/**
* @brief Constructor for CSubtraction class.
* @param a The first argument of the subtraction.
* @param b The second argument of the subtraction.
*/
CSubtraction(CPrintable *a, CPrintable *b);
/**
* @brief Default constructor for CSubtraction class.
*/
CSubtraction() = default;
/**
* @brief Default destructor for CSubtraction class.
*/
~CSubtraction() = default;
/**
* @brief Evaluates the subtraction operation.
* @return A CRational pointer representing the evaluated result.
*/
virtual CRational *evaluate() override;
private:
};

37
src/include/textsaver.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include "../include/saver.hpp"
#include "../include/parser.hpp"
/**
* @class CTextsaver
* @brief A class for saving and loading variables in text format.
*/
class CTextsaver : public CSaver {
private:
CRational *parseNumber( const std::string &s ) const;
public:
/**
* @brief Constructor for CTextsaver.
* @param variablesMap A reference to the map of variables to be saved.
*/
CTextsaver(std::map<std::string, CRational> &variablesMap);
/**
* @brief Destructor for CTextsaver.
*/
~CTextsaver();
/**
* @brief Saves the variables to a file in text format.
* @param fileName The name of the file to save the variables.
* @return True if the variables were successfully saved, false otherwise.
*/
virtual bool save( const std::string &fileName ) override;
/**
* @brief Loads variables from a file in text format.
* @param fileName The name of the file to load the variables from.
* @return True if the variables were successfully loaded, false otherwise.
*/
virtual bool load( const std::string &fileName ) override;
};