PMF36FUOINXVVNP5XW6ZOFPPWFBRK62O4VCX45NE4Y4GDPL2Z3BQC 6U24OB4HZGUVZZTOEHQKTBOHGV5FKEE4EQDGJF6IP3QQ3HXNNWUQC ASGE7I4JJ3HTHHSQKVNL74ZVZQ6WTYX6M54FYKOUE4PCPRPUNSUQC V7A5CRSQQY3WTZFNH73E6J4YOKE7YJMEW2XW7YJTM37UNTWRMZJQC VRPOSMITS7VRSIJU6YNEBELCUBTHPMPJD6F6F5PE35R2KECS42KAC VG2W2CCBAB7KY4FSDKB4ZLBRYO763USFRDHUOA3GQOTVNS6XQLSAC XBKUMBMDBZ37XISOWRJU3QRUUYP3TDT7MO6GYKTLCSTRPESASJTQC main = check prop_neg >> check prop_add_id >> check prop_mult_id >> check prop_assoc_add >> check prop_assoc_mul >> check prop_distrib >> check prop_recip >> check prop_invwherecheck prop = doresult <- verboseCheckResult propunless (isSuccess result) exitFailure
main = do success <- runTestsunless success exitFailure
-- | A finite nimber is represented as a sum of distinct 2-powers, each of which is represented as a product of distinct Fermat 2-powers.-- Hence @'Nimber' {'getNimber' = s}@ represents \(\sum\limits_{t \in s} \prod\limits_{n \in t} 2^{2^n}\). This representation makes sums and products easy to calculate.newtype Nimber = Nimber {getNimber :: S.Set (S.Set Nat)}deriving (Show, Eq)nimberToNatural :: Nimber -> Natural-- nimberToInteger = sum . S.map ((^) @_ @Integer 2 . sum . S.map (2^)) . getNimbernimberToNatural = sum . S.map (product . S.map ((^) @_ @Natural 2 . (2 ^))) . getNimber
-- | Nimbers are ordinals, so they are ordered. They also form a field, but they are not an ordered field.instance Ord Nimber wheren `compare` m = nimberToNatural n `compare` nimberToNatural minstance Enum Nimber wheretoEnum = fromInteger . fromIntegralfromEnum = fromIntegral . nimberToNatural
newtype Nimber = Nimber {getNimber :: Natural}deriving newtype (Show, Eq, Ord, Enum, Bits)
x <- S.toList $ getNimber ay <- S.toList $ getNimber blet cs = x `S.intersection` yp = product $ S.map (\c -> Nimber $ S.fromList [S.singleton c, S.fromList [0 .. c - 1]]) csd = Nimber $ S.singleton $ x `delta` y-- (*d) . Nimber . S.singleton <$> S.toList (getNimber p)
x <- twoPowers ay <- twoPowers blet cs = twoPowers $ x .&. yp = product $ fmap (\c -> bit (bit c) + bit (bit c - 1)) csd = bit $ x `xor` y
recip Nimber {getNimber = s} =let m = foldl max 0 $ S.unions s -- D = 2^2^m is the largest Fermat 2-power less than or equal to naD = Nimber $ S.filter (S.member m) s -- n = aD+bb = Nimber $ S.filter (S.notMember m) sa = Nimber $ S.map (S.delete m) $ getNimber aDsemiD = Nimber . S.singleton $ S.fromList [0 .. m - 1] -- semimultiple of D
recip n =let m = floorLog @Int $ floorLog n -- D = 2^2^m is the largest Fermat 2-power less than or equal to na = n `shiftR` bit m -- n = aD+baD = a `shiftL` bit mb = n `xor` aDsemiD = bit (bit m - 1) -- semimultiple of D
mex :: S.Set Int -> Intmex s = if 0 `notElem` s then 0 else 1 + mex (S.map (+ (-1)) s)-- | Compute nimber sum directly from the definition. This is very slow.nimberAdd :: Int -> Int -> IntnimberAdd = (!!) . (nimberSumTable !!)nimberSumTable :: [[Int]]nimberSumTable = fmap add <$> [(i,) <$> [0 ..] | i <- [0 ..]]whereadd (a, b) = mex $ S.fromList [nimberSumTable !! a' !! b | a' <- [0 .. a - 1]] `S.union` S.fromList [nimberSumTable !! a !! b' | b' <- [0 .. b - 1]]-- | Compute nimber product directly from the definition. This is very slow.nimberMul :: Int -> Int -> IntnimberMul = (!!) . (nimberProdTable !!)nimberProdTable :: [[Int]]nimberProdTable = fmap mul <$> [(i,) <$> [0 ..] | i <- [0 ..]]wheremul (a, b) = mex $ S.fromList [(nimberProdTable !! a' !! b) `nimberAdd` (nimberProdTable !! a !! b') `nimberAdd` (nimberProdTable !! a' !! b') | a' <- [0 .. a - 1], b' <- [0 .. b - 1]]