🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of SergiiVlasiuk

SergiiVlasiuk's solution

to All Your Base in the Scala Track

Published at Aug 27 2019 · 0 comments
Instructions
Test suite
Solution

Convert a number, represented as a sequence of digits in one base, to any other base.

Implement general base conversion. Given a number in base a, represented as a sequence of digits, convert it to base b.

Note

  • Try to implement the conversion yourself. Do not use something else to perform the conversion for you.

About Positional Notation

In positional notation, a number in base b can be understood as a linear combination of powers of b.

The number 42, in base 10, means:

(4 * 10^1) + (2 * 10^0)

The number 101010, in base 2, means:

(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)

The number 1120, in base 3, means:

(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)

I think you got the idea!

Yes. Those three numbers above are exactly the same. Congratulations!

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command sbt test in the exercise directory.

For more detailed info about the Scala track see the help page.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

AllYourBaseTest.scala

import org.scalatest.{Matchers, FunSuite}

/** @version 2.3.0 */
class AllYourBaseTest extends FunSuite with Matchers {

  test("single bit one to decimal") {
    AllYourBase.rebase(2, List(1), 10) should be(Some(List(1)))
  }

  test("binary to single decimal") {
    pending
    AllYourBase.rebase(2, List(1, 0, 1), 10) should be(Some(List(5)))
  }

  test("single decimal to binary") {
    pending
    AllYourBase.rebase(10, List(5), 2) should be(Some(List(1, 0, 1)))
  }

  test("binary to multiple decimal") {
    pending
    AllYourBase.rebase(2, List(1, 0, 1, 0, 1, 0), 10) should be(
      Some(List(4, 2)))
  }

  test("decimal to binary") {
    pending
    AllYourBase.rebase(10, List(4, 2), 2) should be(
      Some(List(1, 0, 1, 0, 1, 0)))
  }

  test("trinary to hexadecimal") {
    pending
    AllYourBase.rebase(3, List(1, 1, 2, 0), 16) should be(Some(List(2, 10)))
  }

  test("hexadecimal to trinary") {
    pending
    AllYourBase.rebase(16, List(2, 10), 3) should be(Some(List(1, 1, 2, 0)))
  }

  test("15-bit integer") {
    pending
    AllYourBase.rebase(97, List(3, 46, 60), 73) should be(Some(List(6, 10, 45)))
  }

  test("empty list") {
    pending
    AllYourBase.rebase(2, List(), 10) should be(Some(List(0)))
  }

  test("single zero") {
    pending
    AllYourBase.rebase(10, List(0), 2) should be(Some(List(0)))
  }

  test("multiple zeros") {
    pending
    AllYourBase.rebase(10, List(0, 0, 0), 2) should be(Some(List(0)))
  }

  test("leading zeros") {
    pending
    AllYourBase.rebase(7, List(0, 6, 0), 10) should be(Some(List(4, 2)))
  }

  test("input base is one") {
    pending
    AllYourBase.rebase(1, List(0), 10) should be(None)
  }

  test("input base is zero") {
    pending
    AllYourBase.rebase(0, List(), 10) should be(None)
  }

  test("input base is negative") {
    pending
    AllYourBase.rebase(-2, List(1), 10) should be(None)
  }

  test("negative digit") {
    pending
    AllYourBase.rebase(2, List(1, -1, 1, 0, 1, 0), 10) should be(None)
  }

  test("invalid positive digit") {
    pending
    AllYourBase.rebase(2, List(1, 2, 1, 0, 1, 0), 10) should be(None)
  }

  test("output base is one") {
    pending
    AllYourBase.rebase(2, List(1, 0, 1, 0, 1, 0), 1) should be(None)
  }

  test("output base is zero") {
    pending
    AllYourBase.rebase(10, List(7), 0) should be(None)
  }

  test("output base is negative") {
    pending
    AllYourBase.rebase(2, List(1), -7) should be(None)
  }

  test("both bases are negative") {
    pending
    AllYourBase.rebase(-2, List(1), -7) should be(None)
  }
}
object AllYourBase {
  def rebase(fromBase: Int, digits: List[Int], toBase: Int): Option[List[Int]] = {
    val invalidBase = fromBase < 2 || toBase < 2
    lazy val toDecimalValue: Int = digits.foldLeft(0)((total, digit) => (total * fromBase) + digit)

    def invalidDigit(digit: Int): Boolean = digit < 0 || digit >= fromBase

    def convertBase(decimal: Int): List[Int] =
      if (decimal < toBase) decimal :: Nil
      else (decimal % toBase)::convertBase(decimal / toBase)

    if (digits.exists(invalidDigit) || invalidBase) None
    else Some(convertBase(toDecimalValue).reverse)
  }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?