15.07.2013 Views

Genel Programlama I

Genel Programlama I

Genel Programlama I

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>Genel</strong> <strong>Programlama</strong> I<br />

04.11.2010<br />

Ders 6<br />

1


Fonksiyonlar<br />

C'de alt programlara fonksiyon denir. Fonksiyon sözcüğü burada matematiksel<br />

anlamıyla değil diğer programlama dillerinde kullanılan, "alt program", "prosedür",<br />

"subroutine" sözcüklerinin karşılığı olarak kullanılmaktadır.<br />

Fonksiyonlar C dilinin temel yapı taşlarıdır. Çalıştırılabilen bir C programı en az bir C<br />

fonksiyonundan oluşur. Bir C programının oluşturulmasında fonksiyon sayısında bir<br />

kısıtlama yoktur.<br />

Fonksiyonların onları çağıran fonksiyonlardan aldıkları girdileri ve yine onları çağıran<br />

fonksiyonlara gönderdikleri çıktıları vardır. Fonksiyonların girdilerine aktüel<br />

parametreler (actual parameters) ya da argumanlar (arguments) diyoruz.<br />

Fonksiyonların çıktılarına geri dönüş değeri (return value) diyoruz.<br />

2


Bir fonksiyon iki farklı amaçla kullanılabilir :<br />

•Fonksiyon, icrası süresince belli amaçları gerçekleştirir. (Belli işlemleri yapar)<br />

•Fonksiyon icrası sonunda üreteceği bir değeri kendisini çağıran fonksiyona<br />

gönderebilir.<br />

Fonksiyonların Tanımlanması ve Çağırılması<br />

Bir fonksiyonun ne iş yapacağının ve bu işi nasıl yapacağının C dilinin sentaks<br />

kurallarına uygun olarak anlatılmasına o fonksiyonun tanımlanması (definition)<br />

denir. Fonksiyon tanımlamaları aşağıda inceleneceği gibi birtakım kurallara tabidir.<br />

Bir fonksiyonun çağırılması ise o fonksiyonun yapacağı işi icraya davet edilmesi<br />

anlamına gelir. Fonksiyon çağırma ifadesi karşılığında derleyici, programın akışını<br />

ilgili fonksiyonun kodunun bulunduğu bölgeye aktaracak şekilde bir kod üretir.<br />

Programın akışı fonksiyonun kodu içinde akıp bu kodu bitirdiğinde, yani fonksiyon<br />

icra edildiğinde, programın akışı yine fonksiyonun çağırıldığı noktaya geri<br />

dönecektir.<br />

3


Fonksiyonların Geri Dönüş Değerleri (return values)<br />

Bir fonksiyonun yürütülmesi sonunda onu çağıran fonksiyona dönüşünde<br />

gönderdiği değere, fonksiyonun geri dönüş değeri (return value) denmektedir.<br />

Her fonksiyon bir geri dönüş değeri üretmek zorunda değildir. Fonksiyonların geri<br />

dönüş değerleri farklı amaçlar için kullanılabilir;<br />

Bazı fonksiyonlar tek bir değer elde etmek amacıyla tasarlanmışlardır. Elde<br />

ettikleri değeri de kendilerini çağıran fonksiyonlara geri dönüş değeri olarak<br />

iletirler. Örneğin:<br />

y = pow(2, 3);<br />

pow fonksiyonu standart bir C fonksiyonudur. Birinci parametresiyle belirtilen<br />

sayının ikinci parametresiyle belirtilen kuvvetini hesaplayarak, hesapladığı sayıyı<br />

geri dönüş değeri olarak kendisini çağıran fonksiyona iletir. Yukarıdaki örnekte 2<br />

sayısının 3. kuvveti bu fonksiyon yardımıyla hesaplanarak bulunan değer y<br />

değişkenine atanmıştır.<br />

4


Bazı fonksiyonların geri dönüş değerleri fonksiyonun yürütülmesi sırasında yapılan<br />

işlemlerin başarısı hakkında bilgi verir. Yani bu tür fonksiyonların geri dönüş değerleri<br />

test amacıyla kullanılmaktadır. Geri dönüş değerleri yapılması istenen işlemin başarılı<br />

olup olmaması durumunu açıklar. Örneğin :<br />

p = malloc(200);<br />

