Scanning a cipher text document, guessing the key, and calculating distribution - c++

My project is breaking a Vignere cipher. However, I've gotten to this last part and now I'm stuck. I don't know if I'm misunderstanding the math or the logic of the program I'm writing, but basically I should be getting different calcDistribution() values for each letter at each position of the key. As it stands, this program returns only 16 unique values grouped together by key position. Ultimately the program should be able to pick the highest value calcDistribution() letter at that key position.
Here is the portion of my code I'm sure is the source of the problem:
//First, we need to pick a character to XOR against the cipherText. reuse char ci, string byte
char kg;
string keyBreakGuess = "";
string ranOuttaNames1 = "";
string ranOuttaNames2 = "";
float charDistribution[128-65][MAXKEY];
for(int d = 0; d < keyGuess; d++){ //This loop will search each spot up to the length of the key
for(ci = 65; ci < 123; ci++){ //This loop will look at every possible key character
cipherText.seekg(0, ios::beg);
for(int e = 0; e < 500 && !cipherText.eof(); e++){ //This loop will handle the sampling
cipherText.seekg((LENGTH * keyGuess * e) + (LENGTH * d), ios::beg); //f(x) = 8*(c)*(x) + 8*(b)
if(cipherText.eof())
break;
for(int f = 0; f < LENGTH && !cipherText.eof(); f++){
cipherText >> kg;
byte += kg;
}
ranOuttaNames2 = ci;
ranOuttaNames1 = string2Binary(ranOuttaNames2);
ranOuttaNames2 = "";
for(int u = 0; u < LENGTH; u++){
if(byte[u] == ranOuttaNames1[u])
ranOuttaNames2 += '0';
else
ranOuttaNames2 += '1';
}
kg = binary2Char(ranOuttaNames2);
if((int)kg >= 32 && (int)kg < 128)
ascii[(int)kg]++;
byte = "";
ranOuttaNames1 = "";
}
//This is where we will calcDist for each char and pick the max character for that piece of the key
charDistribution[(int)ci-65][d] = calcDistribution(ascii, ASCIIBYTELENGTH);
cout << ci << " " << (d+1) << ": " << charDistribution[(int)ci-65][d] << endl;
for(int r = 0; r < ASCIIBYTELENGTH; r++){
ascii[r] = 0;
}
}
}
maxD = 0;
for(int s = 0; s < keyGuess; s++){
for(int q = 65; q < 128; q++){
if(maxD < charDistribution[q-65][s]){
maxD = charDistribution[q-65][s];
kg = q;
}
}
keyBreakGuess += kg;
}
cout << "My guess for the full key: " << keyBreakGuess << endl; //This should have the full key
Here is the calcDistribution function:
//This function will calculate the distribution of characters of an array
double calcDistribution(int arr[], int size)
{
double p = 0.000;
double sum = 0;
//calculate sum of all indexes
for(int t = 0; t < size; t++){
sum += arr[t];
}
//divide each point point in the index by the sum of all indexes
for(int c = 0; c < size; c++){
try{
p += pow((arr[c] / sum), 2);
} catch (int e) {
cerr << "Sum is 0, error occurred.";
exit(1);
}
}
return p;
}
Any pointers in the right direction would be much appreciated.

I solved this problem by adding a case to break if a character wasn't printable or a number or letter. This kept values from repeating because it wouldn't even calculate a value with special characters. While this probably wouldn't hold true for every possible document you would want to encrypt with a Vignere cipher, it did work for the document I was given for this project.

Related

When you have a number which has over 1 digit in a created board, what should you do prevent it from affecting the shape of the board

