সি ++ প্রোগ্রামগুলিতে স্ক্যানফ () ব্যবহার করা কি সিন ব্যবহারের চেয়ে দ্রুত?


126

এটি সত্য কিনা আমি জানি না, তবে যখন সাইটগুলি সরবরাহ করার সমস্যাগুলির একটিতে আমি FAQ পড়ছিলাম তখন আমি এমন কিছু পেয়েছি, যা আমার দৃষ্টি আকর্ষণ করে:

আপনার ইনপুট / আউটপুট পদ্ধতিগুলি পরীক্ষা করুন। সি ++ তে, সিন এবং কাউট ব্যবহার করা খুব ধীর। এগুলি ব্যবহার করুন এবং আপনি গ্যারান্টি দেবেন যে কোনও শালীন পরিমাণের ইনপুট বা আউটপুট দিয়ে কোনও সমস্যা সমাধান করতে সক্ষম হবে না। পরিবর্তে প্রিন্টফ এবং স্ক্যানফ ব্যবহার করুন।

কেউ কি এই বিষয়টি পরিষ্কার করতে পারেন? আসলেই কি সি ++ প্রোগ্রামগুলিতে স্কিনএফ () ব্যবহার করে সিএন >> কিছু ব্যবহার করার চেয়ে দ্রুত ? যদি হ্যাঁ, তবে এটি সি ++ প্রোগ্রামগুলিতে ব্যবহার করা ভাল অভ্যাস? আমি ভেবেছিলাম এটি সি নির্দিষ্ট, যদিও আমি কেবল সি ++ শিখছি ...


14
আমার ধারণা: খারাপ প্রোগ্রামার দুর্বল পারফরম্যান্সের জন্য স্ট্যান্ডার্ড লাইব্রেরিগুলিকে দোষ দেয়। সর্বদা হাস্যরসাত্মক "আমার মনে হয় আমি জিসিসিতে একটি বাগ খুঁজে পেয়েছি" কান্নার মতো ধরণের।
জন কুগেলম্যান

11
@ ক্লিপস: প্রতিযোগিতার জন্য আমি যে এসিএম সমস্যাগুলি নিয়ে কাজ করেছি তাতে প্রচুর পরিমাণে ইনপুট / আউটপুট রয়েছে এবং আপনার প্রোগ্রামটি 60০ সেকেন্ডের মতো কিছু সমাধান করতে হবে ... এটি এখানে একটি আসল সমস্যা হয়ে দাঁড়িয়েছে।
এমপেন

19
--- বলেছিল, অতিরিক্ত পারফরম্যান্স বৃদ্ধির জন্য যদি আপনার
স্ক্যানফ

4
ঠিক যেমন একটি পর্যবেক্ষণ - আমি এটির সাথে ঘুরেছি, এবং ২ য় সমস্যাতে (PRIME1) - একই অ্যালগরিদম ব্যবহার করে, উভয়বার একবার cin / cout এবং একবার স্ক্যানফ / প্রিন্টফ ব্যবহার করেছি এবং প্রথম সংস্করণটি দ্বিতীয়টির চেয়ে দ্রুত ছিল (তবে যথেষ্ট এটি বন্ধ যে এটি পরিসংখ্যানগতভাবে অপ্রাসঙ্গিক)। এটি এমন একটি সমস্যা যা ইনপুট / আউটপুট নিবিড় হিসাবে চিহ্নিত করা হয় এবং ইনপুট / আউটপুট পদ্ধতির কোনও পরিসংখ্যানগত পার্থক্য থাকে না।
অন্ধকার

4
@ উপগ্রহ - উভয় পদ্ধতির পরীক্ষা সম্পর্কে তথ্যের জন্য ধন্যবাদ। যদিও আমি দু: খিত - আমি সিন ও
কাউটকে

উত্তর:


209

এখানে একটি সাধারণ মামলার তাত্ক্ষণিক পরীক্ষা: স্ট্যান্ডার্ড ইনপুট থেকে নম্বরগুলির তালিকা পড়ার প্রোগ্রাম এবং সমস্ত নম্বরটি এক্সওআর a

iostream সংস্করণ:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

স্ক্যানফ সংস্করণ:

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

ফলাফল

তৃতীয় প্রোগ্রামটি ব্যবহার করে, আমি 33,280,276 এলোমেলো সংখ্যাযুক্ত একটি পাঠ্য ফাইল তৈরি করেছি। ফাঁসির সময়গুলি হ'ল:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

সংকলকটির অপ্টিমাইজেশন সেটিংস পরিবর্তন করা ফলাফলগুলি মোটেও তেমন বদলেছে বলে মনে হচ্ছে না।

সুতরাং: সত্যিই একটি গতি পার্থক্য আছে।


সম্পাদনা: ব্যবহারকারী ক্লিফিশ নীচে উল্লেখ করেছেন যে গতির পার্থক্য মূলত আইওস্ট্রিম আই / ও ফাংশনগুলির সাথে সিআই / ও ফাংশনগুলির সাথে সুসংগতকরণ বজায় রাখার কারণে। আমরা একটি কল দিয়ে এটি বন্ধ করতে পারি std::ios::sync_with_stdio(false);:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

নতুন ফলাফল:

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

সি ++ আইস্ট্রিমে জয়! দেখা যাচ্ছে যে এই অভ্যন্তরীণ সিঙ্কিং / ফ্লাশিং হ'ল সাধারণত iostream i / o হ্রাস করে। যদি আমরা স্টিডিও এবং আইস্ট্রিম মিশ্রিত না করে থাকি তবে আমরা এটিকে বন্ধ করতে পারি এবং তারপরে আইস্ট্রিমটি সবচেয়ে দ্রুত।

কোড: https://gist.github.com/3845568


6
আমি মনে করি 'এন্ডেল' এর ব্যবহারের ফলে মৃত্যুদণ্ড কার্যকর হতে পারে।
কৃষ্ণ মোহন

2
Std :: endl এর ব্যবহার লুপে নেই।
নিবোট

সিঙ্ক চালু বা বন্ধ করে কোনও তফাত্ করে না। এর জন্য libc ++ দোষারোপ করুন। এটি কেবল libstdc ++
iBug

আপনি কি মনে করেন <cstdio> এবং <stdio.h> এর মধ্যে কোনও পার্থক্য আছে?
চন্দ্রহাস অরোরি

iostreamআপনি যখন একটি scanfকলে একাধিক পূর্ণসংখ্যাকে পার্স করেন তখন হেরে যায় ।
ম্যাক্সিম এগারুশকিন

68

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

cin/ এর পারফরম্যান্স coutধীর হতে পারে কারণ তাদের অন্তর্নিহিত সি লাইব্রেরির সাথে নিজেকে সমন্বয় করা দরকার। যদি সি আইও এবং সি ++ আইও উভয়ই ব্যবহার করা হয় তবে এটি প্রয়োজনীয়।

তবে, আপনি যদি কেবল সি ++ আইও ব্যবহার করতে চলেছেন তবে কোনও আইও ক্রিয়াকলাপের আগে কেবল নীচের লাইনটি ব্যবহার করুন।

std::ios::sync_with_stdio(false);

এ সম্পর্কিত আরও তথ্যের জন্য, সম্পর্কিত libstdc ++ ডক্সটি দেখুন


উপরের লাইনটি স্রেফ পরীক্ষা করেছেন (std :: ios :: sync_with_stdio (মিথ্যা);) এবং এটি সত্যই costdio
gabrielhidasy

cin.tie (স্ট্যাটিক_কাস্ট <ostream *> (0)) ব্যবহার করুন; আরও ভাল পারফরম্যান্সের জন্য
মোহাম্মদ এল-নাকিব

42

সম্ভবত স্ট্রিম ব্যবহারের চেয়ে স্ক্যানফ কিছুটা দ্রুত। যদিও স্ট্রিমগুলি প্রচুর ধরণের সুরক্ষা সরবরাহ করে এবং রানটাইমের সময় বিন্যাসের স্ট্রিংগুলি বিশ্লেষণ করতে হয় না, তবে এটি সাধারণত অতিরিক্ত মেমরি বরাদ্দ না দেওয়ার সুবিধা পায় (এটি আপনার সংকলক এবং রানটাইমের উপর নির্ভর করে)। এটি বলেছে, যদি না পারফরম্যান্স আপনার একমাত্র শেষ লক্ষ্য এবং আপনি সমালোচনামূলক পথে না থাকেন তবে আপনার সত্যিকারের নিরাপদ (ধীর) পদ্ধতির পক্ষে হওয়া উচিত।

একটা খুবই সুস্বাদু ঔষধি Sutter "দ্বারা এখানে লেখা নিবন্ধ যোগাযোগ Manor Farm এর স্ট্রিং Formatters " যারা মত স্ট্রিং formatters এর কর্মক্ষমতা বিস্তারিত অনেকটা মধ্যে যায় sscanfএবং lexical_castএবং যা কিছু ধরনের তৈরি করছিল তাদের ধীরে ধীরে বা দ্রুত চালানো। এটি এক ধরণের উপমা, সম্ভবত সি ধরণের আইও এবং সি ++ শৈলীর মধ্যে পারফরম্যান্সকে প্রভাবিত করবে এমন ধরণের জিনিসগুলির কাছে। ফর্ম্যাটরগুলির সাথে প্রধান পার্থক্যটি প্রকার সুরক্ষা এবং মেমরির বরাদ্দের সংখ্যা ছিল।


19

আমি সবেমাত্র ইউভি অনলাইন (ফ্যাক্টোভিসরস, একটি খুব আকর্ষণীয় সমস্যা, এটি পরীক্ষা করে দেখুন) নিয়ে একটি সমস্যা নিয়ে কাজ করে একটি সন্ধ্যা কাটিয়েছি:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

আমি আমার জমাগুলিতে টিএলই (সময়সীমা অতিক্রম করে) পেয়েছিলাম। এই অনলাইন বিচারকের সাইটগুলি সমাধান করার ক্ষেত্রে, আপনার সমাধানটি মূল্যায়নের জন্য ব্যবহৃত হাজার হাজার পরীক্ষার সম্ভাব্য হ্যান্ডেলগুলির জন্য আপনার কাছে প্রায় ২-৩ দ্বিতীয় সময় সীমা রয়েছে। এই যেমন গণ্য তীব্র সমস্যার জন্য, প্রতিটি মাইক্রোসেকেন্ড গণনা করা হয়।

আমি প্রস্তাবিত অ্যালগরিদম ব্যবহার করছি (সাইটের জন্য আলোচনার ফোরামগুলির বিষয়ে পড়ুন), তবে এখনও টিএলইএস পাচ্ছিলাম।

আমি কেবল "cin >> n >> m" কে "স্ক্যানফ ("% d% d ", & n, & m)" এবং কয়েকটি ক্ষুদ্র "গর্ত" থেকে "মুদ্রণ" এ পরিবর্তন করেছি এবং আমার TLE "গৃহীত" হয়ে গেছে!

সুতরাং, হ্যাঁ, এটি একটি বড় পার্থক্য করতে পারে, বিশেষত যখন সময় সীমা অল্প থাকে।


একমত। ইউভিএ অনলাইন জাজ সমস্যাতে আমার সাথে একই ঘটনা ঘটেছে: সেনা বাডস uva.onlinejudge.org/…
মোহাম্মদ এল-নাকিব

6

আপনি যদি উভয় পারফরম্যান্স এবং স্ট্রিং ফর্ম্যাটিং সম্পর্কে চিন্তা করেন তবে ম্যাথিউ উইলসনের ফাস্টফর্ম্যাটটি একবার দেখুন লাইব্রেরিটি একবার দেখুন।

সম্পাদনা করুন - সেই লাইব্রেরিতে accu প্রকাশনার লিঙ্ক: http://accu.org/index.php/journals/1539


সম্পূর্ণ সম্মত হন। তবে আপনার সচেতন হওয়া দরকার যে ফাস্টফর্ম্যাটটি কেবল আউটপুট জন্য। এর কোনও ইনপুট / পড়ার সুবিধা নেই। (এখনও নেই, যাইহোক)
ডিসিডব্লিউ

দুর্ভাগ্যক্রমে এই লিঙ্কটি মারা গেছে বলে মনে হচ্ছে। এখানে একটি
ওয়েব্যাক

2

এখানে স্টডিও বাস্তবায়ন ( লাইবিও ) রয়েছে যা ফাইল * কে সি ++ স্ট্র্যামবুফ হিসাবে প্রয়োগ করে, এবং এফপ্রিন্টফ একটি রানটাইম ফর্ম্যাট পার্সার হিসাবে প্রয়োগ করে। আইওস্ট্রিমগুলির রানটাইম ফর্ম্যাট পার্সিংয়ের দরকার নেই, এটি সমস্তই সংকলনের সময়ে সম্পন্ন হয়েছে। সুতরাং, ব্যাকেন্ডগুলি ভাগ করে নেওয়ার সাথে, এটা আশা করা যুক্তিসঙ্গত যে রানটাইমের সময় আইওস্ট্রিমগুলি আরও দ্রুত।


আমি তাই মনে করি না. আমি মনে করি জিএনইউ এর লিবিসি খাঁটি সি এবং সমাবেশ।
ক্রিস লুৎজ

2