ifadesiyle bellekte 200 byte uzunluğunda bir blok tahsis etmek isteyen programcı bu<br />

işlemin başarılı bir biçimde yerine getirilip getirilmediğini de test etmek zorundadır.<br />

Hemen arkasından p değişkeninin aldığı değeri kontrol edecek ve işlemin başarısı<br />

hakkında bir karara varacaktır. Dolayısıyla malloc fonksiyonunun geri dönüş değeri,<br />

fonksiyonun yapması gereken işin başarılı bir şekilde sonuçlanıp sonuçlanmadığını<br />

göstermektedir.<br />

5


Bazı fonksiyonlar kendilerine gönderilen argumanları belirli bir kritere göre test<br />

ederler. Ürettikleri geri dönüş değerleri ise test sonucunu belirtir. Örneğin:<br />

if (isalpha(ch)) {<br />

...<br />

}<br />

Burada isalpha fonksiyonu arguman olarak gönderilen karakterin bir harf<br />

karakteri olup olmadığını test eder. Eğer harf karakteriyse, isalpha fonksiyonu 0<br />

dışı bir değere geri dönecek, eğer harf karakteri değilse 0 değerine geri<br />

dönecektir. Çağıran fonksiyonda da geri dönüş değerine göre farklı işlemler<br />

yapılabilecektir.<br />

6


Bazı fonksiyonlar hem belli bir amacı gerçekleştirirler hem de buna ek olarak<br />

amaçlarını tamamlayan bir geri dönüş değeri üretirler. Örneğin :<br />

x = printf("Merhaba Dünya\n");<br />

Burada printf fonksiyonu ekrana Merhaba Dünya yazısını yazmak için kullanılmıştır.<br />

Ancak ekrana yazdığı karakter sayısını da geri dönüş değeri olarak vermektedir.<br />

Bir yazı içersinde bulunan belirli bir karakteri silecek bir fonksiyon tasarladığımızı<br />

düşünelim. Fonksiyon işini bitirdikten sonra yazıdan kaç karakter silmiş olduğunu geri<br />

dönüş değeri ile çağırıldığı yere bildirilebilir.<br />

7


Bazen geri dönüş değerlerine ihtiyaç duyulmaz. Örneğin yalnızca ekranı silme<br />

amacıyla tasarlanmış olan bir fonksiyonun geri dönüş değerine sahip olması<br />

gereksizdir.<br />

clrscr();<br />

clrscr fonksiyonu yalnızca ekranı siler, böyle bir fonksiyonun geri dönüş<br />

değerine ihtiyacı yoktur.<br />

Fonksiyonların geri dönüş değerlerinin de türleri söz konusudur.<br />

Fonksiyonların geri dönüş değerleri herhangi bir türden olabilir. Geri dönüş<br />

değerlerinin türleri fonksiyonların tanımlanması sırasında belirtilir.<br />

8


Fonksiyonların Tanımlanması<br />

Kendi yazdığımız fonksiyonlar için tanımlama (definition) terimini kullanıyoruz. C'de<br />

fonksiyon tanımlama işleminin genel biçimi şöyledir:<br />

[Geri dönüş değerinin türü] ([parametreler])<br />

{<br />

...<br />

...<br />

}<br />

Yukarıdaki gösterimde açısal parantez içinde belirtilen ifadeler zorunlu olarak<br />

bulunması gerekenleri köşeli parantez içinde belirtilen ifadeler ise bulunması zorunlu<br />

olmayan, isteğe bağlı (optional) ifadeleri göstermektedir. Tanımlanan fonksiyonlar en<br />

az bir blok içerirler. Bu bloğa fonksiyonun ana bloğu denir. Ana blok içinde istenildiği<br />

kadar içiçe blok yaratılabilir. Aşağıdaki fonksiyon tanımlamasından fonk1<br />

fonksiyonunun parametre almadığını ve geri dönüş değerinin de double türden<br />

olduğunu anlıyoruz.<br />

double fonk1()<br />

{<br />

...<br />

... Fonksiyonun ana bloğu<br />

...<br />

}<br />

9


void Anahtar Sözcüğü<br />

Bir fonksiyonun parametre değişkeni ya da geri dönüş değeri olmak zorunda<br />

