{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module: Crypto.HDKey.BIP39
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
-- mnemonic codes for deterministic key generation, supporting wordlists in
-- multiple languages.

module Crypto.KDF.BIP39 (
  -- * Mnemonic construction and validation
    mnemonic
  , _mnemonic
  , valid
  , _valid

  -- * Seed derivation
  , seed
  , _seed
  , seed_unsafe

  -- * Wordlists
  --
  -- $wordlists
  , Wordlist(..)
  , english
  , chinese_traditional
  , chinese_simplified
  , czech
  , french
  , korean
  , italian
  , japanese
  , portuguese
  , spanish
  ) where

import qualified Crypto.KDF.PBKDF as PBKDF
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA512 as SHA512
import Data.Bits ((.&.), (.|.), (.>>.), (.<<.))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU
import qualified Data.Foldable as F
import qualified Data.Maybe as M
import qualified Data.Primitive.Array as PA
import Data.Word (Word64)
import qualified Data.List as L
import Prelude hiding (words)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.ICU.Normalize2 as ICU
import System.IO.Unsafe (unsafePerformIO)

fi :: (Integral a, Num b) => a -> b
fi :: forall a b. (Integral a, Num b) => a -> b
fi = a -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# INLINE fi #-}

-- | A BIP39 wordlist.
newtype Wordlist = Wordlist (PA.Array T.Text)

-- | Generate a BIP39 mnemonic from some entropy, using the default English
--   wordlist.
--
--   The entropy must be at least 128 bits long and at most 256 bits
--   long. Providing invalid entropy will result in an 'ErrorCall'
--   exception.
--
--   >>> import qualified System.Entropy as E
--   >>> trop <- E.getEntropy 16
--   >>> mnemonic trop
--   "coral maze mimic half fat breeze thought club give brass bone snake"
mnemonic
  :: BS.ByteString -- ^ 128-256 bits of entropy
  -> T.Text
mnemonic :: ByteString -> Text
mnemonic = Wordlist -> ByteString -> Text
_mnemonic Wordlist
english

-- | Generate a BIP39 mnemonic from some entropy, using the provided
--   wordlist.
--
--   The entropy must be at least 128 bits long and at most 256 bits
--   long. Providing invalid entropy will result in an 'ErrorCall'
--   exception.
--
--   >>> import qualified System.Entropy as E
--   >>> trop <- E.getEntropy 16
--   >>> _mnemonic czech trop
--   "naslepo lysina dikobraz slupka beseda rorejs ostraha kobliha napevno blahobyt kazivost jiskra"
_mnemonic
  :: Wordlist
  -> BS.ByteString -- ^ 128-256 bits of entropy
  -> T.Text
_mnemonic :: Wordlist -> ByteString -> Text
_mnemonic (Wordlist Array Text
wlist) entropy :: ByteString
entropy@(BI.PS ForeignPtr Word8
_ Int
_ Int
l)
  | Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
16 = [Char] -> Text
forall a. HasCallStack => [Char] -> a
error [Char]
"ppad-bip39 (mnemonic): invalid entropy length"
  | Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
32 = [Char] -> Text
forall a. HasCallStack => [Char] -> a
error [Char]
"ppad-bip39 (mnemonic): invalid entropy length"
  | Bool
otherwise =
      let has :: ByteString
has = ByteString -> ByteString
SHA256.hash ByteString
entropy
          h :: Word8
h   = ByteString -> Word8
BU.unsafeHead ByteString
has
          n :: Int
n   = Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot` Int
4
          kek :: Word8
kek = Word8
h Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. (Word8
0b1111_1111 Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
.<<. (Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
n)) -- top n bits
          cat :: ByteString
cat = ByteString
entropy ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Word8 -> ByteString
BS.singleton Word8
kek
      in  Text -> [Text] -> Text
T.intercalate Text
" " (Array Text -> ByteString -> [Text]
words Array Text
wlist ByteString
cat)
{-# INLINE _mnemonic #-}

-- remaining, bits pool, number of bits in pool
type Acc = (BS.ByteString, Word64, Int)

words :: PA.Array T.Text -> BS.ByteString -> [T.Text]
words :: Array Text -> ByteString -> [Text]
words Array Text
wlist ByteString
bs = (Acc -> Maybe (Text, Acc)) -> Acc -> [Text]
forall b a. (b -> Maybe (a, b)) -> b -> [a]
L.unfoldr Acc -> Maybe (Text, Acc)
coalg (ByteString
bs, Word64
0, Int
0) where
  mask :: Word64
mask = Word64
0b0111_1111_1111
  coalg :: Acc -> Maybe (T.Text, Acc)
  coalg :: Acc -> Maybe (Text, Acc)
coalg (ByteString
etc, Word64
acc, Int
len)
    | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
10 =
        let w11 :: Int
w11  = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fi ((Word64
acc Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
.>>. (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
11)) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
mask) -- take bits from pool
            nacc :: Word64
nacc = Word64
acc Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. ((Word64
1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
.<<. (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
11)) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
1)   -- adjust pool
            nlen :: Int
nlen = Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
11                            -- track less bits
            word :: Text
word = Array Text -> Int -> Text
forall a. Array a -> Int -> a
PA.indexArray Array Text
wlist Int
w11
        in  (Text, Acc) -> Maybe (Text, Acc)
forall a. a -> Maybe a
Just (Text
word, (ByteString
etc, Word64
nacc, Int
nlen))
    | Bool -> Bool
not (ByteString -> Bool
BS.null ByteString
etc) =
        let next :: Word8
next = ByteString -> Word8
BU.unsafeHead ByteString
etc
            rest :: ByteString
rest = ByteString -> ByteString
BU.unsafeTail ByteString
etc
            nacc :: Word64
nacc = (Word64
acc Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
.<<. Int
8) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fi Word8
next -- add bits to pool
            nlen :: Int
nlen = Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
8                  -- track additional bits
        in  Acc -> Maybe (Text, Acc)
coalg (ByteString
rest, Word64
nacc, Int
nlen)
    | Bool
otherwise =
        Maybe (Text, Acc)
forall a. Maybe a
Nothing
{-# INLINE words #-}

-- | Derive a master seed from a provided mnemonic and passphrase, where the
--   mnemonic has been generated from the default English wordlist.
--
--   The mnemonic's length and words are validated. If you want to
--   validate the mnemonic's words against a non-English wordlist, use
--   '_seed'.
--
--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"
--   >>  let pass = "hunter2"
--   >>> seed mnem pass
--   <512-bit long seed>
seed
  :: T.Text        -- ^ mnemonic
  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)
  -> BS.ByteString -- ^ seed
seed :: Text -> Text -> ByteString
seed = Wordlist -> Text -> Text -> ByteString
_seed Wordlist
english

-- | Derive a master seed from a provided mnemonic and passphrase, where the
--   mnemonic has been generated from an arbitrary wordlist.
--
--   The provided mnemonic is checked for validity using '_valid'.
--   Providing an invalid mnemonic will result in an 'ErrorCall'
--   exception.
--
--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"
--   >>  let pass = "hunter2"
--   >>> _seed english mnem pass
--   <512-bit long seed>
_seed
  :: Wordlist      -- ^ wordlist
  -> T.Text        -- ^ mnemonic
  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)
  -> BS.ByteString -- ^ seed
_seed :: Wordlist -> Text -> Text -> ByteString
_seed Wordlist
wlist Text
mnem Text
pass
  | Bool -> Bool
not (Wordlist -> Text -> Bool
_valid Wordlist
wlist Text
mnem) =
      [Char] -> ByteString
forall a. HasCallStack => [Char] -> a
error [Char]
"ppad-bip39 (seed): invalid mnemonic"
  | Bool
otherwise =
      let salt :: ByteString
salt = Text -> ByteString
TE.encodeUtf8 (Text
"mnemonic" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
ICU.nfkd Text
pass)
          norm :: ByteString
norm = Text -> ByteString
TE.encodeUtf8 (Text -> Text
ICU.nfkd Text
mnem)
      in  (ByteString -> ByteString -> ByteString)
-> ByteString -> ByteString -> Word64 -> Word32 -> ByteString
PBKDF.derive ByteString -> ByteString -> ByteString
SHA512.hmac ByteString
norm ByteString
salt Word64
2048 Word32
64 where
{-# INLINE _seed #-}

-- | Derive a master seed from a provided mnemonic and passphrase.
--
--   The mnemonic's length is validated, but its individual words are
--   /not/. This function thus works for every wordlist.
--
--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"
--   >>  let pass = "hunter2"
--   >>> seed_unsafe mnem pass
--   <512-bit long seed>
seed_unsafe
  :: T.Text        -- ^ mnemonic
  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)
  -> BS.ByteString -- ^ seed
seed_unsafe :: Text -> Text -> ByteString
seed_unsafe Text
mnem Text
pass
  | [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Text -> [Text]
T.words Text
mnem) Int -> [Int] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Int
12, Int
15, Int
18, Int
21, Int
24] =
      [Char] -> ByteString
forall a. HasCallStack => [Char] -> a
error [Char]
"ppad-bip39 (seed_unsafe): invalid mnemonic"
  | Bool
otherwise =
      let salt :: ByteString
salt = Text -> ByteString
TE.encodeUtf8 (Text
"mnemonic" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
ICU.nfkd Text
pass)
          norm :: ByteString
norm = Text -> ByteString
TE.encodeUtf8 (Text -> Text
ICU.nfkd Text
mnem)
      in  (ByteString -> ByteString -> ByteString)
-> ByteString -> ByteString -> Word64 -> Word32 -> ByteString
PBKDF.derive ByteString -> ByteString -> ByteString
SHA512.hmac ByteString
norm ByteString
salt Word64
2048 Word32
64 where

-- | Validate a mnemonic against the default English wordlist.
--
--   Verifies that the mnemonic has a valid length, and that every word
--   is contained in the wordlist.
--
--   >>> valid "coral maze mimic half fat breeze thought club give brass bone snake"
--   True
--   >>> valid "coral maze mimic half fat breeze thought club give brass bone"
--   False
valid
  :: T.Text -- ^ mnemonic
  -> Bool   -- ^ 'True' if valid
valid :: Text -> Bool
valid Text
mnem =
       [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
ws Int -> [Int] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Int
12, Int
15, Int
18, Int
21, Int
24]
    Bool -> Bool -> Bool
&& (Maybe Text -> Bool) -> [Maybe Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Maybe Text -> Bool
forall a. Maybe a -> Bool
M.isJust ((Text -> Maybe Text) -> [Text] -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Text
word -> (Text -> Bool) -> Array Text -> Maybe Text
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
F.find (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
word) Array Text
wlist) [Text]
ws)
  where
    ws :: [Text]
ws = Text -> [Text]
T.words Text
mnem
    Wordlist Array Text
wlist = Wordlist
english

-- | Validate a mnemonic against a wordlist.
--
--   Verifies that the mnemonic has a valid length, and that every word
--   is contained in the provided wordlist.
--
--   >>> let mnem = "持 樓 粗 殺 承 圖 湧 整 拿 路 式 棋"
--   >>> _valid chinese_traditional mnem
--   True
--   >>> _valid chinese_simplified mnem
--   False
_valid
  :: Wordlist
  -> T.Text   -- ^ mnemonic
  -> Bool     -- ^ 'True' if valid
_valid :: Wordlist -> Text -> Bool
_valid (Wordlist Array Text
wlist) Text
mnem =
       [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
ws Int -> [Int] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Int
12, Int
15, Int
18, Int
21, Int
24]
    Bool -> Bool -> Bool
&& (Maybe Text -> Bool) -> [Maybe Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Maybe Text -> Bool
forall a. Maybe a -> Bool
M.isJust ((Text -> Maybe Text) -> [Text] -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Text
word -> (Text -> Bool) -> Array Text -> Maybe Text
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
F.find (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
word) Array Text
wlist) [Text]
ws)
  where
    ws :: [Text]
ws = Text -> [Text]
T.words Text
mnem

-- wordlists ------------------------------------------------------------------

-- $wordlists
--
-- Wordlists for various languages.
--
-- For the following examples:
--
-- >>> import qualified Data.Text.IO as TIO
-- >>> let trop = "0123456789abcdef"

-- | The default English wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic english trop
--   coral maze mimic half fat breeze thought club give brass bone snake
english :: Wordlist
english :: Wordlist
english = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/english.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE english #-}

-- | The default Traditional Chinese wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic chinese_traditional trop
--   持 樓 粗 殺 承 圖 湧 整 拿 路 式 棋
chinese_traditional :: Wordlist
chinese_traditional :: Wordlist
chinese_traditional = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/chinese_traditional.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE chinese_traditional #-}

-- | The default Simplified Chinese wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic chinese_simplified trop
--   持 楼 粗 杀 承 图 涌 整 拿 路 式 棋
chinese_simplified :: Wordlist
chinese_simplified :: Wordlist
chinese_simplified = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/chinese_simplified.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE chinese_simplified #-}

-- | The default Korean wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic korean trop
--   대문 어쩐지 여덟 설거지 볶음 그늘 태권도 단맛 상반기 균형 국왕 진출
korean :: Wordlist
korean :: Wordlist
korean = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/korean.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE korean #-}

-- | The default French wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic french trop
--   chlorure kimono légume flamme endroit bénéfice soulever céleste falaise belette banlieue reprise
french :: Wordlist
french :: Wordlist
french = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/french.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE french #-}

-- | The default Spanish wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic spanish trop
--   charla marido mente guía explicar banco tapa casco gemelo balcón ayuda ropa
spanish :: Wordlist
spanish :: Wordlist
spanish = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/spanish.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE spanish #-}

-- | The default Czech wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic czech trop
--   hadr omladina oslepit metr krajina deflace varovat flirt lovec dechovka cudnost svitek
czech :: Wordlist
czech :: Wordlist
czech = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/czech.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE czech #-}

-- |  The default Italian wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic italian trop
--   conferma nevrotico obbligo indole florido benigno svista cigno grotta belva barbaro sfocato
italian :: Wordlist
italian :: Wordlist
italian = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/italian.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE italian #-}

-- | The default Portuguese wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic portuguese trop
--   capacho juba lareira figurado ejetar avaliar sonhador cachorro exposto autismo aterro refinar
portuguese :: Wordlist
portuguese :: Wordlist
portuguese = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/portuguese.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE portuguese #-}

-- | The default Japanese wordlist.
--
--   >>> TIO.putStrLn $ _mnemonic japanese trop
--   きおん たさい たまご しゃおん こふん えきたい ますく がはく しかい えおり うろこ ひとごみ
japanese :: Wordlist
japanese :: Wordlist
japanese = IO Wordlist -> Wordlist
forall a. IO a -> a
unsafePerformIO (IO Wordlist -> Wordlist) -> IO Wordlist -> Wordlist
forall a b. (a -> b) -> a -> b
$ do
  Text
wlist <- (ByteString -> Text) -> IO ByteString -> IO Text
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
TE.decodeUtf8 ([Char] -> IO ByteString
BS.readFile [Char]
"etc/japanese.txt")
  let ls :: [Text]
ls = Text -> [Text]
T.lines Text
wlist
  Wordlist -> IO Wordlist
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array Text -> Wordlist
Wordlist ([Text] -> Array Text
forall a. [a] -> Array a
PA.arrayFromList [Text]
ls))
{-# NOINLINE japanese #-}