I am new to C++. And I am trying to implement a 2048 game based on C++ for practice. And I am trying to create a board first.
The problem I have is that when the number is become a two digit numbers it will affect the shape of the wall like this:
Here is the test code:
#include<iostream>
using namespace std;
int main()
{
string gameboard[24][25];
int p = 24 / 4;
for (int i = 0; i < 24;i++)
{
for (int j = 0; j < 25; j++)
{
if (i == 0 || j == 0 || i == 23|| j == 24 || (i % p) == 0 || (j % p) == 0)
{
gameboard[i][j] = '*';
}
else
{
gameboard[i][j] = " ";
}
}
}
gameboard[3][15] = "128";
for (int i = 0; i < 24; ++i)
{
for (int j = 0; j < 25; ++j)
{
cout << gameboard[i][j] << " ";
}
cout << endl;
}
}
So I put a string number "128", it will break the wall. What should I do to prevent this?
It looks like you actually want a char gameboard[24][25]; rather than a 2d array of strings. When each cell of the board is exactly 1 character wide then you just need to print it character by character to get expected output.
If you do that you need to place individual digits rather than the complete number as string:
gameboard[3][13] = '1';
gameboard[3][14] = '2';
gameboard[3][15] = '8';
I recommend to wrap this inside a function:
void place_number(int number, int row, int col,char gameboard[24][25]) {
int x = row * a + b;
int y = col * c + d;
std::string s = std::to_string(number);
for (int i=0; i<s.size(); ++i) {
gameboard[x][y+i] = s[i];
}
}
With coefficients a,b,c and d choosen such that the numbers end up in the right positions.
Doing such formatted printing can become cumbersome rather fast. If you need more sophisticated control I suggest to use a library for that, for example ncurses.
from the 25 places reserved for the width, you should consider to substract the number of placed occupied for the number you print in the cell..
you can turn the number to std::string and take its length
or mathematically get the number of digits using some existing functions like log

Print histogram with "*" representing relative frequencies in C++

