private function multiplyStrings($topNumber, $bottomNumber)
    {
        $topNumberLength = strlen($topNumber);
        $bottomNumberLength = strlen($bottomNumber);

        if ($topNumberLength < $bottomNumberLength) {
            // multiplication is commutative, and this algorithm
            // performs better if the bottom number is shorter.
            return $this->multiplyStrings($bottomNumber, $topNumber);
        }

        $stepNumber = 0;
        $result = new DecimalNumber('0');

        for ($i = $bottomNumberLength - 1; $i >= 0; $i--) {
            $carryOver = 0;
            $partialResult = '';

            // optimization: we don't need to bother multiplying by zero
            if ($bottomNumber[$i] === '0') {
                $stepNumber++;
                continue;
            }

            if ($bottomNumber[$i] === '1') {
                // multiplying by one is the same as copying the top number
                $partialResult = strrev($topNumber);
            } else {
                // digit-by-digit multiplication using carry-over
                for ($j = $topNumberLength - 1; $j >= 0; $j--) {
                    $multiplicationResult = ($bottomNumber[$i] * $topNumber[$j]) + $carryOver;
                    $carryOver = floor($multiplicationResult / 10);
                    $partialResult .= $multiplicationResult % 10;
                }

                if ($carryOver > 0) {
                    $partialResult .= $carryOver;
                }
            }

            // pad the partial result with as many zeros as performed steps
            $padding = str_pad('', $stepNumber, '0');
            $partialResult = $padding . $partialResult;

            // add to the result
            $result = $result->plus(
                new DecimalNumber(strrev($partialResult))
            );

            $stepNumber++;
        }

        return (string) $result;
    }