C program to crack passwords












3














I have implemented a program in C to crack passwords by generating all possible combinations of words ([A-Z][a-z]) up to the length of 5. While the program works, I would like to receive comments on the efficiency of the algorithm and other design decisions that would improve the code. The exercise is part of the course CS50 by Harvard.



I timed the program using unix's time and the time the program took to print all the combinations was



**real**    14m39.433s; 
**user** 0m10.040s;
**sys** 0m36.356s.


CS50.h is a library developed for the course as training wheels for students. String (char*) and Bool are types defined in this library.



#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <cs50.h>

int main(int argc, char *argv)
{
if(argc != 2)
{
printf("Usage: ./crack hashn");
return 1;
}

string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char password[6] = "";
string hash = argv[1];
char salt[3];
memcpy(salt, hash, 2);
salt[2] = '';

bool flag = false;
int alphabet_len = 52;

for(int i = 0; i < alphabet_len; i++)
{
password[0] = alphabet[i];
password[1] = password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int j = 0; j < alphabet_len; j++)
{
password[1] = alphabet[j];
password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int k = 0; k < alphabet_len; k++)
{
password[2] = alphabet[k];
password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int l = 0; l < alphabet_len; l++)
{
password[3] = alphabet[l];
password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int m = 0; m < alphabet_len; m++)
{
password[4] = alphabet[m];
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}

if(flag)
printf("Password: %sn", password);
else
printf("Password not foundn");
}









share|improve this question









New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • Post the declaration of crypt().
    – chux
    3 hours ago










  • @chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
    – Reinderien
    3 hours ago










  • Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
    – chux
    3 hours ago
















3














I have implemented a program in C to crack passwords by generating all possible combinations of words ([A-Z][a-z]) up to the length of 5. While the program works, I would like to receive comments on the efficiency of the algorithm and other design decisions that would improve the code. The exercise is part of the course CS50 by Harvard.



I timed the program using unix's time and the time the program took to print all the combinations was



**real**    14m39.433s; 
**user** 0m10.040s;
**sys** 0m36.356s.


CS50.h is a library developed for the course as training wheels for students. String (char*) and Bool are types defined in this library.



#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <cs50.h>

int main(int argc, char *argv)
{
if(argc != 2)
{
printf("Usage: ./crack hashn");
return 1;
}

string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char password[6] = "";
string hash = argv[1];
char salt[3];
memcpy(salt, hash, 2);
salt[2] = '';

bool flag = false;
int alphabet_len = 52;

for(int i = 0; i < alphabet_len; i++)
{
password[0] = alphabet[i];
password[1] = password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int j = 0; j < alphabet_len; j++)
{
password[1] = alphabet[j];
password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int k = 0; k < alphabet_len; k++)
{
password[2] = alphabet[k];
password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int l = 0; l < alphabet_len; l++)
{
password[3] = alphabet[l];
password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int m = 0; m < alphabet_len; m++)
{
password[4] = alphabet[m];
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}

if(flag)
printf("Password: %sn", password);
else
printf("Password not foundn");
}









share|improve this question









New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • Post the declaration of crypt().
    – chux
    3 hours ago










  • @chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
    – Reinderien
    3 hours ago










  • Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
    – chux
    3 hours ago














3












3








3







I have implemented a program in C to crack passwords by generating all possible combinations of words ([A-Z][a-z]) up to the length of 5. While the program works, I would like to receive comments on the efficiency of the algorithm and other design decisions that would improve the code. The exercise is part of the course CS50 by Harvard.



I timed the program using unix's time and the time the program took to print all the combinations was



**real**    14m39.433s; 
**user** 0m10.040s;
**sys** 0m36.356s.


CS50.h is a library developed for the course as training wheels for students. String (char*) and Bool are types defined in this library.



#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <cs50.h>

int main(int argc, char *argv)
{
if(argc != 2)
{
printf("Usage: ./crack hashn");
return 1;
}

string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char password[6] = "";
string hash = argv[1];
char salt[3];
memcpy(salt, hash, 2);
salt[2] = '';

bool flag = false;
int alphabet_len = 52;

for(int i = 0; i < alphabet_len; i++)
{
password[0] = alphabet[i];
password[1] = password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int j = 0; j < alphabet_len; j++)
{
password[1] = alphabet[j];
password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int k = 0; k < alphabet_len; k++)
{
password[2] = alphabet[k];
password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int l = 0; l < alphabet_len; l++)
{
password[3] = alphabet[l];
password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int m = 0; m < alphabet_len; m++)
{
password[4] = alphabet[m];
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}

if(flag)
printf("Password: %sn", password);
else
printf("Password not foundn");
}









share|improve this question









New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I have implemented a program in C to crack passwords by generating all possible combinations of words ([A-Z][a-z]) up to the length of 5. While the program works, I would like to receive comments on the efficiency of the algorithm and other design decisions that would improve the code. The exercise is part of the course CS50 by Harvard.



I timed the program using unix's time and the time the program took to print all the combinations was



**real**    14m39.433s; 
**user** 0m10.040s;
**sys** 0m36.356s.


CS50.h is a library developed for the course as training wheels for students. String (char*) and Bool are types defined in this library.



#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <cs50.h>

int main(int argc, char *argv)
{
if(argc != 2)
{
printf("Usage: ./crack hashn");
return 1;
}

string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char password[6] = "";
string hash = argv[1];
char salt[3];
memcpy(salt, hash, 2);
salt[2] = '';

bool flag = false;
int alphabet_len = 52;

for(int i = 0; i < alphabet_len; i++)
{
password[0] = alphabet[i];
password[1] = password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int j = 0; j < alphabet_len; j++)
{
password[1] = alphabet[j];
password[2] = password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int k = 0; k < alphabet_len; k++)
{
password[2] = alphabet[k];
password[3] = password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int l = 0; l < alphabet_len; l++)
{
password[3] = alphabet[l];
password[4] = '';
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
for(int m = 0; m < alphabet_len; m++)
{
password[4] = alphabet[m];
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}
if(flag)
break;
}

if(flag)
printf("Password: %sn", password);
else
printf("Password not foundn");
}






performance c






share|improve this question









New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 4 hours ago









Reinderien

3,399720




3,399720






New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 10 hours ago









Oar

161




161




New contributor




Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Oar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • Post the declaration of crypt().
    – chux
    3 hours ago










  • @chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
    – Reinderien
    3 hours ago










  • Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
    – chux
    3 hours ago


















  • Post the declaration of crypt().
    – chux
    3 hours ago










  • @chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
    – Reinderien
    3 hours ago










  • Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
    – chux
    3 hours ago
















Post the declaration of crypt().
– chux
3 hours ago




Post the declaration of crypt().
– chux
3 hours ago












@chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
– Reinderien
3 hours ago




@chux pubs.opengroup.org/onlinepubs/007904975/functions/crypt.html
– Reinderien
3 hours ago












Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
– chux
3 hours ago




Oar, "CS50.h is a library ... Bool are types defined in this library." Why mention Bool is it is not used in code? Or is that a typo and you meant bool?
– chux
3 hours ago










2 Answers
2






active

oldest

votes


















4














cs50.h?



This seems less like a set of "training wheels" and more like a bicycle for fish. It's potentially confusing, opaque, and doesn't seem all that useful. If I were you, I'd be learning how to code in real C - using char*, and bool from stdbool.h.



Don't store things that should be computed



Your string alphabet shouldn't exist. Just iterate a char between a-z and A-Z. Characters can be incremented the same way that integers can.



Input validation



It seems like you expect hash to be two characters long, but you don't check that. You should be checking it with strlen; then you can issue memcpy without later setting a null terminator, as it'll be null-terminated already.



DRY



Don't repeat yourself. This is the most important aspect of the program that needs improvement. This block:



                for(int m = 0; m < alphabet_len; m++)
{
password[4] = alphabet[m];
if(!strcmp(hash, crypt(password, salt)))
{
flag = true;
break;
}
}
if(flag)
break;


