Seumur Hidup Cacing

28

Ketentuan

Sebuah worm adalah setiap daftar bilangan bulat non-negatif, dan yang paling kanan (yaitu, terakhir ) elemen disebut kepala . Jika kepala bukan 0, cacing memiliki segmen aktif yang terdiri dari blok elemen yang paling panjang yang berdekatan yang mencakup kepala dan memiliki semua elemennya setidaknya sebesar kepala . The segmen aktif berkurang adalah segmen aktif dengan kepala dikurangi dengan 1. Sebagai contoh, worm 3 1 2 3 2memiliki segmen aktif 2 3 2, dan segmen aktif berkurang adalah 2 3 1.

Aturan evolusi

Cacing berevolusi langkah demi langkah sebagai berikut:

Pada langkah t (= 1, 2, 3, ...),
    jika head adalah 0: hapus head yang
    lain: ganti segmen aktif dengan t + 1 salinan gabungan dari segmen aktif tereduksi.

Fakta : Cacing apa pun akhirnya berevolusi menjadi daftar kosong , dan jumlah langkah untuk melakukannya adalah masa hidup cacing itu .

(Detail dapat ditemukan dalam The Worm Principle , sebuah makalah oleh LD Beklemishev. Penggunaan "daftar" berarti urutan yang terbatas, dan "kepala" yang berarti elemen terakhirnya , diambil dari makalah ini - tidak boleh dikacaukan dengan penggunaan umum untuk daftar sebagai tipe data abstrak , di mana head biasanya berarti elemen pertama .)

Contoh (segmen aktif dalam tanda kurung)

Cacing: 0,1

step    worm
         0(1)
1        0 0 0
2        0 0 
3        0
4           <- lifetime = 4

Cacing: 1,0

step    worm
         1 0
1       (1)
2        0 0 0
3        0 0 
4        0
5           <- lifetime = 5

Cacing: 1,1

step    worm
        (1 1)
1        1 0 1 0 
2        1 0(1) 
3        1 0 0 0 0 0
4        1 0 0 0 0
5        1 0 0 0
...
8       (1) 
9        0 0 0 0 0 0 0 0 0 0
10       0 0 0 0 0 0 0 0 0
...
18       0
19           <- lifetime = 19

Cacing: 2

step    worm
        (2)
1       (1 1)
2        1 0 1 0 1 0
3        1 0 1 0(1)
4        1 0 1 0 0 0 0 0 0
5        1 0 1 0 0 0 0 0
6        1 0 1 0 0 0 0
...
10       1 0(1)
11       1 0 0 0 0 0 0 0 0 0 0 0 0 0
12       1 0 0 0 0 0 0 0 0 0 0 0 0
...
24      (1)
25       0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
50       0
51          <- lifetime = 51

Cacing: 2,1

        (2 1)
1        2 0 2 0
2        2 0(2)
3        2 0(1 1 1 1)
4        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0
5        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0(1 1 1)
6        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
7        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0(1 1)
8        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0{1 0}^9
...
??          <- lifetime = ??      

Cacing: 3

step    worm
        (3)
1       (2 2)
2       (2 1 2 1 2 1)
3        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 
4        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1(2)
5        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0(2 1 2 1 1 1 1 1 1 1)
6        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^7
7        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^6 (2 1 2 1 1 1 1 1 1) 
...      ...
??          <- lifetime = ??


Ke samping

Umur cacing biasanya sangat besar, seperti yang ditunjukkan oleh batas bawah berikut dalam hal hierarki standar fungsi yang tumbuh cepat f α :

worm                lower bound on lifetime
----------------    ------------------------------------------
11..10 (k 1s)       f_k(2)
2                   f_ω(2)
211..1 (k 1s)       f_(ω+k)(2)
2121..212 (k 2s)    f_(ωk)(2)
22..2 (k 2s)        f_(ω^k)(2)
3                   f_(ω^ω)(2)
...
n                   f_(ω^ω^..^ω)(2) (n-1 ωs)  >  f_(ε_0) (n-1)

Hebatnya, worm [3] sudah memiliki masa hidup yang jauh melebihi jumlah Graham , G:

f ω ω (2) = f ω 2 (2) = f ω2 (2) = f ω + 2 (2) = f ω + 1 (f ω + 1 (2)) >> f ω + 1 (64) > G.


Tantangan Golf Code

Tulis subprogram fungsi sesingkat mungkin dengan perilaku berikut:

Input : Cacing apa saja.
Output : Masa hidup worm.

Ukuran kode diukur dalam byte.


Berikut ini sebuah contoh (Python, golf hingga sekitar 167 byte):

