Last active
July 23, 2023 12:57
-
-
Save revathskumar/da8ece60fd4bed6e5de415e6324c6760 to your computer and use it in GitHub Desktop.
Convert a decimal integer to a hexdecimal string : port of https://github.com/rtfeldman/elm-hex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Hex exposing (fromString, toString) | |
{-| Convert to and from Hex strings. | |
@docs fromString, toString | |
-} | |
import Math exposing ( modBy ) | |
{-| Convert a hexdecimal string such as "abc94f" to a decimal integer. | |
Hex.fromString "a5" == Ok 165 | |
Hex.fromString "hat" == Err "invalid hexadecimal string" | |
-} | |
fromString : String -> Result String Int | |
fromString str = | |
if String.isEmpty str then | |
Err "Empty strings are not valid hexadecimal strings." | |
else | |
let | |
result = | |
if String.startsWith "-" str then | |
let | |
list = case Array.popFirst (String.toArray str) of | |
Nothing -> | |
[] | |
Just { first = head, rest = tail } -> | |
if Array.isEmpty tail then | |
[] | |
else | |
tail | |
in | |
fromStringHelp (Array.length list - 1) list 0 | |
|> Result.map negate | |
else | |
fromStringHelp (String.length str - 1) (String.toArray str) 0 | |
formatError err = | |
String.join " " | |
[ "\"" ++ str ++ "\"" | |
, "is not a valid hexadecimal string because" | |
, err | |
] | |
in | |
Result.mapError formatError result | |
fromStringHelp : Int -> Array Char -> Int -> Result String Int | |
fromStringHelp position chars accumulated = | |
case chars of | |
[] -> | |
Ok accumulated | |
_ -> | |
case Array.popFirst chars of | |
Nothing -> | |
Result.Ok accumulated | |
Just { first = head, rest = tail } -> | |
-- NOTE: It's important to have this call `fromStringHelp` directly. | |
-- Previously this called a helper function, but that meant this | |
-- was not tail-call optimized; it did not compile to a `while` loop | |
-- the way it does now. See 240c3d5aa4f97463b924728935d2989621e9fd6b | |
case head of | |
'0' -> | |
fromStringHelp (position - 1) tail accumulated | |
'1' -> | |
fromStringHelp (position - 1) tail (accumulated + (16 ^ position)) | |
'2' -> | |
fromStringHelp (position - 1) tail (accumulated + (2 * (16 ^ position))) | |
'3' -> | |
fromStringHelp (position - 1) tail (accumulated + (3 * (16 ^ position))) | |
'4' -> | |
fromStringHelp (position - 1) tail (accumulated + (4 * (16 ^ position))) | |
'5' -> | |
fromStringHelp (position - 1) tail (accumulated + (5 * (16 ^ position))) | |
'6' -> | |
fromStringHelp (position - 1) tail (accumulated + (6 * (16 ^ position))) | |
'7' -> | |
fromStringHelp (position - 1) tail (accumulated + (7 * (16 ^ position))) | |
'8' -> | |
fromStringHelp (position - 1) tail (accumulated + (8 * (16 ^ position))) | |
'9' -> | |
fromStringHelp (position - 1) tail (accumulated + (9 * (16 ^ position))) | |
'a' -> | |
fromStringHelp (position - 1) tail (accumulated + (10 * (16 ^ position))) | |
'b' -> | |
fromStringHelp (position - 1) tail (accumulated + (11 * (16 ^ position))) | |
'c' -> | |
fromStringHelp (position - 1) tail (accumulated + (12 * (16 ^ position))) | |
'd' -> | |
fromStringHelp (position - 1) tail (accumulated + (13 * (16 ^ position))) | |
'e' -> | |
fromStringHelp (position - 1) tail (accumulated + (14 * (16 ^ position))) | |
'f' -> | |
fromStringHelp (position - 1) tail (accumulated + (15 * (16 ^ position))) | |
nonHex -> | |
Err (String.fromChar nonHex ++ " is not a valid hexadecimal character.") | |
{-| Convert a decimal integer to a hexdecimal string such as `"abc94f"`. | |
Hex.toString 165 == "a5" | |
-} | |
toString : Int -> String | |
toString num = | |
String.fromArray | |
<| (if num < 0 then | |
Array.pushFirst '-' (unsafePositiveToDigits [] (negate num)) | |
else | |
unsafePositiveToDigits [] num | |
) | |
{-| ONLY EVER CALL THIS WITH POSITIVE INTEGERS! | |
-} | |
unsafePositiveToDigits : Array Char -> Int -> Array Char | |
unsafePositiveToDigits digits num = | |
if num < 16 then | |
let | |
h = | |
unsafeToDigit num | |
in | |
Array.pushFirst h digits | |
else | |
let | |
h = | |
unsafeToDigit (modBy 16 num) | |
in | |
unsafePositiveToDigits (Array.pushFirst h digits) (num // 16) | |
{-| ONLY EVER CALL THIS WITH INTEGERS BETWEEN 0 and 15! | |
-} | |
unsafeToDigit : Int -> Char | |
unsafeToDigit num = | |
case num of | |
0 -> | |
'0' | |
1 -> | |
'1' | |
2 -> | |
'2' | |
3 -> | |
'3' | |
4 -> | |
'4' | |
5 -> | |
'5' | |
6 -> | |
'6' | |
7 -> | |
'7' | |
8 -> | |
'8' | |
9 -> | |
'9' | |
10 -> | |
'a' | |
11 -> | |
'b' | |
12 -> | |
'c' | |
13 -> | |
'd' | |
14 -> | |
'e' | |
15 -> | |
'f' | |
_ -> | |
-- if this ever gets called with a number over 15, it will never | |
-- terminate! If that happens, debug further by uncommenting this: | |
-- | |
-- Debug.todo ("Tried to convert " ++ toString num ++ " to hexadecimal.") | |
unsafeToDigit num |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment