Browse Source

Merge branch '05-protein_translation'

Frédéric G. MARAND 9 months ago
parent
commit
c285451aff

+ 10 - 0
protein-translation/.exercism/config.json

@@ -0,0 +1,10 @@
+{
+  "blurb": "Translate RNA sequences into proteins.",
+  "authors": ["MichaelBunker"],
+  "contributors": [],
+  "files": {
+    "solution": ["ProteinTranslation.php"],
+    "test": ["ProteinTranslationTest.php"],
+    "example": [".meta/example.php"]
+  }
+}

+ 1 - 0
protein-translation/.exercism/metadata.json

@@ -0,0 +1 @@
+{"track":"php","exercise":"protein-translation","id":"a8eb2e85efd14f0fb3259b51969cd61e","url":"https://exercism.org/tracks/php/exercises/protein-translation","handle":"Fairgame","is_requester":true,"auto_approve":false}

+ 52 - 0
protein-translation/HELP.md

@@ -0,0 +1,52 @@
+# Help
+
+## Running the tests
+
+## Running the tests
+
+1. Go to the root of your PHP exercise directory, which is `<EXERCISM_WORKSPACE>/php`.
+   To find the Exercism workspace run
+
+       ➜ exercism debug | grep Workspace
+
+1. Get [PHPUnit] if you don't have it already.
+
+       ➜ wget -O phpunit https://phar.phpunit.de/phpunit-9.phar
+       ➜ chmod +x phpunit
+       ➜ ./phpunit --version
+
+2. Execute the tests:
+
+       ➜ ./phpunit file_to_test.php
+
+   For example, to run the tests for the Hello World exercise, you would run:
+
+       ➜ ./phpunit HelloWorldTest.php
+
+[PHPUnit]: https://phpunit.de
+
+## Submitting your solution
+
+You can submit your solution using the `exercism submit ProteinTranslation.php` command.
+This command will upload your solution to the Exercism website and print the solution page's URL.
+
+It's possible to submit an incomplete solution which allows you to:
+
+- See how others have completed the exercise
+- Request help from a mentor
+
+## Need to get help?
+
+If you'd like help solving the exercise, check the following pages:
+
+- The [PHP track's documentation](https://exercism.org/docs/tracks/php)
+- The [PHP track's programming category on the forum](https://forum.exercism.org/c/programming/php)
+- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
+- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
+
+Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
+
+To get help if you're having trouble, you can use one of the following resources:
+
+ - [/r/php](https://www.reddit.com/r/php) is the PHP subreddit.
+ - [StackOverflow](https://stackoverflow.com/questions/tagged/php) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.

+ 98 - 0
protein-translation/ProteinTranslation.php

@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * By adding type hints and enabling strict type checking, code can become
+ * easier to read, self-documenting and reduce the number of potential bugs.
+ * By default, type declarations are non-strict, which means they will attempt
+ * to change the original type to match the type specified by the
+ * type-declaration.
+ *
+ * In other words, if you pass a string to a function requiring a float,
+ * it will attempt to convert the string value to a float.
+ *
+ * To enable strict mode, a single declare directive must be placed at the top
+ * of the file.
+ * This means that the strictness of typing is configured on a per-file basis.
+ * This directive not only affects the type declarations of parameters, but also
+ * a function's return type.
+ *
+ * For more info review the Concept on strict type checking in the PHP track
+ * <link>.
+ *
+ * To disable strict typing, comment out the directive below.
+ */
+
+declare(strict_types=1);
+
+class ProteinError {
+
+  protected string $msg;
+
+  public function __construct(string $msg) {
+    $this->msg = $msg;
+  }
+
+  public function Error(): string {
+    return $this->msg;
+  }
+
+}
+
+const STOP = "STOP";
+const ErrStop = new ProteinError(STOP);
+const ErrInvalidCodon = new ProteinError("Invalid codon");
+
+class ProteinTranslation {
+
+  public function getProteins(string $rna) {
+    return $this->FromRNA($rna);
+  }
+
+  private function FromRNA(string $rna): array|ProteinError {
+    $names = [];
+    for ($i = 0; $i < strlen($rna); $i += 3) {
+      $codon = substr($rna, $i, 3);
+      $nameOrError = $this->FromCodon($codon);
+      if (is_string($nameOrError)) {
+        $names[] = $nameOrError;
+        continue;
+      }
+      if ($nameOrError == ErrStop) {
+        return $names;
+      }
+      throw new InvalidArgumentException($nameOrError->Error());
+    }
+    return $names;
+  }
+
+  private function FromCodon(string $codon): string|ProteinError {
+    $names = [
+      "AUG" => "Methionine",
+      "UUU" => "Phenylalanine",
+      "UUC" => "Phenylalanine",
+      "UUA" => "Leucine",
+      "UUG" => "Leucine",
+      "UCU" => "Serine",
+      "UCC" => "Serine",
+      "UCA" => "Serine",
+      "UCG" => "Serine",
+      "UAU" => "Tyrosine",
+      "UAC" => "Tyrosine",
+      "UGU" => "Cysteine",
+      "UGC" => "Cysteine",
+      "UGG" => "Tryptophan",
+      "UAA" => STOP,
+      "UAG" => STOP,
+      "UGA" => STOP,
+    ];
+    @$name = $names[$codon];
+    if (empty($name)) {
+      return ErrInvalidCodon;
+    }
+    if ($name == STOP) {
+      return ErrStop;
+    }
+    return $name;
+  }
+
+}

+ 200 - 0
protein-translation/ProteinTranslationTest.php

@@ -0,0 +1,200 @@
+<?php
+
+/*
+ * By adding type hints and enabling strict type checking, code can become
+ * easier to read, self-documenting and reduce the number of potential bugs.
+ * By default, type declarations are non-strict, which means they will attempt
+ * to change the original type to match the type specified by the
+ * type-declaration.
+ *
+ * In other words, if you pass a string to a function requiring a float,
+ * it will attempt to convert the string value to a float.
+ *
+ * To enable strict mode, a single declare directive must be placed at the top
+ * of the file.
+ * This means that the strictness of typing is configured on a per-file basis.
+ * This directive not only affects the type declarations of parameters, but also
+ * a function's return type.
+ *
+ * For more info review the Concept on strict type checking in the PHP track
+ * <link>.
+ *
+ * To disable strict typing, comment out the directive below.
+ */
+
+declare(strict_types=1);
+
+class ProteinTranslationTest extends PHPUnit\Framework\TestCase
+{
+    private ProteinTranslation $translater;
+
+    public static function setUpBeforeClass(): void
+    {
+        require_once 'ProteinTranslation.php';
+    }
+
+    public function setUp(): void
+    {
+        $this->translater = new ProteinTranslation();
+    }
+
+    public function testEmptyRnaSequence(): void
+    {
+        $this->assertEquals([], $this->translater->getProteins(''));
+    }
+
+    public function testMethionineRnaSequence(): void
+    {
+        $this->assertEquals(['Methionine'], $this->translater->getProteins('AUG'));
+    }
+
+    public function testPhenylalanineRnaSequenceOne(): void
+    {
+        $this->assertEquals(['Phenylalanine'], $this->translater->getProteins('UUU'));
+    }
+
+    public function testPhenylalanineRnaSequenceTwo(): void
+    {
+        $this->assertEquals(['Phenylalanine'], $this->translater->getProteins('UUC'));
+    }
+
+    public function testLeucineRnaSequenceOne(): void
+    {
+        $this->assertEquals(['Leucine'], $this->translater->getProteins('UUA'));
+    }
+
+    public function testLeucineRnaSequenceTwo(): void
+    {
+        $this->assertEquals(['Leucine'], $this->translater->getProteins('UUG'));
+    }
+
+    public function testSerineRnaSequenceOne(): void
+    {
+        $this->assertEquals(['Serine'], $this->translater->getProteins('UCU'));
+    }
+
+    public function testSerineRnaSequenceTwo(): void
+    {
+        $this->assertEquals(['Serine'], $this->translater->getProteins('UCC'));
+    }
+
+    public function testSerineRnaSequenceThree(): void
+    {
+        $this->assertEquals(['Serine'], $this->translater->getProteins('UCA'));
+    }
+
+    public function testSerineRnaSequenceFour(): void
+    {
+        $this->assertEquals(['Serine'], $this->translater->getProteins('UCG'));
+    }
+
+    public function testTyrosineRnaSequenceOne(): void
+    {
+        $this->assertEquals(['Tyrosine'], $this->translater->getProteins('UAU'));
+    }
+
+    public function testTyrosineRnaSequenceTwo(): void
+    {
+        $this->assertEquals(['Tyrosine'], $this->translater->getProteins('UAC'));
+    }
+
+    public function testCysteineRnaSequenceOne(): void
+    {
+        $this->assertEquals(['Cysteine'], $this->translater->getProteins('UGU'));
+    }
+
+    public function testCysteineRnaSequenceTwo(): void
+    {
+        $this->assertEquals(['Cysteine'], $this->translater->getProteins('UGC'));
+    }
+
+    public function testTryptophanRnaSequence(): void
+    {
+        $this->assertEquals(['Tryptophan'], $this->translater->getProteins('UGG'));
+    }
+
+    public function testStopCodonRnaSequenceOne(): void
+    {
+        $this->assertEquals([], $this->translater->getProteins('UAA'));
+    }
+
+    public function testStopCodonRnaSequenceTwo(): void
+    {
+        $this->assertEquals([], $this->translater->getProteins('UAG'));
+    }
+
+    public function testStopCodonRnaSequenceThree(): void
+    {
+        $this->assertEquals([], $this->translater->getProteins('UGA'));
+    }
+
+    public function testToCodonsTranslateToProteins(): void
+    {
+        $this->assertEquals(['Phenylalanine', 'Phenylalanine'], $this->translater->getProteins('UUUUUU'));
+    }
+
+    public function testToDifferentCodonsTranslateToProteins(): void
+    {
+        $this->assertEquals(['Leucine', 'Leucine'], $this->translater->getProteins('UUAUUG'));
+    }
+
+    public function testTranslateRnaStrandToCorrectProteinList(): void
+    {
+        $this->assertEquals(
+            ['Methionine', 'Phenylalanine', 'Tryptophan'],
+            $this->translater->getProteins('AUGUUUUGG')
+        );
+    }
+
+    public function testTranslationStopsIfStopCodonAtBeginningOfSequence(): void
+    {
+        $this->assertEquals([], $this->translater->getProteins('UAGUGG'));
+    }
+
+    public function testTranslationStopsIfStopCodonAtEndOfTwoCodonSequence(): void
+    {
+        $this->assertEquals(['Tryptophan'], $this->translater->getProteins('UGGUAG'));
+    }
+
+    public function testTranslationStopsIfStopCodonAtEndOfThreeCodonSequence(): void
+    {
+        $this->assertEquals(['Methionine', 'Phenylalanine'], $this->translater->getProteins('AUGUUUUAA'));
+    }
+
+    public function testTranslationStopsIfStopCodonInMiddleOfThreeCodonSequence(): void
+    {
+        $this->assertEquals(['Tryptophan'], $this->translater->getProteins('UGGUAGUGG'));
+    }
+
+    public function testTranslationStopsIfStopCodonInMiddleOfSixCodonSequence(): void
+    {
+        $this->assertEquals(
+            ['Tryptophan', 'Cysteine', 'Tyrosine'],
+            $this->translater->getProteins('UGGUGUUAUUAAUGGUUU')
+        );
+    }
+
+    public function invalidCodonDataProvider(): array
+    {
+        return [
+            'Non-existing' => ['AAA'],
+            'Unknown' => ['XYZ'],
+            'Incomplete' => ['AUGU'],
+        ];
+    }
+
+    /**
+     * @dataProvider invalidCodonDataProvider
+     */
+    public function testTranslateFailsForInvalidCodons(string $rna): void
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Invalid codon');
+        $this->translater->getProteins($rna);
+    }
+
+    public function testTranslatePassesIfStopCodeBeforeIncompleteSequence(): void
+    {
+        $this->assertEquals(['Phenylalanine', 'Phenylalanine'], $this->translater->getProteins('UUCUUCUAAUGGU'));
+    }
+}

+ 53 - 0
protein-translation/README.md

@@ -0,0 +1,53 @@
+# Protein Translation
+
+Welcome to Protein Translation on Exercism's PHP Track.
+If you need help running the tests or submitting your code, check out `HELP.md`.
+
+## Instructions
+
+Translate RNA sequences into proteins.
+
+RNA can be broken into three nucleotide sequences called codons, and then translated to a polypeptide like so:
+
+RNA: `"AUGUUUUCU"` => translates to
+
+Codons: `"AUG", "UUU", "UCU"`
+=> which become a polypeptide with the following sequence =>
+
+Protein: `"Methionine", "Phenylalanine", "Serine"`
+
+There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise.  If it works for one codon, the program should work for all of them.
+However, feel free to expand the list in the test suite to include them all.
+
+There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated.
+
+All subsequent codons after are ignored, like this:
+
+RNA: `"AUGUUUUCUUAAAUG"` =>
+
+Codons: `"AUG", "UUU", "UCU", "UAA", "AUG"` =>
+
+Protein: `"Methionine", "Phenylalanine", "Serine"`
+
+Note the stop codon `"UAA"` terminates the translation and the final methionine is not translated into the protein sequence.
+
+Below are the codons and resulting Amino Acids needed for the exercise.
+
+Codon                 | Protein
+:---                  | :---
+AUG                   | Methionine
+UUU, UUC              | Phenylalanine
+UUA, UUG              | Leucine
+UCU, UCC, UCA, UCG    | Serine
+UAU, UAC              | Tyrosine
+UGU, UGC              | Cysteine
+UGG                   | Tryptophan
+UAA, UAG, UGA         | STOP
+
+Learn more about [protein translation on Wikipedia](http://en.wikipedia.org/wiki/Translation_(biology))
+
+## Source
+
+### Created by
+
+- @MichaelBunker