değildir. Bir fonksiyonun parametre değişkeni olmadığı iki şekilde belirtilebilir:<br />

•Fonksiyon parametre parantezinin içi boş bırakılır, yani buraya hiçbirşey yazılmaz.<br />

•Fonksiyon parametre parantezinin içine void anahtar sözcüğü yazılır.<br />

fonk() fonk(void)<br />

{ {<br />

... ...<br />

} }<br />

Yukarıdaki tanımlamalar C'de aynı anlama gelmiyor. Fonksiyon prototipleri konusunu<br />

öğrenirken bu iki tanımlama arasındaki farkı da öğrenmiş olacağız. Şimdilik bu iki<br />

tanımlamanın aynı anlama geldiğini ve fonksiyonun parametre almadığını<br />

belirttiklerini varsayacağız.<br />

10


Geri dönüş değerine ihtiyaç duyulmadığı durumlarda da geri dönüş<br />

değerinin türü yerine void anahtar sözcüğü yerleştirilir. Örneğin:<br />

void sample(void)<br />

{<br />

...<br />

}<br />

Yukarıda tanımlanan sample fonksiyonu parametre almamakta ve bir<br />

geri dönüş değeri de üretmemektedir.<br />

Fonksiyon tanımlarken geri dönüş değeri yazılmayabilir. Bu durum geri<br />

dönüş türünün olmadığı anlamına gelmez. Eğer geri dönüş değeri<br />

yazılmazsa, C derleyicileri tanımlanan fonksiyonun int türden bir geri<br />

dönüş değerine sahip olduğunu varsayarlar. Örneğin :<br />

sample2()<br />

{<br />

...<br />

}<br />

Tanımlanan sample2 fonksiyonunun parametresi yoktur ama int türden<br />

bir geri dönüş değeri vardır.<br />

11


C dilinde fonksiyon içinde fonksiyon tanımlanamaz!<br />

Örneğin aşağıdaki durum error oluşturur, çünkü sample2 fonksiyonu<br />

sample1 fonksiyonunun içinde tanımlanmıştır:<br />

double sample1()<br />

{<br />

...<br />

int sample2() /* error */<br />

{<br />

...<br />

}<br />

...<br />

}<br />

tanımlamanın aşağıdaki şekilde yapılması gerekirdi :<br />

double sample1()<br />

{<br />

...<br />

}<br />

int sample2()<br />

{<br />

...<br />

}<br />

12


Fonksiyonların Çağırılması (function calls)<br />

C dilinde fonksiyon çağırma operatörü olarak () kullanılmaktadır. Bir fonksiyon<br />

çağırıldığı zaman programın akışı fonksiyonu icra etmek üzere bellekte fonksiyonun<br />

kodunun bulunduğu bölgeye atlar, fonksiyonun icra edilme işlemi bittikten sonra da<br />

akış tekrar çağıran fonksiyonun kalınan yerinden devam eder.<br />

Bir fonksiyonun geri dönüş değeri varsa, fonksiyon çağırma ifadesi geri dönüş değerini<br />

üretir.<br />

Geri dönüş değeri bir değişkene atanabileceği gibi doğrudan aritmetik işlemlerde de<br />

kullanılabilir. Örneğin:<br />

sonuc = hesapla();<br />

Burada hesapla fonksiyonunun çağırılma ifadesiyle üretilen geri dönüş değeri sonuc<br />

değişkenine atanmaktadır. Bir başka deyişle bir fonksiyon çağırma ifadesinin ürettiği<br />

değer, ilgili fonksiyonun ürettiği (eğer üretiyorsa) geri dönüş değeridir. Yukarıdaki<br />

örnekte önce hesapla() fonksiyonu çağırılacak daha sonra fonksiyonun icra<br />

edilmesiyle oluşan geri dönüş değeri sonuc değişkenine atanacaktır.<br />

13


Fonksiyonlar ancak tanımlanmış fonksiyonların içerisinden çağırılabilirler. Blokların<br />

dışından fonksiyon çağırılamaz. Çağıran fonksiyon ile çağırılan fonksiyonun her ikisi de<br />

aynı amaç kod içerisinde bulunmak zorunda değildir. Çağıran fonksiyon ile çağırılan<br />

fonksiyon farklı amaç kodlar içerisinde de bulunabilir. Çünkü derleme işlemi sırasında<br />

