Summary Files Commits Branches @commit @diff @stats
path: root / Year 1 / Object-oriented Programming / Solutions Sem. 05 / ex2-interval.cpp
blob: 4b1b7ae39a353199e6eafdfeb05750c3b8aa5eb8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// 
// ++============++
// || Оценка: Да ||
// ++============++
//
// +-----------+
// | !Бележки! |
// +-----------+
//
// > Ангел Димитриев - Fri, 25 Mar 2022, 19:45
// Супер решение.
// 
// Единственотата по-сериозна забележка, която имам е че изчисляваш counts при извикването на set.-a.
// 
// Не винаги е добре това, защото може временно да ти потрябва огромен интервал, а ти ще му сметнеш всичките характеристики, дори и да не са поискани. По-правилно би било да ги смяташ в palindromesCount/primesCount/nonRepeatingDigitsCount и да имаш променлива isModified, за да знаеш дали трябва да ги смяташ
//

#include <iostream> // cin, cout
#include <math.h>   // sqrt

/* Generic functions */

bool isPowerOf2(int x)
{
	return x > 0 &&            // No (normal) power of 2 creates negatives or 0
		   (x & (x - 1)) == 0; // Thanks https://stackoverflow.com/a/600306/12036073, original implementation was a lot less efficient
}

bool isPrime(int num) // Forked from my own implementation - https://gitlab.com/Syndamia/algorithms/-/blob/master/C/IsPrime.c
{
	if (num < 0) num *= -1; // This is required, since we calculate sqrt(num)

	if (num == 2 || num == 3 || num == 5 || num == 7 || num == 11 || num == 13)
		return true;

	// A lot of numbers are divisible by these ones, so we're going to do a bit of simple optimization
	if (num == 0 || num == 1 || num % 2 == 0 || num % 3 == 0 || num % 5 == 0 || num % 7 == 0 || num % 11 == 0 || num % 13 == 0)
		return false;

	// All primes follow the formula 6n +- 1 (where n >= 1). Since we've already checked up to 13, we're going to
	// start with 18 - 1 = 17. Also using root of number to eliminate a lot of checks.
	// Primary source: https://en.wikipedia.org/wiki/Primality_test#Simple_methods
	for (int i = 18, root = sqrt(num); i - 1 <= root; i += 6)
	{
		if (num % (num - 1) == 0 || num % (num + 1) == 0)
			return false;
	}
	return true;
}

bool isPalindrome(int num)
{
	if (num < 0)
		return false;

	int buff = 0, tNum = num;
	while (tNum != 0)
	{
		buff = buff * 10 + (tNum % 10);
		tNum /= 10;
	}
	return buff == num;
}

bool hasRepeatingDigits(int num)
{
	if (num < 0) num *= -1; // if num is negative, num % 10 will also be negative, which mustn't happen

	bool digitOccur[] = { false, false, false, false, false, false, false, false, false, false };
	while (num != 0)
	{
		if (digitOccur[num % 10])
			return true;
		digitOccur[num % 10] = true;
		num /= 10;
	}
	return false;
}

bool hasNotRepeatingDigits(int num)
{
	return !hasRepeatingDigits(num);
}

/* Class Interval */

class Interval
{
	int a;
	int b;

	// "Caches" for the heavy operations
	int primeNumbersCount;
	int palindromeNumbersCount;
	int nonRepeatingDigitsNumbersCount;
	bool edgesPowersOf2;

	int counter(bool (comparator)(int)) const
	{
		int count = 0;
		for (int i = a; i <= b; i++)
		{
			if (comparator(i))
				count++;
		}
		return count;
	}

	void calculateCounts()
	{
		this->primeNumbersCount = counter(isPrime);
		this->palindromeNumbersCount = counter(isPalindrome);
		this->nonRepeatingDigitsNumbersCount = counter(hasNotRepeatingDigits);
	}

public:
	Interval();
	Interval(int a, int b);

	int get_a() const;
	void set_a(int a);

	int get_b() const;
	void set_b(int b);

	int getLen() const;
	bool hasInInterval(int x) const;