হ্যাঁ আইস্ট্রিস্ট সিএসডিডিওর চেয়ে ধীর er
হ্যাঁ আপনি সম্ভবত সিটিডিও ব্যবহার করবেন না যদি আপনি সি ++ তে বিকাশ করছেন।
এটি বলার পরেও, আপনি যদি ফর্ম্যাটিং, টাইপ সুরক্ষা, ব্লা, ব্লা, ব্লাহ সম্পর্কে কোনও চিন্তা না করেন তবে স্ক্যানফের চেয়ে আই / ও পাওয়ার আরও দ্রুততর উপায় আছে ...

উদাহরণস্বরূপ এটি STDIN থেকে একটি নম্বর পেতে একটি কাস্টম রুটিন:

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}

1
getchar_unlocked () মানসম্মত নয়, এবং ভিজ্যুয়াল স্টুডিওতে নয় জিসিসির জন্য উপলব্ধ
মোহাম্মদ এল-নাকিব

1

সমস্যাটি হ'ল cinপ্রচুর ওভারহেড জড়িত কারণ এটি আপনাকে scanf()কলগুলির উপরে একটি বিমূর্ত স্তর দেয় । আপনি ব্যবহার করা উচিত নয় scanf()ওভার cinআপনি সি ++ সফ্টওয়্যার লিখছেন কারণ চাই হয় cinজন্য। আপনি যদি পারফরম্যান্স চান তবে আপনি সম্ভবত সি ++ তে আই / ও লিখছেন না।


2
কি cinসত্যিই আরো "বিমূর্ত" (রানটাইম এ) তুলনায় scanf? আমার মনে হয় না ... scanfঅবশ্যই রানটাইমের সময় ফর্ম্যাটটির স্ট্রিংটি ব্যাখ্যা করতে হবে, তবে iostreamসংকলন-সময়ে ফর্ম্যাটটি জানে।
নিবোট

1
@nibot: টাইপ কম্পাইল-সময়ে পরিচিত হয় কিন্তু বিন্যাস । ইনপুটটি হেক্সাডেসিমাল হিসাবে প্রত্যাশিত কিনা তা উদাহরণস্বরূপ কীভাবে রানটাইমেstd::istream কনফিগার করা হয় (আই / ও ম্যানিপুলেটরগুলির মাধ্যমে বা অবজেক্টে নিজেই পতাকা স্থাপন করে) তার উপর নির্ভরশীল । একজন তাই একটি কল অন্যদিকে বস্তু, এমন কোন রাষ্ট্র হয়েছে এ ব্যাপারে আরো অনেক কিছু স্থিতিশীল। istreamFILE*scanf
ড্রিমলাক্স

1

বিবৃতি cinএবং coutসাধারণ ব্যবহার তুলনায় ধীর হবে বলে মনে হচ্ছে scanfএবংprintf সি ++ এর , তবে আসলে তারা দ্রুত FA

জিনিস হল: সি ++ সালে যখনই আপনি ব্যবহার cinএবং cout, একটি সুসংগত প্রক্রিয়া ডিফল্ট নিশ্চিত করে তোলে যে সঞ্চালিত যে আপনি উভয় ব্যবহার করেন scanfএবং cinএকে অপরের সাথে সুসংগত আপনার প্রোগ্রাম করুন, তারপরে তারা উভয় হবে। এই সিঙ্ক প্রক্রিয়াটি সময় নেয়। অতএব cinএবং coutআস্তে আস্তে আস্তে থাকুন।

তবে সিঙ্ক্রোনাইজেশন প্রক্রিয়াটি যদি না ঘটে সেট করা থাকে তবে cinতার চেয়ে দ্রুত scanf

সিঙ্ক প্রক্রিয়াটি এড়িয়ে যেতে নীচের কোড স্নিপেটটি আপনার প্রোগ্রামের শুরুতে অন্তর্ভুক্ত করুন main():

std::ios::sync_with_stdio(false);

পরিদর্শন এই সাইটের আরও তথ্যের জন্য।


সিঙ্ক্রোনাইজেশন সম্পর্কে আপনার ব্যাখ্যার জন্য +1। আমি সবেমাত্র সিঙ্ক বন্ধ করে দিয়েছি এবং কিছু কোডে স্ক্যানফ এবং সিন দুটি ব্যবহার করেছি । এখন আমি জানি এটিতে কী ছিল? ধন্যবাদ!
দরিউশ

1
#include <stdio.h>
#include <unistd.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

ফাইলটির শেষে একটি বাগ রয়েছে, তবে এই সি কোডটি দ্রুততর সি ++ সংস্করণের চেয়ে নাটকীয়ভাবে দ্রুত।

