DEV Community

Kristian Pedersen
Kristian Pedersen

Posted on

#30daysofelm Day 12: elm-binary

This is day 12 of my 30 day Elm challenge

Code/demo: https://ellie-app.com/bVZL2PwfQbra1

Screenshot of binary number application

Unrelated to today's project, I just discovered Bekk's Elm christmas calendar, with 24 very nicely written posts:

Yesterday, I wrote some binary conversion functions from scratch, which was fun!

Today, I want to recreate yesterday's project using icidasset/elm-binary

1. Convert from int to binary, and display as a string

elm-binary has built-in conversion from int to binary, but it's represented as a list of bits.

Here's how I got it as a regular string instead:

intToBinary : Int -> String
intToBinary n =
    n -- 42
        |> Binary.fromDecimal -- [1, 0, 1, 0, 1, 0]
        |> Binary.toIntegers -- [1, 0, 1, 0, 1, 0]
        |> List.map String.fromInt -- ["1", "0", "1", "0", "1", "0"]
        |> String.join "" -- "101010"
Enter fullscreen mode Exit fullscreen mode

2. Figure out how many bits a number has

Today I discovered a much simpler solution than yesterday, as expected.

I entered 2 ^ x = 42 in WolframAlpha, and the solution was right there:

log(42) / log(2) = 5.392317422778761. 101010 is 6 bits, so rounding up works!

I stopped doing well in mathematics around the time we were introduced to logarithms, but it's fun to be able to use it in a setting like this.

numberOfBitsIn : Int -> Int
numberOfBitsIn n =
    ceiling (logBase 2 n)
Enter fullscreen mode Exit fullscreen mode

3. Generate numbers with same bits or 1 more

One silly thing I did yesterday was generating all numbers that have the same bits, even the lower ones.

The goal was to find the next highest number, so n + 1 should be the range's starting point.

Other than that, the basic idea is the same as yesterday:

generateBits : Int -> List String
generateBits n =
    let
        min =
            n + 1

        max =
            2 ^ (1 + numberOfBitsIn n)
    in
    List.range min max
        |> List.map intToBinary
Enter fullscreen mode Exit fullscreen mode

4. Getting numbers with matching 1 bits

First, I made this prototype in JavaScript:

const n = "101010"
const bin = ["101100", "101101", "101110", "101111", "110000", "110001", "110010", "110011", "110100", "110101", "110110", "110111", "111000", "111001", "111010", "111011", "111100", "111101", "111110", "111111", "1000000", "1000001", "1000010", "1000011", "1000100", "1000101", "1000110", "1000111", "1001000", "1001001", "1001010", "1001011", "1001100", "1001101", "1001110", "1001111", "1010000", "1010001", "1010010", "1010011", "1010100", "1010101", "1010110", "1010111", "1011000", "1011001", "1011010", "1011011", "1011100", "1011101", "1011110", "1011111", "1100000", "1100001", "1100010", "1100011", "1100100", "1100101", "1100110", "1100111", "1101000", "1101001", "1101010", "1101011", "1101100", "1101101", "1101110", "1101111", "1110000", "1110001", "1110010", "1110011", "1110100", "1110101", "1110110", "1110111", "1111000", "1111001", "1111010", "1111011", "1111100", "1111101", "1111110", "1111111", "10000000"]

const matches = bin.reduce((acc, char) => {
    const hasSameNumberOf1s = char
        .split("")
        .filter(n => n === "1")
        .length === 3

    if (hasSameNumberOf1s) {
        acc.push(char)
    }

    return acc
}, [])

console.log(bin.length) // 85
console.log(matches.length) // 20
Enter fullscreen mode Exit fullscreen mode

Then in Elm, I combined this with List.head to get the first element, along with some other small changes, including a utility function for counting 1 bits:

count1s : String -> Int
count1s binaryNumber =
    binaryNumber
        |> String.toList
        |> List.map String.fromChar
        |> List.filter (\n -> n == "1")
        |> List.length


getNextNumberWithSame1Bits : Int -> String
getNextNumberWithSame1Bits number =
    generateBits number
        |> List.foldl
            (\currentBinaryNumber acc ->
                if count1s currentBinaryNumber == count1s (intToBinary number) then
                    List.append acc [ currentBinaryNumber ]

                else
                    acc
            )
            []
        |> List.head
        |> Maybe.withDefault ""
Enter fullscreen mode Exit fullscreen mode

This code shows two things I need help making more elegant:

  1. Converting from Char to String. I just want strings of length 1. :)
  2. List.append acc [ curr ] looks a bit strange. In JavaScript, I would do acc.push(curr) or [...acc, curr]
  3. Doing Maybe.withDefault is a bit tiresome, although it's better than undefined.

5. The view function

The setup and update function are the same as all other standard Browser.sandbox programs, so I won't bother posting them here.

Here's the view function:

view : Model -> Html Msg
view model =
    div []
        [ input
            [ type_ "number"
            , autofocus True
            , onInput NewNumberEntered
            , value <| String.fromInt model.currentInt
            ]
            []

        -- Int to binary
        , h1 [] [ text "Int to binary" ]
        , text <| String.fromInt model.currentInt ++ ": " ++ intToBinary model.currentInt
        , p [] [ text <| String.fromInt <| numberOfBitsIn model.currentInt ]

        -- Same bits or 1 more
        , div []
            [ h1 [] [ text "Same bits or 1 more:" ]
            , text <|
                (generateBits model.currentInt
                    |> String.join ", "
                )
            ]

        -- Next test
        , h1 [] [ text "Next number is: " ]
        , text <|
            let
                nextBinary =
                    getNextNumberWithSame1Bits model.currentInt

                nextInt =
                    nextBinary
                        |> binaryToInt
                        |> String.fromInt
            in
            nextInt
                ++ " ("
                ++ nextBinary
                ++ ")"
        ]
Enter fullscreen mode Exit fullscreen mode

I think it looks a bit messy with all the conversions and joins, but it gets the job done.

Conclusion

As I suspected, using a binary number package was way nicer than implementing things myself. I'm happier with today's code.

One of the coming days, I'm pretty sure I will visualize these numbers somehow.

When you use the up arrow in the input field, notice how the next number jumps up and down a bit.

See you tomorrow!

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay