F2J33IDG7QQEVMUX7EEIWSV43BMGWET3EPTR4CUAZ5PZ7SFXIXFQC ADE3QH6CJXDMZNXCPR7YTFLUHUV6TFP7Z3V7SMKUELP3JVGCACXQC 73K2ULQWAV2A2MICO4YYOVKX55WJYYHBBQSALBZQBKQJLYIDDPGQC VZUEVF2UF4O2S3SFZKSV45QBUMQQ5GVNQMYYFU5PXPFX344P7W3QC CEQIUHRVILZ2JCYTQPEK4LTHSKEZMMS3YXTE3L3ELGMW2RNOH7YQC 3BR6NSGOYSWGFSMQZGMWCCEKGJVLICBXCKCZ454PQ3E46OUVTQ7QC 2MUXDVSCTYM5RSSP47X2PG7WGCDEUFVGTEG3PJ73PMDPIILUZCYAC 26SW6RDYLEFO6NKSH3TAWMVSMMVEM4E44ODI4T54UOI6ZJV7VAPAC 7C7XXN3TSFTJZK4M3BM3T2DJJVB6SCZF7UX42R64ATKVBPIUMWPQC D53SPHGPFSQ2KE2PLBUZVGEIS2SPFSJRQANHEJ6X3XEBUHJAZHYQC VRPOSMITS7VRSIJU6YNEBELCUBTHPMPJD6F6F5PE35R2KECS42KAC PMF36FUOINXVVNP5XW6ZOFPPWFBRK62O4VCX45NE4Y4GDPL2Z3BQC VG2W2CCBAB7KY4FSDKB4ZLBRYO763USFRDHUOA3GQOTVNS6XQLSAC 6U24OB4HZGUVZZTOEHQKTBOHGV5FKEE4EQDGJF6IP3QQ3HXNNWUQC MCBWM3FBZPTBMBJM4QISAX45C2D75QGHWXPLAYMN3EXJJ2CV4ASAC V7A5CRSQQY3WTZFNH73E6J4YOKE7YJMEW2XW7YJTM37UNTWRMZJQC P63H7XZA5FGJP5RTASK5NOWQDV4BSKFFCN4AIS7RW44KUK3LK2JQC 2Q7SZHYMAFCYLG5MROYQ4BJ7HDIASPLIZJP7SM7BNX4GESUCZXJQC TEWWDULPAUYV2VFT3LGUTXGVVDPFFTBRHXQVJAZT63D5Q2NLPS6QC KARMFRM7OJF6RDDAVRISRV223RS6AWE6BG33QMFJ4GYLQFZYL56QC WRKY5TH74QSTDAEKIP7NJO4PJVU2HTTYVY7B5OE7VTSRWEOK2J6AC BKOAQCTL55P6HCIDOLA3WKRDJPPZTFDFROKMKRCLXT7ANA3JG4NQC TF6PK6JDRLBRDRJVI7M4RWSJG5NK6SAMSYINXD6ZGSE7DL4ILTMAC 5CXVNGYMMIBKPFJFJK7T5HLMUAP2IE7UCFZBFZTY5DBJFRQHPP5QC ASGE7I4JJ3HTHHSQKVNL74ZVZQ6WTYX6M54FYKOUE4PCPRPUNSUQC IKAX7QBJIVJZSXXD6I6UBMPEVPEH3QFNEYGWJKQWMV64IV5U664QC XL3UEJW5ODILLWT37VFOTWJG5J4YV4MTODD5BTREEC7ZFSYXQHZQC 2JT446RMTHP23QL57CULGRWI7A7ATV7HRV2D5FZ4SULIDU5AD4KAC 7IBKFG3D35V2UV3JTGM36ZYCF5H3VTLNHAQT24VYKMXQFEJ247IQC WFOLCYCZVNRVCEWSDJR36BGSN6IKJ4CMISYCOWBLENMOP2JUTRIQC L6TQHWB6JUMHD6KZR3RMO23UHQ2E37P2BAFZGDXBL5KWK2WWAIJQC E6Q5N4UCYKSFJ4YYFRPLOUASOIARTLT2ZU2JJXM3TFP4FICDGAOAC O6UAIBEXBNE2XMWQWU6YKGGRLDQFI364JMV6HCYCCI3LBQRD6RPQC M373MXDAPWQZQZT4RWAXFLLUUP5266TW5RUKHB7QNUBUKYQDTOYQC G25ET6HXYNPM2HJ2L76TGOLVV2LBDA3CIWCAN6SL2WKNTUG56RDQC {-# LANGUAGE DerivingStrategies #-}{-# OPTIONS_GHC -Wno-type-defaults #-}-- | In combinatorial game theory, nimbers represent the values of impartial games. They are the simplest way of making the ordinals into a Field.-- See /On Numbers and Games/ by John Conway.---- Nimber addition is defined by \(\alpha+\beta = \operatorname{mex}\{\alpha'+\beta, \alpha+\beta'\}\), where \(\operatorname{mex} S\) is the smallest ordinal not in \(S\).---- Nimber multiplication is defined by \(\alpha\cdot\beta = \operatorname{mex}\{\alpha'\cdot\beta + \alpha\cdot\beta' - \alpha'\cdot\beta'\}\).---- This module implements /finite/ nimbers, which form the smallest quadratically closed field of characteristic 2.module Data.Nimber( Nimber (..),floorLog,sqr,pow,artinSchreierRoot,solveQuadratic,)whereimport Data.Bitsimport Numeric.Naturalnewtype Nimber = Nimber {getNimber :: Natural}deriving newtype (Show, Eq, Ord, Enum, Bits)-- | Index of highest-order set bit, or -1 if there are none.floorLog :: (Bits a, Num b) => a -> bfloorLog n| n == zeroBits = -1| otherwise = 1 + floorLog (n .>>. 1)mult' :: Int -> Nimber -> Nimber -> Nimbermult' _ 0 _ = 0mult' _ _ 0 = 0mult' _ 1 b = bmult' _ a 1 = amult' m a b =let semiD = bit (bit m - 1) -- semimultiple of Ds1 = a .>>. bit m -- a = a1D+a2s2 = a .^. (s1 .<<. bit m)t1 = b .>>. bit m -- b = b1D+b2t2 = b .^. (t1 .<<. bit m)c = mult' (m - 1) s2 t2in ((mult' (m - 1) (s1 + s2) (t1 + t2) - c) .<<. bit m) + mult' (m - 1) (mult' (m - 1) s1 t1) semiD + c-- | Finite nimber addition is calculated as follows: the nimber sum of a two-power and itself is 0, while the nimber sum of a set of distinct two-powers is their ordinary sum.---- Finite nimber multiplication is calculated as follows: the nimber square of a Fermat two-power is its sesquimultiple, while the nimber product of a set of distinct Fermat two-powers is their ordinary product.-- The sesquimultiple of a Fermat two-power is equal to itself plus the product of all smaller Fermat two-powers.instance Num Nimber wherefromInteger = Nimber . fromIntegral . abs(+) = xor(-) = xora * b =let m = max (floorLog @Int (floorLog a)) (floorLog @Int (floorLog b)) -- D = 2^2^m is the largest Fermat 2-power less than or equal to both a and bin mult' m a bnegate = idabs = idsignum 0 = 0signum _ = 1sqr' :: Int -> Nimber -> Nimbersqr' _ 0 = 0sqr' _ 1 = 1sqr' m n =let a = n .>>. bit m -- n = aD+baD = a .<<. bit mb = n .^. aDsemiD = bit (bit m - 1) -- semimultiple of Da2 = sqr' (m - 1) a -- a^2in a2 .<<. bit m + mult' (m - 1) a2 semiD + sqr' (m - 1) b-- | Squaring function. Faster than multiplying @n@ by itself.sqr :: Nimber -> Nimbersqr n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nin sqr' m n
module Data.Nimber (module M) where
-- | Raise a @'Nimber'@ to an integral power. Faster than using '^' or '^^'.pow :: (Integral a, Bits a) => Nimber -> a -> Nimberx `pow` n| n < 0 = recip x `pow` negate n| otherwise =let m = floorLog @Int $ floorLog xin (foldr (mult' m . snd) 1 . filter (testBit n . fst) . zip [0 ..] . take (1 + floorLog (n + 1))) $ iterate (sqr' m) x-- | The finite nimbers are a field of characteristic 2. There is no field homomorphism from the rationals to the nimbers, so @'fromRational'@ is always an error.instance Fractional Nimber wherefromRational _ = error "Cannot map from field of characteristic 0 to characteristic 2"recip n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nrecip' _ 0 = error "Divide by zero"recip' _ 1 = 1recip' k l =let a = l .>>. bit k -- n = aD+baD = a .<<. bit kb = l .^. aDsemiD = bit (bit k - 1) -- semimultiple of Din mult' k (l + a) $ recip' (k - 1) (mult' (k - 1) semiD (sqr' (k - 1) a) + mult' (k - 1) b (a + b))in recip' m n-- | The only reason this instance exists is to define square roots. None of the other @'Floating'@ methods apply to @'Nimber'@s.instance Floating Nimber wheresqrt n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nsqrt' _ 0 = 0sqrt' _ 1 = 1sqrt' k l =let a = l .>>. bit k -- n = aD+baD = a .<<. bit kb = l .^. aDsemiD = bit $ bit k - 1 -- semimultiple of Dsqrta = sqrt' (k - 1) ain sqrta .<<. bit k + mult' (k - 1) sqrta (sqrt' (k - 1) semiD) + sqrt' (k - 1) bin sqrt' m npi = error "π is not a nimber"exp _ = error "exp undefined for nimbers"log _ = error "log undefined for nimbers"sin _ = error "Trigonometric functions undefined for nimbers"cos _ = error "Trigonometric functions undefined for nimbers"tan _ = error "Trigonometric functions undefined for nimbers"asin _ = error "Trigonometric functions undefined for nimbers"acos _ = error "Trigonometric functions undefined for nimbers"atan _ = error "Trigonometric functions undefined for nimbers"sinh _ = error "Hyperbolic functions undefined for nimbers"cosh _ = error "Hyperbolic functions undefined for nimbers"tanh _ = error "Hyperbolic functions undefined for nimbers"asinh _ = error "Hyperbolic functions undefined for nimbers"acosh _ = error "Hyperbolic functions undefined for nimbers"atanh _ = error "Hyperbolic functions undefined for nimbers"evens :: [a] -> [a]evens (x : _ : xs) = x : evens xsevens xs = xs-- | @'artinSchreierRoot' n@ is the smallest solution to the equation \(x^2 - x = n\).-- The algorithm is due to Chin-Long Chen: <https://ieeexplore.ieee.org/document/1056557>.-- In fields of characteristic 2, the standard quadratic formula does not work, but any quadratic equation can be solved using square roots and Artin-Schreier roots.---- This function is __much__ slower than @'sqrt'@.artinSchreierRoot :: Nimber -> NimberartinSchreierRoot 0 = 0artinSchreierRoot 1 = 2artinSchreierRoot 2 = 4artinSchreierRoot 3 = 6artinSchreierRoot n =let m = 1 + floorLog @Int (floorLog n) -- 2^2^m is the order of the smallest field containing nm' = if n < bit (bit m - 1) then m else m + 1 -- 2^2^m' is the order of the smallest field containing the Artin-Schreier root of nsquares = iterate (sqr' m) nquarts = evens squaressemiTrace = sum $ take (bit (m' - 1)) quarts -- trace of the Artin-Screier root of nin if semiTrace == 1thenlet s = sum $ doj <- [1 .. bit (m' - 2) - 1]i <- [j .. bit (m' - 2) - 1]pure $ mult' m' (squares !! (shiftL i 1 - 1 .^. bit (m' - 1))) (squares !! (shiftL j 1 - 2))in flip clearBit 0 $s+ sqr s+ mult' m(squares !! (bit m' - 1))(1 + sum (take (bit (m' - 2)) $ drop (bit (m' - 2)) quarts))elselet y = bit $ bit m' - 1z = artinSchreierRoot $ sqr y + y + nin y + z-- | @'solveQuadratic' p q@ returns the solutions to the equation \(X^2 + px + q = 0\).solveQuadratic :: Nimber -> Nimber -> (Nimber, Nimber)solveQuadratic 0 q = (sqrt q, sqrt q)solveQuadratic p q = let x = p * artinSchreierRoot (q / sqr p) in (min x $ x + p, max x $ x + p)
import Data.Nimber.Finite as M
{-# LANGUAGE DerivingStrategies #-}{-# OPTIONS_GHC -Wno-type-defaults #-}-- | In combinatorial game theory, nimbers represent the values of impartial games. They are the simplest way of making the ordinals into a Field.-- See /On Numbers and Games/ by John Conway.---- Nimber addition is defined by \(\alpha+\beta = \operatorname{mex}\{\alpha'+\beta, \alpha+\beta'\}\), where \(\operatorname{mex} S\) is the smallest ordinal not in \(S\).---- Nimber multiplication is defined by \(\alpha\cdot\beta = \operatorname{mex}\{\alpha'\cdot\beta + \alpha\cdot\beta' - \alpha'\cdot\beta'\}\).---- This module implements /finite/ nimbers, which form the smallest quadratically closed field of characteristic 2.module Data.Nimber.Finite( FiniteNimber (..),floorLog,sqr,pow,artinSchreierRoot,solveQuadratic,)whereimport Data.Bitsimport Numeric.Naturalnewtype FiniteNimber = FiniteNimber {getFiniteNimber :: Natural}deriving newtype (Show, Eq, Ord, Enum, Bits)-- | Index of highest-order set bit, or -1 if there are none.floorLog :: (Bits a, Num b) => a -> bfloorLog n| n == zeroBits = -1| otherwise = 1 + floorLog (n .>>. 1)mult' :: Int -> FiniteNimber -> FiniteNimber -> FiniteNimbermult' _ 0 _ = 0mult' _ _ 0 = 0mult' _ 1 b = bmult' _ a 1 = amult' m a b =let semiD = bit (bit m - 1) -- semimultiple of Ds1 = a .>>. bit m -- a = a1D+a2s2 = a .^. (s1 .<<. bit m)t1 = b .>>. bit m -- b = b1D+b2t2 = b .^. (t1 .<<. bit m)c = mult' (m - 1) s2 t2in ((mult' (m - 1) (s1 + s2) (t1 + t2) - c) .<<. bit m) + mult' (m - 1) (mult' (m - 1) s1 t1) semiD + c-- | Finite nimber addition is calculated as follows: the nimber sum of a two-power and itself is 0, while the nimber sum of a set of distinct two-powers is their ordinary sum.---- Finite nimber multiplication is calculated as follows: the nimber square of a Fermat two-power is its sesquimultiple, while the nimber product of a set of distinct Fermat two-powers is their ordinary product.-- The sesquimultiple of a Fermat two-power is equal to itself plus the product of all smaller Fermat two-powers.instance Num FiniteNimber wherefromInteger = FiniteNimber . fromIntegral . abs(+) = xor(-) = xora * b =let m = max (floorLog @Int (floorLog a)) (floorLog @Int (floorLog b)) -- D = 2^2^m is the largest Fermat 2-power less than or equal to both a and bin mult' m a bnegate = idabs = idsignum 0 = 0signum _ = 1sqr' :: Int -> FiniteNimber -> FiniteNimbersqr' _ 0 = 0sqr' _ 1 = 1sqr' m n =let a = n .>>. bit m -- n = aD+baD = a .<<. bit mb = n .^. aDsemiD = bit (bit m - 1) -- semimultiple of Da2 = sqr' (m - 1) a -- a^2in a2 .<<. bit m + mult' (m - 1) a2 semiD + sqr' (m - 1) b-- | Squaring function. Faster than multiplying @n@ by itself.sqr :: FiniteNimber -> FiniteNimbersqr n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nin sqr' m n-- | Raise a @'FiniteNimber'@ to an integral power. Faster than using '^' or '^^'.pow :: (Integral a, Bits a) => FiniteNimber -> a -> FiniteNimberx `pow` n| n < 0 = recip x `pow` negate n| otherwise =let m = floorLog @Int $ floorLog xin (foldr (mult' m . snd) 1 . filter (testBit n . fst) . zip [0 ..] . take (1 + floorLog (n + 1))) $ iterate (sqr' m) x-- | The finite nimbers are a field of characteristic 2. There is no field homomorphism from the rationals to the nimbers, so @'fromRational'@ is always an error.instance Fractional FiniteNimber wherefromRational _ = error "Cannot map from field of characteristic 0 to characteristic 2"recip n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nrecip' _ 0 = error "Divide by zero"recip' _ 1 = 1recip' k l =let a = l .>>. bit k -- n = aD+baD = a .<<. bit kb = l .^. aDsemiD = bit (bit k - 1) -- semimultiple of Din mult' k (l + a) $ recip' (k - 1) (mult' (k - 1) semiD (sqr' (k - 1) a) + mult' (k - 1) b (a + b))in recip' m n-- | The only reason this instance exists is to define square roots. None of the other @'Floating'@ methods apply to @'FiniteNimber'@s.instance Floating FiniteNimber wheresqrt n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to nsqrt' _ 0 = 0sqrt' _ 1 = 1sqrt' k l =let a = l .>>. bit k -- n = aD+baD = a .<<. bit kb = l .^. aDsemiD = bit $ bit k - 1 -- semimultiple of Dsqrta = sqrt' (k - 1) ain sqrta .<<. bit k + mult' (k - 1) sqrta (sqrt' (k - 1) semiD) + sqrt' (k - 1) bin sqrt' m npi = error "π is not a nimber"exp _ = error "exp undefined for nimbers"log _ = error "log undefined for nimbers"sin _ = error "Trigonometric functions undefined for nimbers"cos _ = error "Trigonometric functions undefined for nimbers"tan _ = error "Trigonometric functions undefined for nimbers"asin _ = error "Trigonometric functions undefined for nimbers"acos _ = error "Trigonometric functions undefined for nimbers"atan _ = error "Trigonometric functions undefined for nimbers"sinh _ = error "Hyperbolic functions undefined for nimbers"cosh _ = error "Hyperbolic functions undefined for nimbers"tanh _ = error "Hyperbolic functions undefined for nimbers"asinh _ = error "Hyperbolic functions undefined for nimbers"acosh _ = error "Hyperbolic functions undefined for nimbers"atanh _ = error "Hyperbolic functions undefined for nimbers"evens :: [a] -> [a]evens (x : _ : xs) = x : evens xsevens xs = xs-- | @'artinSchreierRoot' n@ is the smallest solution to the equation \(x^2 - x = n\).-- The algorithm is due to Chin-Long Chen: <https://ieeexplore.ieee.org/document/1056557>.-- In fields of characteristic 2, the standard quadratic formula does not work, but any quadratic equation can be solved using square roots and Artin-Schreier roots.---- This function is __much__ slower than @'sqrt'@.artinSchreierRoot :: FiniteNimber -> FiniteNimberartinSchreierRoot 0 = 0artinSchreierRoot 1 = 2artinSchreierRoot 2 = 4artinSchreierRoot 3 = 6artinSchreierRoot n =let m = 1 + floorLog @Int (floorLog n) -- 2^2^m is the order of the smallest field containing nm' = if n < bit (bit m - 1) then m else m + 1 -- 2^2^m' is the order of the smallest field containing the Artin-Schreier root of nsquares = iterate (sqr' m) nquarts = evens squaressemiTrace = sum $ take (bit (m' - 1)) quarts -- trace of the Artin-Screier root of nin if semiTrace == 1thenlet s = sum $ doj <- [1 .. bit (m' - 2) - 1]i <- [j .. bit (m' - 2) - 1]pure $ mult' m' (squares !! (shiftL i 1 - 1 .^. bit (m' - 1))) (squares !! (shiftL j 1 - 2))in flip clearBit 0 $s+ sqr s+ mult' m(squares !! (bit m' - 1))(1 + sum (take (bit (m' - 2)) $ drop (bit (m' - 2)) quarts))elselet y = bit $ bit m' - 1z = artinSchreierRoot $ sqr y + y + nin y + z-- | @'solveQuadratic' p q@ returns the solutions to the equation \(X^2 + px + q = 0\).solveQuadratic :: FiniteNimber -> FiniteNimber -> (FiniteNimber, FiniteNimber)solveQuadratic 0 q = (sqrt q, sqrt q)solveQuadratic p q = let x = p * artinSchreierRoot (q / sqr p) in (min x $ x + p, max x $ x + p)