bir fonksiyonun çağırıldığını gören derleyici, amaç kod içerisine (yani .obj içine)<br />

çağırılan fonksiyonun adını ve çağırılış biçimini yazmaktadır. Çağıran fonksiyon ile<br />

çağırılan fonksiyon arasında bağlantı kurma işlemi, bağlama aşamasında, bağlayıcı<br />

program (linker) tarafından yapılır.<br />

Bu nedenle tanımlanan bir fonksiyon içerisinde, var olmayan bir fonksiyon çağırılsa bile<br />

derleme aşamasında bir hata oluşmaz. Hata bağlama aşamasında oluşur. Çünkü<br />

bağlayıcı çağırılan fonksiyonu bulamayacaktır.<br />

Bütün C programları çalışmaya main fonksiyonundan başlar. Programın başladığı<br />

nokta olma dışında main fonksiyonunun diğer fonksiyonlardan başka hiçbir farkı<br />

yoktur. main fonksiyonun icrası bitince program da sonlanır. Bir C programının<br />

çalışabilmesi için mutlaka bir main fonksiyonuna sahip olması gerekir.<br />

14


Değer döndürmeyen bir fonksiyonun tanımlanması ve çağırılması:<br />

15


Değer döndüren bir fonksiyona örnek:<br />

16


eturn İfadesi<br />

Bir fonksiyonun geriye değer döndürüp döndürmemesi, o fonksiyonu genel yapı<br />

içersinde nasıl kullanacağınıza bağlıdır. Eğer hazırlayacağınız fonksiyonun, çalışıp,<br />

üreteceği sonuçları başka yerlerde kullanmayacaksanız, fonksiyondan geriye değer<br />

dönmesi gerekmez. Ancak fonksiyonun ürettiği sonuçları, bir değişkene atayıp<br />

kullanacaksanız, o zaman fonksiyonun geriye değer döndürmesi gerekir. Bunun için<br />

'return' ifadesini kullanırız.<br />

Geriye değer döndürmeyen fonksiyonları tanımlarken, başına void koyuyorduk. Geriye<br />

değer döndüren fonksiyonlar içinse, hangi tipte değer dönecekse, onu fonksiyon<br />

adının başına koyuyoruz. Diyelim ki fonksiyonumuz bir tamsayı döndürecekse, int; bir<br />

karakter döndürecekse char diye belirtiyoruz. Fonksiyon içersinden neyin döneceğine<br />

gelince, burada da return ifadesi devreye giriyor.<br />

Fonksiyonun neresinde olduğu farketmez, return sonuç döndürmek üzere kullanılır.<br />

Döndüreceği sonuç, elle girilmiş veya değişkene ait bir değer olabilir. Önemli olan<br />

döndürülecek değişken tipiyle, döndürülmesi vaad edilen değişken tipinin<br />

birbirinden farklı olmamasıdır. Yani int kup_hesapla( ) şeklinde bir tanımlama<br />

yaptıysanız, double tipinde bir sonucu döndüremezsiniz. Daha doğrusu<br />

döndürebilirsiniz ama program yanlış çalışır. Tip uyuşmazlığı genel hatalardan biri<br />

olduğu için, titiz davranmanız gerekir.<br />

17


Dikkat edilmesi gereken bir diğer konu; return koyduğunuz yerde, fonksiyonun<br />

derhâl sonlanmasıdır. Fonksiyonun kalan kısmı çalışmaz.<br />

Geriye değer döndürmeyen fonksiyonlar için de aynı durum geçerlidir, onlarda<br />

da return ifadesini kullanabilirsiniz. Değer döndürsün, döndürmesin yazdığınız<br />

fonksiyonda herhangi bir yere 'return;' yazın. Fonksiyonun bu noktadan<br />

itibaren çalışmayı kestiğini fark edeceksiniz.<br />

Bu fonksiyonu çalıştırmanın uygun olmadığı şartlarda, kullanabileceğiniz bir<br />

yöntemdir. Bir kontrol ekranında, kullanıcı adı ve/veya şifresini yanlış<br />

girildiğinde, programın çalışmasını anında kesmek isteyebilirsiniz. Böyle bir<br />

durumda 'return;' kullanılabilir.<br />

18


Fonksiyon Prototipleri<br />