from itertools import *
def T(w):
    w=w[::-1]
    t=0
    while w:
        t+=1
        if w[0]:a=list(takewhile(lambda e:e>=w[0],w));a[0]-=1;w=a*(t+1)+w[len(a):]
        else:w=w[1:]
    return t


NB : Jika t (n) adalah masa hidup cacing [n], maka laju pertumbuhan t (n) kira-kira sama dengan fungsi Goodstein . Jadi, jika ini bisa di-golf hingga di bawah 100 byte, itu bisa juga memberikan jawaban yang menang untuk pertanyaan Jumlah Cetak Terbesar . (Untuk jawaban itu, laju pertumbuhan bisa sangat dipercepat dengan selalu memulai penghitung langkah di n - nilai yang sama dengan worm [n] - alih-alih memulainya di 0.)

res
sumber
Saya bingung dengan kode Anda. Anda mengatakan kepala adalah elemen paling kanan , tetapi dalam contoh Python Anda, Anda memperlakukan kepala sebagai w[0]yang merupakan elemen paling kiri dari daftar itu?
@LegoStormtroopr Jika Anda dapat mempertimbangkan daftar memiliki kiri dan kanan. Jika Anda hanya mempertimbangkan yang pertama dan terakhir, Anda bisa memetakan yang paling kanan ke yang pertama atau terakhir ketika membaca string awal - yang bukan bagian dari pertanyaan. Tetapi input fungsi juga tidak didefinisikan secara ketat.
Bob
@LegoStormtroopr - Tangkapan bagus; Saya mengoreksi kode dengan menambahkan baris untuk membalikkan worm input, yang kepalanya memang seharusnya di sebelah kanan (yaitu elemen terakhir dalam daftar w). Ini untuk efisiensi bahwa program beroperasi pada worm terbalik.
res
Mendapatkan benar jawaban untuk 2 1mungkin terlalu banyak untuk meminta dalam waktu yang wajar, tetapi tes yang berguna adalah bahwa urutan harus mulai (2 1), 2 0 2 0, 2 0 (2), 2 0 (1 1 1 1), ...
Peter Taylor
1
@ThePlasmaRailgun - Mengutip Harvey Friedman, angka yang berasal dari fungsi di level ε_0 dalam hierarki yang tumbuh cepat (seperti masa cacing) benar - benar TIDAK DAPAT DIANDINGKAN dibandingkan dengan TREE (3) .
res

Jawaban:

15

GolfScript ( 56 54 karakter)