paradox@scorpion 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6  make test        
time ./xor-c < rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

আসল সি ++ 30 সিসি নিয়েছে সি কোডটি 2 সিসি নিয়েছিল।


-1

অবশ্যই আইস্ট্রিমের উপরে cstdio ব্যবহার করা হাস্যকর। কমপক্ষে আপনি যখন সফ্টওয়্যারটি বিকাশ করেন (আপনি যদি ইতিমধ্যে সি ++ এর উপরে সি ++ ব্যবহার করে থাকেন তবে সমস্ত উপায় যান এবং কেবলমাত্র এর অসুবিধাগুলি ভোগার পরিবর্তে এটির সুবিধাগুলি ব্যবহার করুন)।

তবে অনলাইন জাজে আপনি সফ্টওয়্যারটি বিকাশ করছেন না, আপনি এমন একটি প্রোগ্রাম তৈরি করছেন যা মাইক্রোসফ্ট সফটওয়্যারটি 3 সেকেন্ডে অর্জন করতে 60 সেকেন্ড সময় নেয় এমন কাজ করতে সক্ষম হবে!

সুতরাং, এক্ষেত্রে সোনার নিয়মটি এর মতো হয় (অবশ্যই যদি আপনি জাভা ব্যবহার করে আরও বেশি সমস্যায় পড়েন না)

  • সি ++ ব্যবহার করুন এবং সমস্যাটি সমাধান করার জন্য এর সমস্ত শক্তি (এবং ভারীকরণ / আস্তে) ব্যবহার করুন
  • আপনি যদি সময় সীমিত পান তবে প্রিন্টফেস এবং স্ক্যানফের জন্য সিন এবং কাউট পরিবর্তন করুন (আপনি যদি শ্রেণীর স্ট্রিংটি ব্যবহার করে ভুল হয়ে থাকেন তবে এই জাতীয় মুদ্রণ করুন: প্রিন্টফ (% s, mystr.c_str ());
  • আপনি যদি এখনও সময় সীমাবদ্ধ পান তবে কিছু স্পষ্টতর অপ্টিমাইজেশন করার চেষ্টা করুন (যেমন / যখন / ডাবহিলস বা পুনরাবৃত্ত ফাংশনগুলির জন্য অনেক বেশি এমবেড থাকা এড়ানো)। খুব বড় যে রেফারেন্স অবজেক্টগুলি দিয়ে যেতে তা নিশ্চিত করুন ...
  • যদি আপনি এখনও সময় সীমাবদ্ধ পান তবে std :: ভেক্টর এবং সি-অ্যারেগুলির জন্য সেটগুলি পরিবর্তন করে দেখুন।
  • যদি আপনি এখনও সময় সীমিত পান তবে পরবর্তী সমস্যাটিতে যান ...

-2

এর scanfচেয়ে দ্রুত থাকলেও cinতাতে কিছু আসে যায় না। বেশিরভাগ সময় আপনি হার্ড ড্রাইভ বা কীবোর্ড থেকে পড়বেন be আপনার আবেদন মধ্যে কাঁচা ডেটা পথ মাত্রার আদেশ বেশি সময় লাগে তুলনায় এটি লাগে scanfবা cinএটি প্রক্রিয়া।


পাইপগুলির মাধ্যমে আইপিসির কী হবে? আপনি কি মনে করেন যে সেখানে কোনও লক্ষণীয় পারফরম্যান্স হিট হতে পারে?
ড্রিমলাক্স

এমনকি পাইপগুলির মাধ্যমে আইপিসির সাহায্যে, কেবল স্ক্যানফ / সিনের সাথে পার্স করার চেয়ে কার্নেলের অভ্যন্তরে এবং বাইরে যাওয়ার জন্য আরও অনেক বেশি সময় ব্যয় করা হয়।
জে কনরোড

8
আমি এই অঞ্চলে পরীক্ষাগুলি করেছি এবং অবশ্যই কোট এবং সিএন পারফরম্যান্স স্তন্যপান করছি। ব্যবহারকারীর ইনপুটগুলির জন্য এটি নগন্য, যদিও কার্য সম্পাদন গুরুত্বপূর্ণ এমন জিনিসগুলির পক্ষে এটি অবশ্যই নয়। যদিও অন্যান্য সি ++ ফ্রেমওয়ার্ক বিদ্যমান, যা দ্রুত।
জোহানেস স্কাউব - লিটব

সমস্যা হল iostream হয় HDD তুলনায় ধীর। হ্যাঁ, এটি অনেকটা স্তন্যপান করে।
polkovnikov.ph
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.