Blum integer: Difference between revisions

Content added Content deleted
(Added Easylang)
(Haskell implementation)
Line 483: Line 483:
<pre>
<pre>
Same as Wren example.
Same as Wren example.
</pre>

=={{header|Haskell}}==
Works with <code>GHC 9.2.8</code> and [https://hackage.haskell.org/package/arithmoi-0.13.0.0 arithmoi-0.13.0.0].
<syntaxhighlight lang="haskell">
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Rosetta.BlumInteger
( Stream (..)
, Stats (..)
, toList
, blumIntegers
, countLastDigits
) where

import GHC.Natural (Natural)
import Math.NumberTheory.Primes (Prime (..), nextPrime)

-- | A stream is an infinite list.
data Stream a = a :> Stream a
deriving Functor

-- | Converts a stream to the corresponding infinite list.
toList :: Stream a -> [a]
toList (x :> xs) = x : toList xs

unsafeFromList :: [a] -> Stream a
unsafeFromList = foldr (:>) $ error "fromList: finite list"

primes3mod4 :: Stream (Prime Natural)
primes3mod4 = unsafeFromList [nextPrime 3, nextPrime 7 ..]

-- Assume:
-- * All numbers in all the streams are distinct.
-- * Each stream is sorted.
-- * In the stream of streams, the first element of each stream is less than the first element of the next stream.
sortStreams :: forall a. Ord a => Stream (Stream a) -> Stream a
sortStreams ((x :> xs) :> xss) = x :> sortStreams (insert xs xss)
where
insert :: Stream a -> Stream (Stream a) -> Stream (Stream a)
insert ys@(y :> _) zss@(zs@(z :> _) :> zss')
| y < z = ys :> zss
| otherwise = zs :> insert ys zss'

-- | The
blumIntegers :: Stream Natural
blumIntegers = sortStreams $ go $ unPrime <$> primes3mod4
where
go :: Stream Natural -> Stream (Stream Natural)
go (p :> ps) = ((p *) <$> ps) :> go ps

data Stats a = Stats
{ s1 :: !a
, s3 :: !a
, s7 :: !a
, s9 :: !a
} deriving (Show, Eq, Ord, Functor)

lastDigit :: Natural -> Int
lastDigit n = fromIntegral $ n `mod` 10

updateCount :: Stats Int -> Natural -> Stats Int
updateCount !dc n = case lastDigit n of
1 -> dc { s1 = s1 dc + 1 }
3 -> dc { s3 = s3 dc + 1 }
7 -> dc { s7 = s7 dc + 1 }
9 -> dc { s9 = s9 dc + 1 }
_ -> error "updateCount: impossible"

countLastDigits :: forall a. Fractional a => Int -> Stream Natural -> Stats a
countLastDigits n = fmap f . go Stats { s1 = 0, s3 = 0, s7 = 0, s9 = 0 } n
where
go :: Stats Int -> Int -> Stream Natural -> Stats Int
go !dc 0 _ = dc
go !dc m (x :> xs) = go (updateCount dc x) (m - 1) xs

f :: Int -> a
f m = fromIntegral m / fromIntegral n


{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE TypeApplications #-}

module Main
( main
) where

import Control.Monad (forM_)
import Text.Printf (printf)

import Numeric.Natural (Natural)

import Rosetta.BlumInteger

main :: IO ()
main = do
let xs = toList blumIntegers

printf "The first 50 Blum integers are:\n\n"
forM_ (take 50 xs) $ \x -> do
printf "%3d\n" x
printf "\n"

nth xs 26_828
forM_ [100_000, 200_000 .. 400_000] $ nth xs
printf "\n"

printf "Distribution by final digit for the first 400000 Blum integers:\n\n"
let Stats r1 r3 r7 r9 = countLastDigits @Double 400_000 blumIntegers
forM_ [(1 :: Int, r1), (3, r3), (7, r7), (9, r9)] $ \(d, r) ->
printf "%d: %6.3f%%\n" d $ r * 100
printf "\n"

where

nth :: [Natural] -> Int -> IO ()
nth xs n = printf "The %6dth Blum integer is %8d.\n" n $ xs !! (n - 1)
</syntaxhighlight>

{{out}}
<pre>
The first 50 Blum integers are:

21
33
57
69
77
93
129
133
141
161
177
201
209
213
217
237
249
253
301
309
321
329
341
381
393
413
417
437
453
469
473
489
497
501
517
537
553
573
581
589
597
633
649
669
681
713
717
721
737
749

The 26828th Blum integer is 524273.
The 100000th Blum integer is 2075217.
The 200000th Blum integer is 4275533.
The 300000th Blum integer is 6521629.
The 400000th Blum integer is 8802377.

Distribution by final digit for the first 400000 Blum integers:

1: 25.001%
3: 25.017%
7: 24.997%
9: 24.985%
</pre>
</pre>