{-
Outputs a sorted list of numbers of the form 26^a * 10^b, and their
base-10 logarithm.
Copyright 2019 Ken Takusagawa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
-}
{-# LANGUAGE ScopedTypeVariables, LambdaCase, PackageImports #-}
module Main where {
import System.Environment(getArgs);
import Data.Function((&));
import Prelude hiding((.),(>>));
import qualified Data.List as List;
import qualified Data.Bifunctor as Bifunctor;
import qualified Data.List.Ordered as Ordered;
import Data.Ord(comparing);
import Text.Printf(printf);
type Ii = Integer;
main :: IO();
main = getArgs >>= \case{
[] -> mapM_ print1 nums; -- infinite output
-- limit to puzzles less than 10^size
[size] -> takeWhile (\n -> logn n <= read size * log 10) nums & mapM_ print1;
_ -> undefined;
};
bases :: [Ii];
bases = [26,10];
data N = N [Ii] deriving (Show,Eq);
value :: N -> Ii;
value (N x) = zipWith (^) bases x & product;
instance Ord N where {
compare = comparing
value -- OK if the values are not too large
-- logn
;
};
nums :: [N];
nums = N (map (const 0) bases) : foldr Ordered.union [] (do {
index <- zipWith const [0..] bases; -- enumFromTo 0 (length bases -1)
return (map (incn index) nums);
});
-- We use foldr union and not unionAll because we know the list of
-- bases is finite. If it were infinite (e.g., all primes), the
-- we would never be able to calculate values for comparison.
-- The function below is unsatisfying because splitAt requires linear
-- time. However, although using an array would improve this
-- subroutine to constant time, the whole algorithm (I think) needs to
-- copy the whole array to modify it, so it would still require linear
-- time. Note that this is linear time in the number of bases, which
-- is typically very small, e.g., 2.
-- increment just the nth item in the list
incn :: Ii -> N -> N;
incn n (N x) = let {
(p,q:rest) = List.genericSplitAt n x;
} in N $ p ++ (succ q:rest);
-- abandoned, don't know how to write this.
listofsplitfunctions:: [[a] -> ([a],[a])];
listofsplitfunctions = (\l -> ([],l)):
(\(a:x) -> ([a],x)):undefined;
-- unused, but inspiration for listofsplitfunctions
allsplits :: [a] -> [([a],[a])];
allsplits [] = [];
allsplits l@(x:rest) = ([],l):(allsplits rest & map (Bifunctor.first (\z -> x:z)));
logn :: N -> Double;
logn (N x) = zipWith logpow bases x & sum;
logpow :: Ii -> Ii -> Double;
logpow base expo = fromInteger expo * log (fromInteger base);
print1 :: N -> IO ();
print1 n@(N x) = putStrLn $ printf "%.2f" (logn n / log 10) ++ " = log10( " ++ manyzprint x ++ " )";
zprint :: Ii -> Ii -> String;
zprint base expo = show base ++ " ^ " ++ printf "%-2d" expo;
manyzprint :: [Ii] -> String;
manyzprint expos = zipWith zprint bases expos & List.intersperse " * " & concat;
} --end