{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L;

Demo online

Saya pikir trik utama di sini adalah menjaga agar worm tetap dalam urutan terbalik. Itu berarti cukup kompak untuk menemukan panjang segmen aktif: .0+.({<}+??(di mana 0ditambahkan sebagai pelindung untuk memastikan bahwa kami menemukan elemen yang lebih kecil dari kepala).


Sebagai tambahan, beberapa analisis umur cacing. Saya akan menunjukkan cacing sebagai age, head tail(yaitu dalam urutan terbalik dari notasi pertanyaan ini) menggunakan eksponen untuk menunjukkan pengulangan di kepala dan ekor: misalnya 2^3adalah 2 2 2.

Lemma : untuk setiap segmen aktif xs, ada fungsi f_xssedemikian rupa sehingga age, xs 0 tailberubah menjadi f_xs(age), tail.

Bukti: tidak ada segmen aktif yang dapat mengandung a 0, jadi usia pada saat kita menghapus semuanya sebelum ekor tidak tergantung pada ekor dan karenanya hanya berfungsi sebagai xs.

Lemma : untuk setiap segmen aktif xs, cacing age, xsmati pada usia f_xs(age) - 1.

Bukti: oleh lemma sebelumnya, age, xs 0berubah menjadi f_xs(age), []. Langkah terakhir adalah penghapusan itu 0, yang tidak tersentuh sebelumnya karena tidak pernah dapat membentuk bagian dari segmen aktif.

Dengan kedua lemmata itu, kita dapat mempelajari beberapa segmen aktif sederhana.

Untuk n > 0,

age, 1^n 0 xs -> age+1, (0 1^{n-1})^{age+1} 0 xs
              == age+1, 0 (1^{n-1} 0)^{age+1} xs
              -> age+2, (1^{n-1} 0)^{age+1} xs
              -> f_{1^{n-1}}^{age+1}(age+2), xs

jadi f_{1^n} = x -> f_{1^{n-1}}^{x+1}(x+2)(dengan case dasar f_{[]} = x -> x+1, atau jika Anda suka f_{1} = x -> 2x+3). Kita melihat bahwa di f_{1^n}(x) ~ A(n+1, x)mana Afungsi Ackermann – Péter.

age, 2 0 xs -> age+1, 1^{age+1} 0 xs
            -> f_{1^{age+1}}(age+1)

Itu cukup untuk menangani 1 2( 2 1dalam notasi pertanyaan):

1, 1 2 -> 2, 0 2 0 2
       -> 3, 2 0 2
       -> f_{1^4}(4), 2
       -> f_{1^{f_{1^4}(4)+1}}(f_{1^4}(4)+1) - 1, []

Jadi diberi input, 2 1kita mengharapkan output ~ A(A(5,4), A(5,4)).

1, 3 -> 2, 2 2
     -> 3, 1 2 1 2 1 2
     -> 4, 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> 5, 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> f_{21212}^4(5) - 1

age, 2 1 2 1 2 -> age+1, (1 1 2 1 2)^{age+1}
               -> age+2, 0 1 2 1 2 (1 1 2 1 2)^age
               -> age+3, 1 2 1 2 (1 1 2 1 2)^age

dan saya benar-benar dapat mulai memahami mengapa fungsi ini tumbuh begitu gila.

Peter Taylor
sumber
Sangat keren. Saya pikir program ini juga akan memberikan jawaban yang menang untuk program terminasi terpendek yang ukuran outputnya melebihi jumlah Graham . (Pemenang saat ini ada 63 byte kode Haskell.) Misalnya, pada 55 byte, sesuatu seperti (karena saya rentan terhadap kesalahan sintaksis) 9{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L~menghitung umur worm [9], yang jauh melebihi jumlah Graham - dan dapat menjadi bermain golf lebih lanjut.
res
9

GolfScript, 69 62 karakter

{0:?~%{(.{[(]{:^0=2$0+0=<}{\(@\+}/}{,:^}if;^?):?)*\+.}do;?}:C;

Fungsi Cmengharapkan worm pada stack dan menggantinya dengan hasilnya.

Contoh:

> [1 1]
19

> [2]
51

> [1 1 0]
51
Howard
sumber
Fantastis! Tentunya Anda dapat sedikit memodifikasi ini untuk juga memberikan pemenang yang pasti untuk pertanyaan "Angka Terbesar yang Dapat Dicetak" .
res
Saya tidak melihat Anda memposting apa pun di sana, jadi saya melanjutkan dan memposting modifikasi kode ini sebagai apa yang saya yakini sebagai jawaban yang menang sejauh ini - dengan anggapan bahwa *dan ^tidak digunakan sebagai operator aritmatika penggandaan dan secara eksponensial. Tentu saja, jika Anda ingin mengirimkan jawaban Anda sendiri (tanpa diragukan lagi superior) di sana, saya akan dengan senang hati menghapus jawaban saya.
res
7

Ruby - 131 karakter

Saya tahu ini tidak dapat bersaing dengan solusi GolfScript di atas dan saya cukup yakin bahwa ini dapat mengurangi skor atau lebih banyak karakter, tapi jujur ​​saya senang bisa menyelesaikan masalah yang ungolfed. Teka-teki yang bagus!

f=->w{t=0;w.reverse!;until w==[];t+=1;if w[0]<1;w.shift;else;h=w.take_while{|x|x>=w[0]};h[0]-=1;w.shift h.size;w=h*t+h+w;end;end;t}

Solusi pra-golf saya yang darinya berasal di atas:

def life_time(worm)
  step = 0
  worm.reverse!
  until worm.empty?
    step += 1
    if worm.first == 0
      worm.shift
    else
      head = worm.take_while{ |x| x >= worm.first }
      head[0] -= 1
      worm.shift(head.size)
      worm = head * (step + 1) + worm
    end
  end
  step
end
OI
sumber
Tip umum: banyak masalah golf yang bekerja pada bilangan bulat non-negatif, dalam hal if foo==0ini dapat dipangkas if foo<1. Itu bisa menyelamatkanmu satu char di sini.
Peter Taylor
Kebetulan, saya merasa menakjubkan bahwa ini bekerja tanpa sedetik pun reverse.
Peter Taylor
Ah, tidak. Ini hanya berfungsi pada kasus uji karena mereka hanya memiliki segmen aktif palindromik.
Peter Taylor
Terima kasih atas tip golfnya, @PeterTaylor. Juga, tangkapan yang bagus pada bagian kedua yang hilang. Saya telah menambahkannya. Saya akan mencoba menulis ulang dengan cara lain tanpa menggunakan reverse nanti. Saya cukup yakin saya bisa mendapatkan elseklausa ke satu baris dan kemudian menukar dengan if..else..endpernyataan ternary. Saya juga bisa menggunakan lambda untuk menyimpan beberapa karakter, saya pikir.
OI
6

Sclipting (43 karakter)

글坼가⑴감套擘終長①加⒈丟倘⓶增⓶가采⓶擘❷小終⓷丟❶長貶❷가掊貶插①增復合감不가終終

Ini mengharapkan input sebagai daftar yang dipisahkan oleh ruang. Ini menghasilkan jawaban yang benar untuk 1 1dan 2, tetapi untuk 2 1atau 3terlalu lama jadi saya menyerah menunggu sampai selesai.

Dengan komentar:

글坼 | split at spaces
가⑴ | iteration count = 0

감套 | while:
  擘終長①加⒈丟 | remove zeros from end and add to iteration count
  倘 | if the list is not empty:
    ⓶增⓶ | increment iteration count
    가采⓶擘❷小終⓷丟 | separate out active segment
    ❶長貶❷가掊貶插 | compute reduced active segment
    ①增復合 | repeat reduced active segment and concat
    감 | continue while loop
  不 | else
    가 | stop while loop
  終 | end if
終 | end while
Timwi
sumber
2
Tautan ke juru bahasa akan berguna ... Juga, 86 byte, menggunakan UTF-16?
Peter Taylor
@PeterTaylor: Terima kasih, menambahkan tautan ke penerjemah ke artikel. Dan ya, 43 karakter BMP menerjemahkan ke 86 byte di UTF-16.
Timwi
5

k (83)

worm:{-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;|,/x)}

ini mungkin bisa di-pegolf lebih jauh, karena hanya mengimplementasikan perulangan dengan cukup mudah.

fungsi evolusi dasar {x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y},, adalah 65 karakter, dan menggunakan beberapa trik untuk berhenti menambah usia ketika worm mati. pembungkus memaksa input bilangan bulat tunggal ke daftar, membalikkan input (lebih pendek untuk menulis pengulangan dalam hal worm dibalik dari notasi Anda), meminta fixpoint, memilih usia sebagai output, dan menyesuaikan hasilnya untuk memperhitungkan overshoot dalam generasi terakhir.

jika saya melakukan pemaksaan dan pembalikan secara manual, itu turun menjadi 80 ( {-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;x)}).

beberapa contoh:

  worm 1 1 0
51
  worm 2
51
  worm 1 1
19

sayangnya, itu mungkin tidak banyak digunakan untuk Largest Number Printable , kecuali dalam arti yang sangat teoretis, karena sangat lambat, terbatas pada bilangan bulat 64-bit, dan mungkin tidak terlalu hemat memori.

khususnya, worm 2 1dan worm 3hanya churn (dan mungkin akan membuang 'wsfull(kehabisan memori) jika saya membiarkan mereka terus berjalan).

Aaron Davies
sumber
Saya sudah mencoba menjalankan program Anda dengan penerjemah online ini , tetapi tidak menunjukkan keluaran apa pun. (Mengirimkan file teks dengan ekstensi .k seharusnya memanggil penerjemah K.) Apakah Anda tahu apa yang mungkin dilakukan untuk mengirim output ke stdout?
res
Sepertinya itu menjalankan kona, klon open-source dari k3. Kode saya ditulis dalam k4, dan tidak mungkin kompatibel dengan k3. Anda bisa mendapatkan salinan q / k4 gratis terbatas waktu di kx.com/software-download.php ; setelah Anda memilikinya, mulai REPL, ketik ` to switch from q` untuk k, dan tempel kode saya. Atau, Anda dapat menyimpan kode saya dalam file dengan .kekstensi dan memuatnya ke juru bahasa.
Aaron Davies
2

APL (Dyalog Unicode) , 52 byte SBCS

Disimpan 7 byte berkat @ngn dan @ Adám.

0{⍬≡⍵:⍺⋄n←⍺+10=⊃⍵:n1↓⍵⋄n∇∊(⊂1n/-∘1@1¨)@1⊆∘⍵⍳⍨⌊\⍵}⌽

Cobalah online!

Penjelasan:

0{...}⌽     A monadic function train. We define a recursive function with two
            arguments: zero (our counter), and the reverse of our input
⍬≡⍵:⍺       Our base case - if our input is an empty list, return our counter
n←⍺+1       Define 'n' as our counter plus 1
0=⊃⍵:n1↓⍵  If the first element of the input is zero, recurse with the tail
            of our input and n
\⍵         Minimum-expand: creates a new list from our input where each element
            is the incremental minimum     
⍳⍨          Applies above to both sides of the index-of function. Index-of returns
            the index of the first occurence of each element in the left-side list.
            At this point, a (reversed) input list of [3 4 5 2 3 4] would result
            in [1 1 1 4 4 4]
⊆∘⍵         Partition, composed with our input. Partition creates sublists of the
            right input whenever the integer list in the left input increases.
            This means we now have a list of sub-lists, with the first element
            being the worm's active segment.
(...)@1    ⍝ Take the active segment and apply the following function train...
-∘1@1¨     ⍝ Subtract 1 from the first element of the active segment
1n/        ⍝ Replicate the resultant list above n+1 times
⊂          ⍝ Enclose the above, so as to keep the original shape of our sub-array
∊          ⍝ Enlist everything above together - this recursively concatenates our
           ⍝ new active segment with the remainder of the list
n∇         ⍝ Recurse with the above and n
voidhawk
sumber
Saya pikir APL akan memiliki solusi yang sangat bersih untuk ini, bukankah itu bahasa berbasis array?
ThePlasmaRailgun
1

Scala, 198

type A=List[Int]
def T(w:A)={def s(i:Int,l:A):Stream[A]=l match{case f::r=>l#::s(i+1,if(f<1)r
else{val(h,t)=l.span(_>=l(0));List.fill(i)(h(0)-1::h.tail).flatten++t})
case _=>Stream()};s(2,w).length}

Pemakaian:

scala> T(List(2))
res0: Int = 51
ValarDohaeris
sumber
1

K, 95

{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}

.

k)worm:{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}
k)worm 2
51
k)worm 1 1
19
q)worm 1 1 0 0 0 0
635
tmartin
sumber
1

