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_inv
where
check prop = do
result <- verboseCheckResult prop
unless (isSuccess result) exitFailure
main = do success <- runTests
unless 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^)) . getNimber
nimberToNatural = 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 where
n `compare` m = nimberToNatural n `compare` nimberToNatural m
instance Enum Nimber where
toEnum = fromInteger . fromIntegral
fromEnum = fromIntegral . nimberToNatural
newtype Nimber = Nimber {getNimber :: Natural}
deriving newtype (Show, Eq, Ord, Enum, Bits)
x <- S.toList $ getNimber a
y <- S.toList $ getNimber b
let cs = x `S.intersection` y
p = product $ S.map (\c -> Nimber $ S.fromList [S.singleton c, S.fromList [0 .. c - 1]]) cs
d = Nimber $ S.singleton $ x `delta` y
-- (*d) . Nimber . S.singleton <$> S.toList (getNimber p)
x <- twoPowers a
y <- twoPowers b
let cs = twoPowers $ x .&. y
p = product $ fmap (\c -> bit (bit c) + bit (bit c - 1)) cs
d = 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 n
aD = Nimber $ S.filter (S.member m) s -- n = aD+b
b = Nimber $ S.filter (S.notMember m) s
a = Nimber $ S.map (S.delete m) $ getNimber aD
semiD = 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 n
a = n `shiftR` bit m -- n = aD+b
aD = a `shiftL` bit m
b = n `xor` aD
semiD = bit (bit m - 1) -- semimultiple of D
mex :: S.Set Int -> Int
mex 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 -> Int
nimberAdd = (!!) . (nimberSumTable !!)
nimberSumTable :: [[Int]]
nimberSumTable = fmap add <$> [(i,) <$> [0 ..] | i <- [0 ..]]
where
add (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 -> Int
nimberMul = (!!) . (nimberProdTable !!)
nimberProdTable :: [[Int]]
nimberProdTable = fmap mul <$> [(i,) <$> [0 ..] | i <- [0 ..]]
where
mul (a, b) = mex $ S.fromList [(nimberProdTable !! a' !! b) `nimberAdd` (nimberProdTable !! a !! b') `nimberAdd` (nimberProdTable !! a' !! b') | a' <- [0 .. a - 1], b' <- [0 .. b - 1]]