Bildiğiniz gibi fonksiyonlarımızı, main( ) üzerine yazıyoruz. Tek kısa bir<br />

fonksiyon için bu durum rahatsız etmez; ama uzun uzun 20 adet fonksiyon<br />

olduğunu düşünün. main( ) fonksiyonu sayfalar dolusu kodun altında kalacak<br />

ve okunması güçleşecektir. Fonksiyon prototipleri burada devreye girer.<br />

Daha önce yazdığımız programı tekrar yazalım. Ama bu sefer, fonksiyon<br />

prototipi yapısına uygun olarak bunu yapalım.


Yukarıdaki programda<br />

alan( ) fonksiyonunu, main( ) fonksiyonundan önce yazmadık. Sadece<br />

böyle bir fonksiyon olduğunu ve alacağı parametre tiplerini bildirdik.<br />

( İsteseydik parametre adlarını da yazabilirdik ama buna gerek yok. )<br />

Daha sonra main( ) fonksiyonu altına inip, fonksiyonu yazdık.<br />

20


Elektrik Alan hesaplayan fonksiyon<br />

21


Bu fonksiyonun prototip ile yazılması<br />

22


Rekürsif Fonksiyonlar<br />

Bir fonksiyon içersinden, bir diğerini çağırabiliriz. Rekürsif fonksiyonlar,<br />

fonksiyon içersinden fonksiyon çağırmanın özel bir hâlidir. Rekürsif fonksiyon<br />

bir başka fonksiyon yerine kendisini çağırır ve şartlar uygun olduğu sürece bu<br />

tekrarlanır. Rekürsif, Recursive kelimesinden geliyor ve tekrarlamalı,<br />

yinelemeli anlamını taşıyor. Kelimenin anlamıyla, yaptığı iş örtüşmekte.<br />

Rekürsif fonksiyonları bir kenara bırakığ, bildiğimiz yöntemle 1, 5, 9, 13<br />

serisini oluşturan bir fonksiyon yazalım:<br />

23


Bu fonksiyonu yazmak oldukça basitti. Şimdi aynı işi yapan rekürsif bir<br />

fonksiyon yazalım:<br />

Son yazdığımız programla, bir önce yazdığımız program aynı çıktıları üretir.<br />

Ama birbirlerinden farklı çalışırlar.<br />

24


İkinci programın farkını akış diyagramına bakarak sizler de görebilirsiniz.<br />

Rekürsif kullanım, fonksiyonun tekrar tekrar çağrılmasını sağlamıştır.<br />

25


Faktöriyel hesaplayan fonksiyonu, rekürsif olarak yazalım:<br />

26


faktöriyel hesaplaması yapılırken, adımları görmenizi istiyorum. Adım olarak<br />

geçen her kutu, fonksiyonun bir kez çağrılmasını temsil ediyor. Başlangıç<br />

kısmını geçerseniz fonksiyon toplamda 5 kere çağrılıyor.<br />

27


Rekürsif yapılar, oldukça karmaşık olabilir. Fakat kullanışlı oldukları kesin.<br />

Örneğin silme komutları rekürsif yapılardan yararlanır. Bir klasörü altında<br />

bulunan her şeyle birlikte silmeniz gerekiyorsa, rekürsif fonksiyon<br />

kaçınılmazdır. Ya da bazı matematiksel işlemlerde veya arama ( search )<br />

yöntemlerinde yine rekürsif fonksiyonlara başvururuz. Bunların dışında<br />

rekürsif fonksiyonlar, normal fonksiyonlara göre daha az kod kullanılarak<br />

yazılır. Bunlar rekürsif fonksiyonların olumlu yönleri... Ancak hiçbir şey<br />

mükemmel değildir.<br />

Rekürsif fonksiyon kullanmanın bilgisayarınıza bindereceği yük daha fazladır.<br />

Faktoriyel örneğine bakın; tam 5 kez aynı fonksiyonu çağırıyoruz ve bu sırada<br />

bütün değerler bellekte tutuluyor. Eğer çok sayıda iterasyondan söz ediyorsak,<br />

belleğiniz hızla tükenecektir. Rekürsif yapılar, bellekte ekstra yer kapladığı gibi,<br />

normal fonksiyonlara göre daha yavaştır. Üstelik kısa kod yazımına karşın,<br />

