1 ! Copyright (c) 2023 Cecilia Knaebchen.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: kernel locals math math.functions ranges sequences
7 ! https://projecteuler.net/problem=508
12 ! Consider the Gaussian integer i-1. A base i-1 representation
13 ! of a Gaussian integer a+bi is a finite sequence of digits
14 ! d(n-1) d(n-2) ... d(1) d(0) such that:
15 ! a+bi = d(n-1) (i-1)^(n-1) + ... + d(1) (i-1) + d(0)
16 ! Each d(k) is in {0,1}
17 ! There are no leading zeros, i.e. d(n-1) != 0, unless a+bi is
20 ! Here are base i-1 representations of a few Gaussian integers:
21 ! 11+24i -> 111010110001101
22 ! 24-11i -> 110010110011
27 ! Remarkably, every Gaussian integer has a unique base i-1
30 ! Define f(a+bi) as the number of 1s in the unique base i-1
31 ! representation of a+bi. For example, f(11+24i) = 9 and
34 ! Define B(L) as the sum of f(a+bi) for all integers a, b such
35 ! that |a| <= L and |b| <= L. For example, B(500) = 10,795,060.
37 ! Find B(10^15) mod 1,000,000,007.
42 ! f(a+bi) = sum of digits in base i-1 representation of a+bi
43 ! Recursion for f(a+bi):
45 ! -> f(a+bi) = f((x-a+b)/2 + (x-a-b)/2 * i) + x
47 MEMO:: fab ( a b -- n )
48 a b [ zero? ] both? [ 0 ] [ a b + 2 rem dup a - dup [ b + 2 / ] [ b - 2 / ] bi* fab + ] if ;
50 ! B(P,Q,R,S) := Sum(a=P..Q, b=R..S, f(a+bi))
51 ! Recursion for B(P,Q,R,S) exists, basically four slightly
52 ! different versions of B(-S/2,-R/2,P/2,Q/2). If summation is
53 ! over fewer than 5000 terms, we just calculate values of f.
55 MEMO:: B ( P Q R S -- n )
58 P Q [a..b] R S [a..b] [ fab ] cartesian-map [ sum ] map-sum
61 S 2 / floor neg R 2 / ceiling neg P 2 / ceiling Q 2 / floor B
62 S 2 / floor neg R 2 / ceiling neg P 1 - 2 / ceiling Q 1 - 2 / floor 4dup [ swap - 1 + ] 2bi@ * [ B ] dip +
63 S 1 - 2 / floor neg R 1 - 2 / ceiling neg P 2 / ceiling Q 2 / floor 4dup [ swap - 1 + ] 2bi@ * 2 * [ B ] dip +
64 S 1 - 2 / floor neg R 1 - 2 / ceiling neg P 1 + 2 / ceiling Q 1 + 2 / floor 4dup [ swap - 1 + ] 2bi@ * [ B ] dip +
68 : euler508 ( -- answer )
69 10 15 ^ dup [ neg ] dip 2dup B 1000000007 mod ;