I'm trying to convert an histogram with absolute values to an histogram showing the relative frequency of letters in a string, written by the user. The letters frequency should be represented by *. So, if the letter "A" is 1% of a string, there should be two *. 1% = two *.
When trying to calculate the frequency, the output is zero. I don't really understand why.
I've tried to search the internet, but I can't really find something that helps me. I guess I'm stuck, both in my head and coding.
This is the code for my three functions:
void berakna_histogram_abs(const string inm, int arr[ANTAL_BOKSTAVER]){
int i, j = 0;
while (inm[i] != '\0'){
if (inm[i] >= 'a' && inm[i] <= 'z'){
j = inm[i] - 'a';
++arr[j];
}
if (inm[i] >= 'A' && inm[i] <= 'Z'){
j = inm[i] - 'A';
++arr[j];
}
i++;
}
}
void abs_till_rel(int arr[ANTAL_BOKSTAVER], int (&ree)[ANTAL_BOKSTAVER]){
for(int i = 0; i < ANTAL_BOKSTAVER; i++) {
ree[i] = arr[i] / 26;
printf("%c %lf \n", i + 'a', ree[i]);
}
for (int x = 0; x < ANTAL_BOKSTAVER; x++){
}
}
void plotta_histogram_rel(int (&ree)[ANTAL_BOKSTAVER]){
cout << "Frekvensen av bokstäver i texten är: " << endl;
for (int i = 0; i < ANTAL_BOKSTAVER; i++){
cout << char(i + 'a') << " : " << ree[i] << endl;
}
}
I'm not allowed to do any calculations in the third function, that is only for writing the histogram. The whole program is pretty big, if you'd like, I'll provide all the code.
Any help forward is much appreciated.
Thanks!
So, you have some errors that need to be corrected. You do not pass the array as reference in the first function. You pass it by value. So all modifications that will be done to that arra in the first function berakna_histogram_abs will not be visible to the outside world.
You need to do the same as in you other functions -->
void berakna_histogram_abs(const std::string inm, int (&arr)[ANTAL_BOKSTAVER]) {
By the way, the string should also be passed as reference. Anyway. Not so important.
Next problem. You forgot to initialize variable i to 0 in your first function. So, it will have some random value and the program will fail, becuase you access some random index with inm[i].
In your calculation function abs_till_rel you are using the wrong formular. You need to multiply with 100 to get integer results between 0 and 100 for the percentage. And you divide by 26, which makes the result relative to the amount of the number of letters in an alphabet. My guess is that you want to have the relations to the number of letters in the string.
For that, you first need to calculate all counts of letters to get the overall count. Like for example with:
int sum = 0;
for (int i = 0; i < ANTAL_BOKSTAVER; i++) sum += arr[i];
and then divide by this sum, like so:
ree[i] = (arr[i] * 100) / sum;
And to output the histogram, you can simply build a string with stars, using the std::string constructor number 2
Your updated program would look like this:
#include <iostream>
#include <string>
#include <stdio.h>
constexpr int ANTAL_BOKSTAVER = 26;
void berakna_histogram_abs(const std::string inm, int (&arr)[ANTAL_BOKSTAVER]) {
int i = 0, j = 0;
while (inm[i] != '\0') {
if (inm[i] >= 'a' && inm[i] <= 'z') {
j = inm[i] - 'a';
++arr[j];
}
if (inm[i] >= 'A' && inm[i] <= 'Z') {
j = inm[i] - 'A';
++arr[j];
}
i++;
}
}
void abs_till_rel(int arr[ANTAL_BOKSTAVER], int(&ree)[ANTAL_BOKSTAVER]) {
int sum = 0;
for (int i = 0; i < ANTAL_BOKSTAVER; i++) sum += arr[i];
if (sum >0) for (int i = 0; i < ANTAL_BOKSTAVER; i++) {
ree[i] = (arr[i] * 100) / sum;
std::cout << (char)(i + 'a') << '\t' << ree[i] << '\n';
}
for (int x = 0; x < ANTAL_BOKSTAVER; x++) {
}
}
void plotta_histogram_rel(int(&ree)[ANTAL_BOKSTAVER]) {
std::cout << "Frekvensen av bokstäver i texten är: " << std::endl;
for (int i = 0; i < ANTAL_BOKSTAVER; i++) {
std::cout << char(i + 'a') << " : " << std::string(ree[i]*2,'*') << std::endl;
}
}
int main() {
std::string test{"The quick brown fox jumps over the lazy dog"};
int frequencyArray[ANTAL_BOKSTAVER] = {};
int frequencyInPercent[ANTAL_BOKSTAVER] = {};
berakna_histogram_abs(test, frequencyArray);
abs_till_rel(frequencyArray, frequencyInPercent);
plotta_histogram_rel(frequencyInPercent);
}
But in C++, we would use the standard approach and write the followin:
#include <iostream>
#include <map>
#include <string>
#include <cctype>
// Our test string. This is a standard test string that contains all letters
std::string test{ "The quick brown fox jumps over the lazy dog" };
int main() {
// Count all letters
std::map<char, size_t> counter{};
for (const auto& c : test) if (std::isalpha(c)) counter[std::tolower(c)]++;
// Show histogram
for (const auto& [letter, count] : counter)
std::cout << letter << '\t' << std::string((count * 200) / test.size(), '*') << '\n';
return 0;
}
void abs_till_rel(int arr[ANTAL_BOKSTAVER], int langd, double frekArr[ANTAL_BOKSTAVER]){
//Function to calculate the relative frequency of letters in a string.
for (int i = 0; i < ANTAL_BOKSTAVER; i++){
frekArr[i] = arr[i]; //Writes over the input from the user to a new array.
frekArr[i] = frekArr[i] * 200 / langd; //Calculates the relative frequency
//*200 since 1% should be represented by two (2) *.
}
}
void plotta_histogram_rel(double frekArr[ANTAL_BOKSTAVER], int langd){
int j = 0;
for (int i = 0; i < ANTAL_BOKSTAVER; i++){
cout << char(i + 'A') << " : ";
//Creates a histograg, horizontal, with A-Z.
if(frekArr[i] > 0){
for ( j = 0; j < frekArr[i]; j++){
cout << "*";
}
cout << endl;
}
//If the index in frekArr is NOT empty, loop through the index [i] times and
//write double the amount of *.
else {
cout << " " << endl;
//Else, leave it empty.
}
}
}
I've solved the issue. See the working code above.

Count letters in string

Can someone help me.
for example I input
for x RandomName for y and
it needs to count each y letter in x word, and output should be.
a: 2
n: 1
d: 1
void counter(string x, string y)
{
int signs[100];
int amount = 0;
for (int i = 0; i < y.length(); i++)
{
signs[i] = y[i];
for (int j = 0; j < x.length(); j++)
{
if (x[i] == y[i])
{
amount++;
}
}
cout << y[i] << ":" << amount << endl;
}
}
There are several errors in you code:
You always compare x[i] to y[i] ignoring the j index completely, which means you will never count a letter that is not in the same place in both strings.
You have a signs array you assign values to, but never use it. Also, it has the size 100, but for what?
You never reset the amount variable after the internal loop, which means it will not count letter individually.
You have the right idea - two loops, one iterates over all letters in y the other over all letters in x.
Fix the indexes in the comparison, reset the amount to 0 after you print it, and get rid of signs, and you code should work.
You incrise amount every time you find a lettre that match the input, but the amount is the same for every letter taken in parameters. To fix it just create an array of amount for every letter in parameters.
#define max(a, b) ((a) > (b) ? (a) : (b))
#define pass ((void)0)
std::string x = "RandomName";
std::string y = "and";
int maxSize = max(x.size(), y.size());
char* letterList = (char*)_alloca(maxSize);
int used = 0;
for (int l1 = 0; l1 < y.size(); l1++) {
char letter = y.at(l1);
int count = 0;
for (int i = 0; i < maxSize; i++) {
if (letter == letterList[i]) {
continue;//Do not know if that is the right spelling
}
}
letterList[used] = letter;
used++;
for (int l2 = 0; l2 < x.size(); l2++) {
if (letter == x.at(l2)) { count++; }
}
std::cout << letter << " = " << count << std::endl;
}
Should do what you want

Sum of two arrays, carry over operation C++

Beginner here, and I'm stuck. The main program is provided to us, and we're supposed to write 3 functions. readBig(), addBig(), and printBig(). I'm stuck on the addBig() function. It's supposed to sum two arrays, and perform the carry operation. I cannot figure out where I'm going wrong. The carry operation is working out for me.
Any help/direction is appreciated.
#include <iostream>
using namespace std;
//This program will test three functions capable of reading, adding,
//and printing 100-digit numbers.
// Do not change these function prototypes:
void readBig(int[]);
void printBig(int[]);
void addBig(int[], int[], int[]);
// This constant should be 100 when the program is finished.
const int MAX_DIGITS = 100;
//There should be no changes made to the main program when you turn it
in.
int main(){
// Declare the three numbers, the first, second and the sum:
int num1[MAX_DIGITS], num2[MAX_DIGITS], sum[MAX_DIGITS];
bool done = false;
char response;
while (not done)
{
cout << "Please enter a number up to "<<MAX_DIGITS<< " digits: ";
readBig(num1);
cout << "Please enter a number up to "<<MAX_DIGITS<< " digits: ";
readBig(num2);
addBig(num1, num2, sum);
printBig(num1);
cout << "\n+\n";
printBig(num2);
cout << "\n=\n";
printBig(sum);
cout << "\n";
cout <<"test again?";
cin>>response;
cin.ignore(900,'\n');
done = toupper(response)!='Y';
}
return 0;
}
//ReadBig will read a number as a string,
//It then converts each element of the string to an integer and stores
it in an integer array.
//Finally, it reverses the elements of the array so that the ones digit
is in element zero,
//the tens digit is in element 1, the hundreds digit is in element 2,
etc.
void readBig(int num[])
{
for(int i = 0; i < MAX_DIGITS; i++){
num[i] = 0;
}
string numStr;
getline(cin,numStr);
string temp;
//store into array
for (int i = 0; i < numStr.length(); i++){
temp = numStr.at(i);
num[i] = stoi(temp);
}
int arrayLength = MAX_DIGITS;
int temp2;
for (int i = 0; i < (arrayLength/2); i++){
temp2 = num[i];
num[i] = num[(arrayLength - 1) - i];
num[(arrayLength - 1) - i] = temp2;
}
}
//AddBig adds the corresponding digits of the first two arrays and
stores the answer in the third.
//In a second loop, it performs the carry operation.
void addBig(int num1[], int num2[], int sum[])
{
for (int i = 0; i < MAX_DIGITS; i++){
sum[i] = num1[i] + num2[i];
if (sum[i] > 9){
sum[i] = sum[i] - 10;
sum[i+1] = sum[i+1] + 10;
}
}
}
//PrintBig uses a while loop to skip leading zeros and then uses a for
loop to print the number.
void printBig(int array[])
{
int i = 0;
while (array[i] == 0){
i++;
}
for (int j = i; j < MAX_DIGITS;j++){
cout << array[j] << endl;
}
}
Looks like readBig function isn't correct, it stores least significiant digit into num[numStr.length()-1], after reversing it became num[MAX_DIGITS -1 - ( numStr.length()-1], but addNum assumes last digit is num[0].
Correct variant:
void readBig(int num[])
{
//clear num, read numStr...
//store into array
int count = 0;
for (int i = numStr.length()-1; i >= 0; --i){
temp = numStr.at(i);
num[count++] = stoi(temp);
}
|
So this
sum[i] = sum[i] - 10;
sum[i+1] = sum[i+1] + 10;
should most likely be this
sum[i] = sum[i] - 10;
sum[i+1] = sum[i+1] + 1;
Since its the next decimal place it shouldnt be incremented by 10
Also when you get to the last cell in your array
sum[i+1] = sum[i+1] + 1;
this will be out of bounds, so depending on the requirments you will want to change this

Why is it not a perfect triangle C++?

I just finished going through this pascal triangle project and the the output is not a proper triangle , anyone have any idea why this is happening , the spacing looks about right its just the alignment that's way off
#include <string>
#include <iostream>
using namespace std;
int factorial(int numPar); //the factorial function will recieve a number, and return that number's factorial
int pascalNum(int rowPar, int elementPar);
int main(){
//declarations
string strOutput = "";
int userNum;
int rowCounter = 0;
int elementCounter = 0;
int columnsPerRow = 1;
int spaces;
int initialSpaces;
int counter = 0;
//get user input
cout << "Enter an integer from 1 to 10: ";
cin >> userNum;
while (userNum > 10 || userNum < 1){
cout << "Invalid entry: " << endl;
cin >> userNum;
}
initialSpaces = userNum + 4; //needed to make the triangle an isoscoles, and keep it away from left
//calculations
while ((rowCounter + 1) <= userNum){
for (counter = initialSpaces; counter > 0; counter--){
strOutput = strOutput + " ";
}
while (elementCounter < columnsPerRow){
strOutput = strOutput + to_string(pascalNum(rowCounter, elementCounter));
if (pascalNum(rowCounter, elementCounter) < 10){
spaces = 3;
}
else if (pascalNum(rowCounter, elementCounter) < 100){
spaces = 2;
}
else if (pascalNum(rowCounter, elementCounter) < 1000){
spaces = 1;
}
while (spaces > 0){
strOutput = strOutput + " ";
spaces = spaces - 1;
}
elementCounter = elementCounter + 1;
}
cout << strOutput << endl;
columnsPerRow = columnsPerRow + 1;
elementCounter = 0;
rowCounter = rowCounter + 1;
//initialSpaces--; //this makes there be less and less space until the triangle starts
strOutput = "";
}
system("pause>nul");
return 0;
}
int factorial(int numPar) {
//declarations
int counter = 1;
int numResult = 1;
int initial = numPar;
if (numPar > 1){
while (counter <= numPar){
numResult = numResult * counter;
counter++;
}
return numResult;
}
else
return 1;
}
int pascalNum(int rowPar, int elementPar){
int answer;
answer = factorial(rowPar) / (factorial(elementPar) * factorial(rowPar - elementPar));
return answer;
}
Well, you're going about the layout wrong. First you've got to work out your number cell layout. You've got the start of code that does this, but you put all the extra spaces on the right.
What you probably want is something that's centered. Which means that, for a maximum of three digits, you'll want the one padding space on the left, and for one digit, you want a space on either side. And overall you want one space between cells. So your padding code changes to:
for(int elementCounter = 0; elementCounter < columnsPerRow;
elementCounter = elementCounter + 1){
// Don't Repeat Yourself
int num = pascalNum(rowCounter, elementCounter);
string numStr = to_string(num);
if (num < 10){
numStr = numStr + " ";
}
if (num < 100){
numStr = string(" ") + numStr;
}
strOutput += numStr;
}
Now that you know your code has cells with three possible digits and one padding space, draw out what it should look like for a small test case:
###
### ###
### ### ###
Now look at the pattern, and lo, there are two indent spaces on the left per row, or 2 * (9 - r) total, where r goes from 0 to 9. Fix your outer (row) loop accordingly and get rid of the initialSpaces stuff:
while ((rowCounter + 1) <= userNum){
for (counter = 2 * (9 - rowCounter); counter > 0; counter--){
strOutput = strOutput + " ";
}
// ... continue as above ...
And that should fix things. Moral of the story:
Don't forget to draw up your expected output format.
Use graph paper if you have to.