	// The operations for each one are "heavy" so we calculated them on interval
	// edges set and return the "cached" value
	int primesCount() const;
	int palindromesCount() const;
	int nonRepeatingDigitsCount() const;
	bool edgesArePowersOf2() const;

	Interval intersect(const Interval& inter) const;
	bool isSupInterval(const Interval& inter) const;
};

Interval::Interval() : Interval(0, 0)
{}
Interval::Interval(int a, int b)
{
	if (a > b) a = b = 0;

	this->edgesPowersOf2 = isPowerOf2(a) && isPowerOf2(b);
	this->a = a; // We can't use setter here, since it will try to calculate the counts, and we don't want to do that yet.
	this->set_b(b);
}

int Interval::get_a() const
{
	return this->a;
}
void Interval::set_a(int a)
{
	this->a = a;
	if (a > b) this->b = a; // Note: behaviour not defined in task

	this->calculateCounts();
	this->edgesPowersOf2 &= isPowerOf2(a);
}

int Interval::get_b() const
{
	return this->b;
}
void Interval::set_b(int b)
{
	this->b = b;
	if (b < a) this->a = b; // Note: behaviour not defined in task

	this->calculateCounts();
	this->edgesPowersOf2 &= isPowerOf2(b);
}

int Interval::getLen() const
{
	return this->get_a() - this->get_b();
}
bool Interval::hasInInterval(int x) const
{
	return this->get_a() <= x && x <= this->get_b();
}

int Interval::primesCount() const
{
	return this->primeNumbersCount;
}
int Interval::palindromesCount() const
{
	return this->palindromeNumbersCount;
}
int Interval::nonRepeatingDigitsCount() const
{
	return this->nonRepeatingDigitsNumbersCount;
}
bool Interval::edgesArePowersOf2() const
{
	return this->edgesPowersOf2;
}

Interval Interval::intersect(const Interval& inter) const
{
	// [ ] { }  or  { } [ ]  where [] is this, {} is inter
	if (this->b < inter.get_a() || this->a > inter.get_b())
		return Interval();

	// [ { ] }   where [] is this, {} is inter
	if (this->a <= inter.get_a())
		return Interval(inter.a, this->b);
	// { [ } ]   where [] is this, {} is inter
	else
		return Interval(this->a, inter.b);
}
bool Interval::isSupInterval(const Interval& inter) const
{
	return this->a >= inter.a && inter.b <= this->b;
}

/* Main */

void printInter(const Interval& inter)
{
	std::cout << "[" << inter.get_a() << "," << inter.get_b() << "]" << std::endl;
}

int main()
{
	/* Стойчо ни беше направил документ за unit тестове, но имайки предвид, че формално ги няма в задачата, не съм правил */

	Interval t1(3, 10), t2(4, 14), t3;
	printInter(t1);
	printInter(t2);
	printInter(t3);

	Interval intT1T2 = t1.intersect(t2);
	printInter(intT1T2); // [4,10]
	
	std::cout << t2.isSupInterval(intT1T2) << std::endl; // true
	std::cout << intT1T2.primesCount() << std::endl; // 2 (5 and 7)
	std::cout << t2.hasInInterval(14) << " " << t2.hasInInterval(15) << std::endl; // "1 0"
	
	Interval t4(0, 100), t5(-100, 0);
	std::cout << "Pal: " << t4.palindromesCount() << " " << t5.palindromesCount() << std::endl; // "19 1" (negatives arent palindromes)
	std::cout << "Primes: " << t4.primesCount() << " " << t5.primesCount() << std::endl; // "25 25"
	std::cout << "Non repeat: " << t4.nonRepeatingDigitsCount() << " " << t5.nonRepeatingDigitsCount() << std::endl; // "91 91"
	
	std::cout << t5.hasInInterval(-99) << t5.hasInInterval(0) << t5.hasInInterval(1) << std::endl; // "110"

	Interval t6(256, 1024), t7(-1024, -256);
	std::cout << "Two Power Ends: " << t4.edgesArePowersOf2() << " " << t5.edgesArePowersOf2() << " " << t6.edgesArePowersOf2() << " " << t7.edgesArePowersOf2() << std::endl; // "0 0 1 0"
}