Tipo di meta: ottieni la più lunga delle risposte più brevi


14

Il tuo compito - se lo accetti - è quello di scrivere un programma che aiuti a comprendere la mia proposta sul meta calcolando il vincitore di una competizione . Naturalmente, le risposte a questa domanda verranno trattate come proposto, quindi il tuo programma (se corretto) può calcolare se la tua risposta diventerà la risposta accettata.

Regole

  • Il programma legge un file con più righe del seguente formato (vedi esempio di seguito): [Lingua] TAB [NumberOfCharacters] TAB [LinkToAnswer]
  • Il nome del file viene passato come argomento al programma o il file viene reindirizzato all'input standard del programma. È una tua scelta, per favore menziona il metodo quando dai la risposta
  • Si prevede che il formato di input sia corretto. Non è necessario gestire gli errori.
  • Il numero di caratteri è positivo. Il tuo programma deve gestire lunghezze fino a 65535. 64k dovrebbero essere sufficienti per tutti :-)
  • Il programma produce quelle righe sull'output standard che soddisfano l'idea della meta proposta, cioè
    • vince il codice più breve di un particolare linguaggio di programmazione (fase di riduzione)
    • vince il codice più lungo tra tutti i linguaggi di programmazione (fase di smistamento)
    • in caso di pareggio, devono essere stampate tutte le risposte della stessa lunghezza
  • L'ordine dell'output non è importante
  • Anche se vince il codice più lungo, non si tratta . Il tuo codice deve essere il più breve possibile per il tuo linguaggio di programmazione.
  • Le risposte su linguaggi di programmazione di rado che non tentano di abbreviare il codice meritano un downvote, perché cercano di aggirare l'intenzione di questo tipo di domanda. Se esiste una sola risposta per un linguaggio di programmazione specifico, sarebbe considerato un candidato vincitore, quindi potresti iniziare a scrivere il suo codice.

Esempio di file di input (separato da singole schede in caso di problemi con la formattazione):

GolfScript  34  http://short.url/answer/ags
GolfScript  42  http://short.url/answer/gsq
C#  210 http://short.url/answer/cs2
Java    208 http://short.url/answer/jav
C#  208 http://short.url/answer/poi
J   23  http://short.url/answer/jsh
Ruby    67  http://short.url/answer/rub
C#  208 http://short.url/answer/yac
GolfScript  210 http://short.url/answer/210

Uscita prevista (l'ordine non è importante):

C#  208 http://short.url/answer/poi
C#  208 http://short.url/answer/yac
Java    208 http://short.url/answer/jav

Aggiornare

Alcuni programmi si basano sul fatto che esiste un singolo massimo (come il programma di caratteri C # 210). Derivato dalla realtà, qualcuno può anche scrivere un programma GolfScript con 210 caratteri. L'output rimarrebbe lo stesso. Ho aggiunto un tale GolfScript all'input.

Aggiornamento 2

Come suggerito, ho ricodificato (anche ancora code-golf) e la scadenza è il 06-03-2014 (che sembra una data arbitraria, ma tornerò in Germania da allora).

Risultati finali

Ho deciso di votare come segue:

  • Le risposte in cui il numero di caratteri non può essere confermato ricevono un commento per spiegare il conteggio.
  • Le risposte che possono essere facilmente ridotte ottengono un commento, un suggerimento di modifica e vanno nel risultato con il valore di conteggio inferiore. (Spero di averlo visto in anticipo).
  • Le risposte che non vengono compilate ottengono un downvote. (Piuttosto un compito difficile a quanto pare).
  • Le risposte che non vengono giocate ricevono un downvote (come già descritto nelle regole).
  • Le risposte che producono l'output previsto ottengono un voto. A causa di alcune risposte che non funzionano come previsto, utilizzo 4 diversi file di input e controllo il risultato atteso.

Infine, il vincitore viene determinato fornendo la tabella delle risposte qualificanti come input per il mio programma di riferimento (oltre a ricontrollare manualmente il risultato). Se la mia risposta fosse quella vincente, la escluderei dalla lista. In caso di più vincitori, dovrei sceglierne solo uno. Pertanto, alcuni bonus possono essere guadagnati:

  • risposte che accettano più input del previsto (ad es. al di fuori degli intervalli definiti)
  • risposte che usano un'idea intelligente di farla breve

Ho scattato un'istantanea delle risposte il 6 marzo 2014, 19:45 UTC + 1. L'analisi è in corso. Controllare tutte le risposte è più difficile del previsto ...


Questo non dovrebbe essere contrassegnato come code-challenge per ora? Anche quando è la scadenza?
TheConstructor il

2
Alt-name per questo è Code-Blackjack
VoronoiPotato,

Risposte:


2

Java - 556

import java.util.*;class G{public static void main(String[]x){TreeMap<?,TreeMap>m=new TreeMap();try{Scanner s=new Scanner(System.in);for(;;){String[]a=s.nextLine().split("\t");a(new Long(a[1]),a(a[0],m)).put(a[2],a);}}catch(Exception e){}TreeMap<?,Map<?,String[]>>n=new TreeMap();for(TreeMap o:m.values())a(o.firstEntry().getKey(),n).putAll((Map)o.firstEntry().getValue());for(String[]o:n.lastEntry().getValue().values())System.out.println(o[0]+"\t"+o[1]+"\t"+o[2]);}static<T>Map a(T t,Map m){if(m.get(t)==null)m.put(t,new TreeMap());return(Map)m.get(t);}}

Il programma leggerà da STDIN.

import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

class G {
    public static void main(String[] x) {
        TreeMap<?, TreeMap> m = new TreeMap();
        try {
            Scanner s = new Scanner(System.in);
            for (; ; ) {
                String[] a = s.nextLine().split("\t");
                a(new Long(a[1]), a(a[0], m)).put(a[2], a);
            }
        } catch (Exception e) {
        }
        TreeMap<?, Map<?, String[]>> n = new TreeMap();
        for (TreeMap o : m.values())
            a(o.firstEntry().getKey(), n).putAll((Map) o.firstEntry().getValue());
        for (String[] o : n.lastEntry().getValue().values())
            System.out.println(o[0] + "\t" + o[1] + "\t" + o[2]);
    }

    static <T> Map a(T t, Map m) {
        if (m.get(t) == null)
            m.put(t, new TreeMap());
        return (Map) m.get(t);
    }
}
  1. Il programma leggerà riga per riga fino a ArrayIndexOutOfBoundsExceptionquando si verifica un'eccezione ( quando viene rilevata una riga vuota o NoSuchElementExceptionse l'input termina senza trascinare una nuova riga). Ogni riga letta viene aggiunta a TreeMap m, che avrebbe potuto essere definita come TreeMap<String, TreeMap<Long, TreeMap<String,String[]>>>(da sinistra a destra: lingua, dimensione del codice, URL, input).
  2. Quindi TreeSet<Long, TreeSet<String, String[]>> nviene creato un risultato (da sinistra a destra: dimensione del codice, URL, input) in cui firstEntry()vengono aggregati i contenuti di ogni lingua .
  3. lastEntry()degli aggregati TreeMapcontiene il nostro risultato - dobbiamo solo stamparlo.

Prova su ideone.com ( hai cambiato le ultime due righe di input per mostrare che tutte le righe sono state lette)


Ho dovuto aggiungere una variabile per memorizzare BufferedReader -.-
TheConstructor il

1
Forse Java vince questa volta, perché non ha una parola chiave var ...
Thomas Weller,

@ThomasW. ho appena realizzato che avevo lasciato un po 'inutile {} nella versione
golfata.

Anche appena recuperato stavo usando Integer. Mentre int è più corto che lungo, Integer deve essere puntato su Long -.-
TheConstructor

2

Perl, 195 byte

while(<>){/(\S+)\t(\d+)\t(.+)/;push@{$a{$1}},$3if$2==$l{$1};$l{$1}=$2,$a{$1}=[$3]if $2<($l{$1}//65536)}$m=(sort{$b<=>$a}values%l)[0];map{$l=$_;map{print"$l\t$m\t$_\n"if$l{$l}==$m}@{$a{$l}}}keys%l

L'ingresso è previsto in STDIN, il risultato viene scritto in STDOUT:

C#      208     http://short.url/answer/poi
C#      208     http://short.url/answer/yac
Java    208     http://short.url/answer/jav

Versione Ungolfed

#!/usr/bin/env perl
use strict;
$^W=1;

# hash %language remembers the minimum count for a language
# %language: <language> => <minimum count>
my %language;
# hash %array remembers the URLs for the minimum count of the language
# %array: <language> => [<url>, <url>, ....]
my %array;

while(<>){
    # parse input line (no error checking)
    /(\S+)\t(\d+)\t(.+)/;
    my ($lang, $count, $url) = ($1, $2, $3);
    # add URL, if the count is the current minimum for the language
    if ($count == ($language{$lang}//0)) {
    # better, but longer version:
    # if (defined $language{$lang} and $count == $language{$lang}) {
        push @{$array{$lang}}, $url;
    }
    # create a new entry for the language, if there is a new minimum
    if ($count < ($language{$lang}//65536)) {
    # better, but longer version:
    # if (not defined $language{$lang} or $count < $language{$lang}) {
        $language{$lang} = $count;
        $array{$lang} = [$url];   
    }
}

# Sort the minimal values in numerical descending order and
# get the first entry as maximum.
my $maximum = (sort { $b <=> $a } values %language)[0];

# Loop over all URLs of minimal answers for the language,
# but print only the entries for the languages with the largest
# minima.
foreach my $lang (keys %language) {
    foreach my $url (@{$array{$lang}}) {
        if ($language{$lang} == $maximum) {
            print "$lang\t$maximum\t$url\n";
        }
    }
}
__END__

Heiko, come ha sottolineato @grovesNL, alcuni programmi potrebbero fare affidamento sul fatto che esiste un unico massimo. Forse puoi verificare se il tuo programma è interessato. Basta aggiungere una GolfScript 210 http://short.url/answer/210linea all'input e vedere se l'output rimane lo stesso. In realtà penso che il tuo non sia interessato, perché stai usando [0] al massimo, ma al momento non ho Perl disponibile per provarlo.
Thomas Weller,

@ThomasW .: Non è interessato. Ho aggiunto la linea e l'output rimane lo stesso. Dopo la prima parte che legge il file, la struttura dei dati %l/ %languagecontiene le lingue e i loro valori minimi. La struttura dei dati %a/ %arraycontiene solo quelle coppie lingua / URL, il cui valore è il minimo per questa lingua. Quindi i valori minimi vengono ordinati in ordine decrescente e il primo viene utilizzato come massimo globale e come condizione di filtro per %a/ %array.
Heiko Oberdiek,

2

Python 378 377 372

import sys
d=__import__("collections").defaultdict(list)
o={}
x=int
n="\n"
for i,l,u in[a.split()for a in sys.stdin.read().strip().split(n)]:d[i]+=[(l,u)]
for e,b in d.items():o[e]=[i for i in b if i[0]==str(min([x(i[0])for i in b]))]
print("".join(n.join("\t".join([u,s[0],s[1]])for s in y if x(s[0])==max(x(i[0][0])for i in o.values()))+n for u,y in o.items()).strip())

Input sullo stdin:

C:\Users\gcq\Documents\python>type m.txt | python test.py
C#      208     http://short.url/answer/poi
C#      208     http://short.url/answer/yac
Java    208     http://short.url/answer/jav

E questo è quello che avevo prima di iniziare a comprimerlo, a 551 caratteri:

from collections import defaultdict
import sys
d = defaultdict(list)

for language, length, url in [a.split() for a in sys.stdin.read().strip().split("\n")]:
    d[language].append((length, url))

o = {}
for language, data in d.items():
    winval = data[0][0]
    for i in data:
        if int(i[0]) < int(winval):
            winval = i[0]
    o[language] = [i for i in data if i[0] == winval]

maxlen = max(int(i[0][0]) for i in o.values())

for language, dataa in o.items():
    for data in dataa:
        if int(data[0]) == maxlen:
            print("\t".join([language, data[0], data[1]]))

1

C # - 628

Ecco una tua alternativa più lunga che utilizza DataTable:

using Microsoft.VisualBasic.FileIO;namespace System{using T=Data.DataTable;using R=Data.DataRow;using V=Data.DataView;using C=Data.DataColumn;class p{static void Main(string[] a){var I=typeof(Int32);T t=new T();t.Columns.AddRange(new[]{new C("a"),new C("b",I),new C("c"),new C("d",I)});var f=new TextFieldParser(a[0]);f.SetDelimiters("\t");while(!f.EndOfData){var r=t.NewRow();r.ItemArray=f.ReadFields();t.Rows.Add(r);}foreach(R r in t.Rows){r[3]=t.Compute("min(b)","a='"+r[0]+"'");}V v=new V(t);T s=v.ToTable();foreach(R r in s.Select("b='"+t.Compute("max(d)","")+"'")){Console.WriteLine(String.Join("\t",r[0],r[1],r[2]));}}}}

Inizialmente pensavo di aver ottenuto una leggera riduzione del codice usando max / min con DataTable, ma i tipi richiesti per costruire DataTable(righe / colonne / vista) aggiungono molta lunghezza, sfortunatamente. Sono nuovo nel golf del codice, quindi forse qualcuno sarebbe in grado di ridurlo ulteriormente. Ancora una sfida divertente.


1

dg - 286 281 260 251 218 byte

import '/sys'
d=dict!
for(a,b,c)in(map str.split$(sys.stdin.read!).splitlines!)=>d!!a=(d.get a list!)+(list'(int b,c))
for(i,l)in(d.items!)=>for(s,u)in l=>s==(max$map(i->fst$min i)d.values!)=>print$i+' '+(str s)+' '+u

Esempio:

$ cat langs.txt | dg langs.dg 
C# 208 http://short.url/answer/poi
C# 208 http://short.url/answer/yac
Java 208 http://short.url/answer/jav

Versione non golfata :

import '/sys'

s = sys.stdin.read!
d = dict!
# convert the string into a list of tuples (name, score, url)
u = map str.split $ s.splitlines!
# add all the values to the dict (converting the score to an integer)
for (a, b, c) in u =>
  d!!a = (d.get a list!) + (list' (int b, c))
# computes the maximum value amongst the mins
m = max $ map (i -> fst $ min i) d.values!
for (i, l) in (d.items!) =>
  for (s, u) in l =>
    # if the score equals the maximum then print all the line
    s == m => print $ i + ' ' + (str s) + ' ' + u  # actually here .format()
                                                   # would be better

D: Che diamine è dg?
A: Un linguaggio di programmazione che si compila in bytecode CPython, proprio come Scala si compila in JVM. Ciò significa essenzialmente che dg è una sintassi alternativa per Python 3. Permette anche di usare tutte le librerie esistenti.

Maggiori informazioni qui (anche un tutorial!): Https://pyos.github.io/dg


Se lo inserisco in un file, ha solo 217 byte (terminazioni di linea Linux)
Thomas Weller,

@ThomasW. Strano! Usando cat langs.dg | wc -cottengo 218!
rubik,

Forse newline finale o nessuna newline finale?
TheConstructor

@TheConstructor potrebbe essere, anche se non dovrebbe salvarlo con una nuova riga oO
rubik

1

Rebol - 314

d: map[]foreach r read/lines to-file system/script/args[r: split r tab p: take r r/1: to-integer r/1 r/2: reduce[r/2]either none? d/:p[repend d[p r]][case[d/:p/1 > r/1[d/:p: r]d/:p/1 = r/1[append d/:p/2 r/2]]]]l: 0 foreach[k v]d[l: max l v/1]foreach[k v]d[if l = v/1[foreach n v/2[print rejoin[k tab v/1 tab n]]]]

un-golfed

d: map []

foreach r read/lines to-file system/script/args [
    r: split r tab
    p: take r
    r/1: to-integer r/1
    r/2: reduce [r/2]
    either none? d/:p [repend d [p r]] [
        case [
            d/:p/1 > r/1 [d/:p: r]
            d/:p/1 = r/1 [append d/:p/2 r/2]
        ]
    ]
]

l: 0 foreach [k v] d [l: max l v/1]
foreach [k v] d [
    if l = v/1 [
        foreach n v/2 [print rejoin [k tab v/1 tab n]]
    ]
]

Esempio di utilizzo:

$ rebol script.reb data.txt
C#    208   http://short.url/answer/poi
C#    208   http://short.url/answer/yac
Java  208   http://short.url/answer/jav

0

C # - 515

Si aspetta un nome file come argomento

using System.Collections.Generic;namespace N{using S=SortedList<int,T>;class T:List<string>{static void Main(string[]a){var d=new Dictionary<string,S>();int n,m=0;T w=new T();foreach(var l in System.IO.File.ReadAllLines(a[0])){var p=(a=l.Split('\t'))[0];n=int.Parse(a[1]);if(!d.ContainsKey(p))d.Add(p,new S());if(!d[p].ContainsKey(n))d[p].Add(n,new T());d[p][n].Add(l);}foreach(var e in d){n=e.Value.Keys[0];if(n==m)w.AddRange(e.Value[n]);if(n>m)w=e.Value[m=n];}foreach(var e in w)System.Console.WriteLine(e);}}}

Per prima cosa ho progettato il mio programma C # per essere diretto, perché volevo avere una specie di programma di riferimento. Ma poi ho deciso di lanciarmi anche nel contest e ho giocato a golf. Questa è una delle versioni precedenti del codice + alcuni commenti:

// N: namespace
// P: Program
// S: type definition: sorted dictionary
// a: arguments
// d: data container
// r: lines read from text file
// l: single line from r
// t: tabbed part of l after splitting
// p: programming language name
// n: character count
// m: maximum character count
// w: list of winners
// e: entry in data container
// c: winner candidate
using System.Collections.Generic;
namespace N
{
    using S = SortedList<int, P>;
    public class P : List<string>
    {
        public static void Main(string[] a)
        {
            var r = System.IO.File.ReadAllLines(a[0]);
            // Make it a data structure
            var d = new Dictionary<string, S>();
            foreach (var l in r)
            {
                var t = l.Split('\t');
                var p = t[0];
                var n = int.Parse(t[1]);
                if (!d.ContainsKey(p)) d.Add(p, new S());
                if (!d[p].ContainsKey(n)) d[p].Add(n, new P());
                d[p][n].Add(l);
            }
            // Get the maximum values
            var m = 0;
            P w = null;
            foreach (var e in d)
            {
                foreach (var s in e.Value.Keys)
                {
                    if (s > m)
                    {
                        w = e.Value[s];
                        m = s;
                    }
                    else if (s == m)
                    {
                        w.AddRange(e.Value[s]);
                    }
                    break; // Break here to get the shortest solution per language
                }
            }
            // Print everything on console
            foreach (var e in w)
            {
                System.Console.WriteLine(e);
            }
        }
    }
}

Sembra che la dimensione del file 515 sul mio disco includesse un segno di ordine byte. Copia / incolla da qui è solo 512 byte.
Thomas Weller,

0

C # - 460 359

Dopo aver realizzato quanto fosse ingombrante la mia DataTablesoluzione, ho creato il seguente esempio usando Linq. Utilizza la stessa metodologia della mia soluzione precedente.

golfed

namespace System{using Linq;using IO;class p{static void Main(string[]i){var l=(from f in File.ReadAllLines(i[0])let s=f.Split('\t')select new Tuple<string,int,string>(s[0],Convert.ToInt16(s[1]),f)).ToList();foreach(var f in l.Where(a=>a.Item2==l.Where(b=>b.Item1==l.Single(c=>c.Item2==l.Max(d=>d.Item2)).Item1).Min(e=>e.Item2)))Console.WriteLine(f.Item3);}}}

Ungolfed

namespace System
{
    using Linq;
    using IO;
    class p
    {
        static void Main(string[]i)
        {
            var l=(from f in File.ReadAllLines(i[0])
                   let s=f.Split('\t')
                   select new Tuple<string, int, string>(s[0],Convert.ToInt16(s[1]),f)).ToList();
            foreach(var f in l.
                Where(a=>a.Item2==l.
                    Where(b=>b.Item1==l.
                        Single(c=>c.Item2==l.
                            Max(d=>d.Item2)).Item1).
                                Min(e=>e.Item2)))
            Console.WriteLine(f.Item3);
        }
    }
}

Sono ancora abbastanza nuovo in Linq, quindi sono quasi certo che quelle espressioni possano essere ulteriormente ridotte.

Dalla tua domanda, non è chiaro se esiste un'unica soluzione di massima lunghezza. Per le mie risposte ho usato il presupposto che esiste un singolo punto massimo (ovvero, se esisteva anche un massimo di 210 di GolfScript, potrebbe non riuscire in base al record massimo singolo restituito). La soluzione di Heiko avrebbe lo stesso problema. Per risolvere questo problema, dovremmo aggiungere un altro passaggio che conteneva un elenco di massimi collegati per controllare i minimi per ogni lingua.


1
Mi sembra che potresti salvare f come Item3 (invece di s [2]) e quindi scrivere f.Item3 sulla console senza la necessità di ricombinare Item1 e Item2.
Thomas Weller,

Grazie per il chiarimento dei requisiti. Derivato dalla realtà, direi che potrebbe accadere che qualcuno (inesperto, ovviamente) scriva un GolfScript con 210 caratteri.
Thomas Weller,

1
Se converti il ​​primo ciclo foreach in LINQ, diventa ancora più breve:namespace System{using Linq;using IO;class p{static void Main(string[]i){var l=(from f in File.ReadAllLines(i[0])let s=f.Split('\t') select new Tuple<string, int, string>(s[0],Convert.ToInt16(s[1]),f)).ToList();foreach(var f in l.Where(a=>a.Item2==l.Where(b=>b.Item1==l.Single(c=>c.Item2==l.Max(d=>d.Item2)).Item1).Min(e=>e.Item2)))Console.WriteLine(f.Item3);}}}
Thomas Weller,

@ThomasW .: Buona idea per la divisione. Grazie per la conversione del ciclo foreach, ci avevo pensato aggiungendolo in origine, ma non sapevo di poter usare let in quel modo.
GroveNL

Credo di cominciare a capire che cosa il vostro query non :) e sarà anche fallire per una più agevole ingresso in questo modo: Golfscript 100 ..., C# 1 ..., C# 200 .... Questo probabilmente ha bisogno di qualche modifica
Thomas Weller,

0

C ++ - 535

Produrrà solo le risposte legate per la posizione più lunga dopo aver selezionato solo le risposte più brevi di ogni lingua come potenziali vincitori.

#include<fstream>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){

    string s;
    vector<string>l;
    vector<int>v;
    vector<string>u;
    cin>>s;

    ifstream i(s.c_str());

    do{
        int n;
        i>>s;
        if(i.eof())break;
        l.push_back(s);
        i>>n;
        v.push_back(n);
        i>>s;
        u.push_back(s);
    }while(1);

    for(int i=0;i<l.size();i++){
        for(int j=0;j<l.size();j++){
            if(l[j]==l[i]){
                if(v[i]>v[j])l[i]="";
                else if(v[i]<v[j])l[j]="";
            }
        }
    }
    int n=0;
    for(int i=0;i<v.size();i++)
        if(n<v[i]&l[i]!="")n=v[i];

    for(int i=0;i<v.size();i++)
        if(v[i]==n)cout<<l[i]<<'\t'<<v[i]<<'\t'<<u[i]<<endl;
}

Golf (non illeggibile come alcune lingue):

#include<fstream>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){string s;vector<string>l;vector<int>v;vector<string>u;cin>>s;ifstream i(s.c_str());do{int n;i>>s;if(i.eof())break;l.push_back(s);i>>n;v.push_back(n);i>>s;u.push_back(s);}while(1);for(int i=0;i<l.size();i++)for(int j=0;j<l.size();j++)if(l[j]==l[i]){if(v[i]>v[j])l[i]="";else if(v[i]<v[j])l[j]="";}int n=0;for(int i=0;i<v.size();i++)if(n<v[i]&l[i]!="")n=v[i];for(int i=0;i<v.size();i++)if(v[i]==n)cout<<l[i]<<'\t'<<v[i]<<'\t'<<u[i]<<endl;}

Hai verificato l'output rispetto all'output previsto? Con 8 righe di input, deve fornire solo 3 righe come output. Il tuo programma sembra produrre tutto.
Thomas Weller,

@ThomasW. Oh, ho frainteso quella parte. Lo rimuoverò per ora.

@ThomasW. Questo sembra giusto?

Se lo metto in un file ottengo 540 byte, non 535. Potresti ricontrollare la lunghezza, per favore?
Thomas Weller,

Sono 535 caratteri. Siamo entrambi corretti. Posso cambiarlo se vuoi.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.