rekürsif fonksiyonların daha karmaşık olduklarını söyleyebiliriz. Anlamak zaman<br />

zaman sorun olabiliyor. Kısacası bir programda gerçekten rekürsif yapıya<br />

ihtiyacınız olmadığı sürece, ondan kaçınmanız daha iyi!<br />

28


Matematiksel İşlemler<br />

Matemetik kütüphanesi math.h kullanılarak kütüphane içerisindeki hazır<br />

matematiksel işlemler yapılabilir.<br />

Sıkça kullanılan matematiksel fonksiyonlar.<br />

• double ceil( double n ) : Virgüllü n sayısını, kendisinden büyük olan ilk tam<br />

sayıya tamamlar. Örneğin ceil(51.4) işlemi, 52 sonucunu verir.<br />

• double floor( double n ) : Virgüllü n sayısının, virgülden sonrasını atarak, bir<br />

tam sayıya çevirir. floor(51.4) işlemi, 51 sayısını döndürür.<br />

• double fabs( double n ) : Verilen n sayısının mutlak değerini döndürür. fabs(-<br />

23.5), 23.5 değerini verir.<br />

• double fmod( double a, double b ) : a sayısının b sayısına bölümünden kalanı<br />

verir. (Daha önce gördüğümüz modül (%) operatörü, sadece tam sayılarda<br />

kullanılırken, fmod fonksiyonu virgüllü sayılarda da çalışır.)<br />

29


• double pow( double a, double b ) : Üstel değer hesaplamak için kullanılır;<br />

a b değerini verir.<br />

• double sqrt( double a ) : a'nın karekökünü hesaplar.<br />

Bu fonksiyonlardan herhangi birini kullacağınız zaman, program kodununun<br />

başına #include yazmalısınız.<br />

Ayrıca derleyici olarak gcc'yle çalışıyorsanız, derlemek için -lm parametresini<br />

eklemeniz gerekir. (Örneğin: "gcc –o –lm test test.c " gibi...)<br />

30


Üslü sayılarla işlem yapmak<br />

Programlarınızda kullandığınız değişkenler veya sabitler her zaman tam sayı<br />

veya ondalık şeklinde yazabileceğiniz sayılar olmayabilir. Bazen pozitif veya<br />

negatif üslü sayılarla işlem yapmanız gerekebilir.<br />

Örneğin elektrik alan hesabında kullanmanız gereken k=8,99x 10 9 sabitini<br />

programınızda nasıl yazarsınız bunu göreceğiz.<br />

k=8,99e+9<br />

Program içerisinde üslü sayı yazarken üslü kısım yerine “e” yanına da<br />

üssün kaç olduğu yazılır.<br />

Örneğin elektronun yükünü yazmak için e=1,6x 10 -19<br />

e=1,6e-19 yazarsınız<br />

31


Elektrik alan hesaplayan programı inceleyelim<br />

32


Programın sonucunu dosyaya yazdırma.<br />

Programda yaptığımız işlemlerin sonucunu çok büyük olduğunda veya sonuçları<br />

saklamak istediğimizde bunları bir dosyaya yazdırabiliriz.<br />

Bunun için program içerisinde sonuçların kaydedileceği bir dosya tanımlamamız<br />

gerekir.<br />

FILE *output;<br />

Bu şekilde dosyayı tanımladıktan sonra program içerisinde programın<br />

sonuçları kaydedeceği yere bir isim verip bunu açmasını programa iletmemiz<br />

gerekiyor.<br />

Dosyayı açtıktan sonra yapacağımız işlem sonuçları bu dosyaya yazdırmak<br />

olacaktır.<br />

Son olarak ta açtığımız bu dosyayı kapatmamız gerekiyor. Bunun içinde<br />

kullanacağımız komut.<br />

33


Bu yazdıklarımızı bir örnekle açıklayalım.<br />

Program bize sonucun yazdırıldığı alan.txt adında bir dosya verecektir.<br />

34


Potansiyel enerji hesaplayan ve sonucu dosyaya yazdıran bir program yazalım<br />

35


Potansiyel enerji hesaplayan ve sonucu dosyaya yazdıran bir program yazalım<br />

36


Fahrenheit ı Celcius a çeviren bir program yazın.<br />

37

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!