C (gcc) , 396 byte

#define K malloc(8)
typedef*n;E(n e,n o){n s=K,t=s;for(*s=*o;o=o[1];*t=*o)t=t[1]=K;t[1]=e;e=s;}main(c,f,l,j,a)n*f;{n w=K,x=w;for(;l=--c;x=x[1]=K)*x=atoi(f[c]);for(;w&&++l;)if(*w){n v=K,z=v,u=w,t=K;for(a=*v=*w;(u=u[1])&&*u>=*w;*z=*u)z=z[1]=K;for(x=v[1],v=K,*v=a-1,1[u=v]=x;u;u=u[1])w=w[1];for(j=~l;j++;)u=t=E(t,v);for(;(u=u[1])&&(x=u[1])&&x[1];);u[1]=0;w=w?E(w,t):t;}else w=w[1];printf("%d",--l);}

Cobalah online!

Saya tahu saya sangat terlambat ke pesta, tapi saya pikir saya akan mencoba ini di C, yang membutuhkan implementasi daftar terkait. Ini tidak benar-benar golf sama sekali selain mengubah semua pengidentifikasi menjadi karakter tunggal, tetapi berfungsi!

Semua dalam semua, saya cukup senang mengingat ini adalah program ke-3 C / C ++ yang pernah saya tulis.