is repeated nearly verbatim five times. There are many different ways to condense this. The easiest is probably a recursive function that calls itself with an increasing depth integer. This may actually decrease the performance of the application, but that's up to you to test. There are also ways to rewrite this loop to have state so that neither copy-and-paste nor recursion are necessary; you'll probably want to compare such a method against a recursive method to see which is more performant and clean.






share|improve this answer































    1














    regarding:



        printf("Usage: ./crack hashn");



    1. Error messages should be output to stderr, not stdout.

    2. an executable can be renamed, so 'crack' is not a good thing to use.


    Suggest:



        fprintf( stderr, "USAGE: %s hashn", argv[0] );


    Note: argv[0] always contains the executable name






    share|improve this answer





















      Your Answer





      StackExchange.ifUsing("editor", function () {
      return StackExchange.using("mathjaxEditing", function () {
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      });
      });
      }, "mathjax-editing");

      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "196"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });






      Oar is a new contributor. Be nice, and check out our Code of Conduct.










      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210656%2fc-program-to-crack-passwords%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      4














      cs50.h?



      This seems less like a set of "training wheels" and more like a bicycle for fish. It's potentially confusing, opaque, and doesn't seem all that useful. If I were you, I'd be learning how to code in real C - using char*, and bool from stdbool.h.



      Don't store things that should be computed



      Your string alphabet shouldn't exist. Just iterate a char between a-z and A-Z. Characters can be incremented the same way that integers can.



      Input validation



      It seems like you expect hash to be two characters long, but you don't check that. You should be checking it with strlen; then you can issue memcpy without later setting a null terminator, as it'll be null-terminated already.



      DRY



      Don't repeat yourself. This is the most important aspect of the program that needs improvement. This block:



                      for(int m = 0; m < alphabet_len; m++)
      {
      password[4] = alphabet[m];
      if(!strcmp(hash, crypt(password, salt)))
      {
      flag = true;
      break;
      }
      }
      if(flag)
      break;


      is repeated nearly verbatim five times. There are many different ways to condense this. The easiest is probably a recursive function that calls itself with an increasing depth integer. This may actually decrease the performance of the application, but that's up to you to test. There are also ways to rewrite this loop to have state so that neither copy-and-paste nor recursion are necessary; you'll probably want to compare such a method against a recursive method to see which is more performant and clean.






      share|improve this answer




























        4














        cs50.h?



        This seems less like a set of "training wheels" and more like a bicycle for fish. It's potentially confusing, opaque, and doesn't seem all that useful. If I were you, I'd be learning how to code in real C - using char*, and bool from stdbool.h.



        Don't store things that should be computed



        Your string alphabet shouldn't exist. Just iterate a char between a-z and A-Z. Characters can be incremented the same way that integers can.



        Input validation



        It seems like you expect hash to be two characters long, but you don't check that. You should be checking it with strlen; then you can issue memcpy without later setting a null terminator, as it'll be null-terminated already.



        DRY



        Don't repeat yourself. This is the most important aspect of the program that needs improvement. This block:



                        for(int m = 0; m < alphabet_len; m++)
        {
        password[4] = alphabet[m];
        if(!strcmp(hash, crypt(password, salt)))
        {
        flag = true;
        break;
        }
        }
        if(flag)
        break;


        is repeated nearly verbatim five times. There are many different ways to condense this. The easiest is probably a recursive function that calls itself with an increasing depth integer. This may actually decrease the performance of the application, but that's up to you to test. There are also ways to rewrite this loop to have state so that neither copy-and-paste nor recursion are necessary; you'll probably want to compare such a method against a recursive method to see which is more performant and clean.






        share|improve this answer


























          4












          4








          4






          cs50.h?



          This seems less like a set of "training wheels" and more like a bicycle for fish. It's potentially confusing, opaque, and doesn't seem all that useful. If I were you, I'd be learning how to code in real C - using char*, and bool from stdbool.h.



          Don't store things that should be computed



          Your string alphabet shouldn't exist. Just iterate a char between a-z and A-Z. Characters can be incremented the same way that integers can.



          Input validation



          It seems like you expect hash to be two characters long, but you don't check that. You should be checking it with strlen; then you can issue memcpy without later setting a null terminator, as it'll be null-terminated already.



          DRY



          Don't repeat yourself. This is the most important aspect of the program that needs improvement. This block:



                          for(int m = 0; m < alphabet_len; m++)
          {
          password[4] = alphabet[m];
          if(!strcmp(hash, crypt(password, salt)))
          {
          flag = true;
          break;
          }
          }
          if(flag)
          break;


          is repeated nearly verbatim five times. There are many different ways to condense this. The easiest is probably a recursive function that calls itself with an increasing depth integer. This may actually decrease the performance of the application, but that's up to you to test. There are also ways to rewrite this loop to have state so that neither copy-and-paste nor recursion are necessary; you'll probably want to compare such a method against a recursive method to see which is more performant and clean.






          share|improve this answer














          cs50.h?



          This seems less like a set of "training wheels" and more like a bicycle for fish. It's potentially confusing, opaque, and doesn't seem all that useful. If I were you, I'd be learning how to code in real C - using char*, and bool from stdbool.h.



          Don't store things that should be computed



          Your string alphabet shouldn't exist. Just iterate a char between a-z and A-Z. Characters can be incremented the same way that integers can.



          Input validation



          It seems like you expect hash to be two characters long, but you don't check that. You should be checking it with strlen; then you can issue memcpy without later setting a null terminator, as it'll be null-terminated already.



          DRY



          Don't repeat yourself. This is the most important aspect of the program that needs improvement. This block:



                          for(int m = 0; m < alphabet_len; m++)
          {
          password[4] = alphabet[m];
          if(!strcmp(hash, crypt(password, salt)))
          {
          flag = true;
          break;
          }
          }
          if(flag)
          break;


          is repeated nearly verbatim five times. There are many different ways to condense this. The easiest is probably a recursive function that calls itself with an increasing depth integer. This may actually decrease the performance of the application, but that's up to you to test. There are also ways to rewrite this loop to have state so that neither copy-and-paste nor recursion are necessary; you'll probably want to compare such a method against a recursive method to see which is more performant and clean.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 3 hours ago

























          answered 4 hours ago









          Reinderien

          3,399720




          3,399720

























              1














              regarding:



                  printf("Usage: ./crack hashn");



              1. Error messages should be output to stderr, not stdout.

              2. an executable can be renamed, so 'crack' is not a good thing to use.


              Suggest:



                  fprintf( stderr, "USAGE: %s hashn", argv[0] );


              Note: argv[0] always contains the executable name






              share|improve this answer


























                1














                regarding:



                    printf("Usage: ./crack hashn");



                1. Error messages should be output to stderr, not stdout.

                2. an executable can be renamed, so 'crack' is not a good thing to use.


                Suggest:



                    fprintf( stderr, "USAGE: %s hashn", argv[0] );


                Note: argv[0] always contains the executable name






                share|improve this answer
























                  1












                  1








                  1






                  regarding:



                      printf("Usage: ./crack hashn");



                  1. Error messages should be output to stderr, not stdout.

                  2. an executable can be renamed, so 'crack' is not a good thing to use.


                  Suggest:



                      fprintf( stderr, "USAGE: %s hashn", argv[0] );


                  Note: argv[0] always contains the executable name






                  share|improve this answer












                  regarding:



                      printf("Usage: ./crack hashn");



                  1. Error messages should be output to stderr, not stdout.

                  2. an executable can be renamed, so 'crack' is not a good thing to use.


                  Suggest:



                      fprintf( stderr, "USAGE: %s hashn", argv[0] );


                  Note: argv[0] always contains the executable name







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 3 hours ago









                  user3629249

                  1,64759




                  1,64759






















                      Oar is a new contributor. Be nice, and check out our Code of Conduct.










                      draft saved

                      draft discarded


















                      Oar is a new contributor. Be nice, and check out our Code of Conduct.













                      Oar is a new contributor. Be nice, and check out our Code of Conduct.












                      Oar is a new contributor. Be nice, and check out our Code of Conduct.
















                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210656%2fc-program-to-crack-passwords%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Quarter-circle Tiles

                      build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

                      Mont Emei