ThePlasmaRailgun
sumber
Apakah Anda benar-benar membutuhkan daftar tertaut? Mengapa tidak mengalokasikan array saja? Karena ini adalah golf kode, Anda bahkan tidak perlu repot membebaskannya ketika sudah selesai. Anda bahkan mungkin dapat menemukan cara untuk menyimpannya di tumpukan panggilan (tidak yakin).
dfeuer
Juga, Anda tidak perlu fungsi utama. Tulis saja fungsi yang menggunakan worm sebagai argumen dan mengembalikan umurnya. Worm dapat berupa array dan panjangnya, atau mungkin array yang diakhiri dengan angka negatif.
dfeuer
1

Haskell , 84 byte

(0!).reverse
n!(x:y)|x<1=(n+1)!y|(a,b)<-span(>=x)y=(n+1)!(([-1..n]*>x-1:a)++b)
n!_=n

Cobalah online!

Berkat @xnor untuk dua byte.

Saya merasa harus ada cara yang baik untuk memperhitungkan kenaikan umum, tetapi saya belum menemukan yang singkat.

dfeuer
sumber
1
Dua golf kecil : periksa case list kosong kedua, dan geser nke bawah dengan 1.
xnor
Saya juga berpikir seharusnya ada cara untuk tidak menulis (n+1)!dua kali, tetapi usaha saya hanya mengikat.
xnor
0

Perl 5 -pa , 92 byte

while(@F){++$\;my@a;if($b=pop@F){unshift@a,pop@F while$F[-1]>=$b;push@F,(@a,--$b)x($\+1)}}}{

Cobalah online!

Xcali
sumber