24 Ocak 2017 Salı

Bit İşlemleri

Aşağıda bitlerle çalışırken aldığım notlar var. Aslında tüm işlemler temel olarak şöyle yapılıyor.

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

1. Okuma İşlemleri
Okuma işlemi verilen sayının belli bir bit aralığını almak içindir. Temel iki yöntem vardır. İlk yöntem shift işlemini kullanır. İkinci yöntem ise mask işlemini kullanır.

LSB okumak için Mask'lamak Shift İşleminden Daha Hızlıdır
Bazen bir sayının ilk N tane LSB bitini okumak isteriz. Sayıyı toplam bit sayısı - N defa sola kaydırarak gereksiz bitlerden kurtulamak ve daha sonra toplam bit sayısı - N defa sağa kaydırarak istediğimiz bitlere erişmek mümkün. Örneğin 64 bitlik bir sayıda LSB 32 bit şöyle okunur.
lower = (64bitvar << 32) >> 32;
Ancak bu işlem yavaştır. Mask'lamak her zaman daha hızlıdır.

Ortadan Bitleri Okumak
Mask tablosu kullanmak en iyi yöntem.

Ortadan Tek Bir Biti Okumak İçin Sola Kaydırmak - Kullanmayın
Mask tablosunu kullanmayı tercih etmek gerekir. Ancak istenirse shift işlemi ile mask dinamik olarak yaratılabilir.

Örnek:
bit (i) = byte & (1U << i)
Ortadan Tek Bir Biti Okumak İçin Sağa Kaydırmak - Kullanmayın
Mask tablosunu kullanmayı tercih etmek gerekir. Ancak istenirse tek bir biti okumak için aşağıdaki gibi yapılabilir.
bit(i) = byte >> i & 0x1

Sağa kaydırırken, signed tipleri kullanmamak gerekir, çünkü C dili sağa kaydırmanın MSB'de bulunan eksi sayı bitini muhafaza edip etmeyeceğini belirtmez. Örneğin 10000000 kaydırılınca 11000000 olmak zorunda değildir. 01000000 de olabilir. Bu yüzden statik analiz araçları şuna benzer bir hata verirler.
"Left hand operand of right-shift operator is a variable of signed int type".


2. Diğer Notlar

Shift İşlemi Sonucu int veya unsigned int'tir - Integer Promotion
C/C++ kullanırken yapılan en büyük yanlışlardan birisi shift işleminin sonucunun shift edilen tip ile aynı olduğunu varsaymak. C Integer Promotion başlıklı yazıya bakınız.

Negate Operatorünün Sonucu int veya unsigned int'tir - Integer Promotion
C/C++ kullanırken yapılan en büyük yanlışlardan birisi shift işleminin sonucunun shift edilen tip ile aynı olduğunu varsaymak. C Integer Promotion başlıklı yazıya bakınız. Açıklama

The result of the ~ operator is the bitwise complement of its (promoted) operand (that is, each bit in the result is set if and only if the corresponding bit in the converted operand is not set). The integer promotions are performed on the operand, and the result has the promoted type. If the promoted type is an " 'unsigned type', the expression ~E is equivalent to the maximum value representable in that type minus E".


BitMask için Unsigned Int Kullanmak
Bitmask değerini tutmak için int kullanmak sakıncalı diye bir not gördüm.
int mask = 10101010101010101010101010101010
Bitmask değerlerini tutmak için unsigned int kullanılmalıymış.

Negatif Sayılar
Bit İşlemleri yazısına taşıdım.

Sağa Kaydırmada Verinin Bit Uzunluğunu Aşmamak
Sağa kaydırılan veri 32 bit uzunluğunda ise, en fazla 31 bit sağa kaydırılabilir. 32 veya üstü bir sayı kadar kaydırma denenirse, sonuç tanımsızdır (undefined). Hatta Intel işlemciler, sağa kaydırma işlemini bile yapmazlar.

Tüm bitleri 1 yapmak
Örnek:
int64_t x = -1 veya uint64_t x = ~0;   
Belli Sayıda bitleri 1 yapmak
Örnek
int32_t x ~( ~0u << n )
Eğer belli bir pozisyondan başlayarak n tane biti 1 yapmak istersek
int32_t x = ~( ~0u << n ) << m

Verilen Biti Temizlemek
Negate operatörünü kullanmak gerekir. Aşağıdaki örnekte sadece 0x40 alanı temizleniyor, diğer alanlarda değişiklik yapılmıyor. Aynı kullanım şeklini BitStream sınıfının Write() metodunda da görebilirsiniz. Örnek:
data &= ~0x40;
data |= 0 << 15 şeklinde bit temizlenemez!.

Verilen Bitleri Tersine Çevirmek
Hiç kullanmam gerekmedi ancak 32 bitlik bir alanın ilk 16 bitini tersine çevirmek için aşağıdaki gibi yapılabilir. XOR kullanılarak 1 olan alan 0, 0 olan alan ise 1 yapılır.
crc = crc ^ ~0U;
FitsBits
Şöyle yaparız.
int fitsBits(int x, int n) {
  int res = -1;
  if(x >= 0){
    res = (x >> (n - 1));
  }
  else if (x < 0){
    res = (~x >> (n - 1));
  }
  return !res;
}
Reverse
Rotate ile karıştırmamalı. 110 gibi bir biti 011 olacak şekilde başını sonuna getirir.

Power of two
unsigned bir değerin ikinin katı olup olmadığını aşağıdaki gibi anlayabiliriz.
(n & (n - 1)) == 0

Is Even or Odd Power of two
n sayısının aşağıdaki sabit ile and'lenmesi sonucu üssü'nün çift olup olmadığını buluruz. Sonuç 0 ise üssü tektir.
n & 0x55555555

Modulus Operatorü Yerine Bitmask
Aşağıdaki örnekte modulus yerine bit işlemi yapılmış.
x++
x = x & 1023 işlemi aslında

x = (x + 1) % 1024 anlamına geliyor.

Java
Bit İşlemleri yazısına taşıdım.

C
C dilinde bir struct içinde bitfield'lar tanımlanabilir. Bitfield signed veya unsigned olabilir.
A bit-field may have type int , unsigned int , or signed int . Whether the high-order bit position of a plain int bit-field is treated as a sign bit is implementation-defined
Signed alan kullanılması aslında anlamlı değil.Aşağıdaki örnekte çıktı olarak 1 yerine -1 alırız.
#include<stdio.h>
int main(){
struct A{
    int a:1;
};
struct A bb;
bb.a=1;
printf("%d",bb.a);//-1 verir
return 0;
}

Bitfield derleyici tarafından integral bir tipin katı olacak şekilde pad'lenir. Örnek:
struct S {
  unsigned char b1:3;  // 3 bits - b1
  unsigned char   :2;  // 2 bits - unused
  unsigned char b2:6;  // 6 bits - b2
  unsigned char b3:2;  // 2 bits - b3
                       // Total: 13 bits
};                     // 3 bits - unused (implicit padding)

BitStream Sınıfı
Endianness byte'tan büyük herhangi bir tipe byte byte erişmeye başlayınca yani pointer arithmetic kullanınca işin içine girer.  Bu yüzden BitStream sınıfında pointer arithmetic yerine bit kaydırma yapılır.
Bit kaydırma işlemi endianness'tan bağımsızdır.

Java
Bit İşlemleri yazısına taşıdım.

Alternatif olarak  String kullanan basit bir BitStream sınıfı ise aşağıda.
String bitStream = Long.toBinaryString (value);
int numBytes = bitStream.length () / 8;
for (int index = 0; index < numBytes; index++){
    int byteIndex = index * 8;
    String singleByte = bitStream.substring (byteIndex , 8);
    StringBuffer buf = new StringBuffer (singleByte);
    singleByte = buf.reverse().toString();
    int byteValue = Integer.valueOf (singleByte,2);
}
C#
Burada hazır bir sınıf var. Ayrıca BitArray sınıfı da işe yarar.
BitArray bits = new BitArray(1000); // lots of bits
bits[1] = true;

C++
Gömülü Proje - BitStream yazısına taşıdım.

23 Ocak 2017 Pazartesi

exec ailesi

Not : fork() sistem çağrısı başlıklı yazıda std::system() çağrısının altta execl sistem çağrısını kullandığını görebilirsiniz.

Not 2: Windows'ta C kütüphanesinden _spawn, _wspawn ailesine ait metodlar exec ailesine çok benziyor.

exec sistem çağrısı ailesi
exec ailesindeli metodlar şöyle. v ile bitenler çalıştırılacak uygulamaya geçilecek parametreleri bir char dizisi ile alıyorlar. l ile bitenler ise parametreleri variable argument list olarak alıyorlar.
execl
execle
execlp
execv
execve
execvp
Aşağıdaki şekli buradan aldım. Şekilde execlp ve execvp'nin çalıştırılmak istenen uygulamayı $PATH ortam değişkeni altında arayacağı görülebilir.


execl
execl metodu yazısına taşıdım.

execle
Örnek ver

execlp
Çalıştırılmak istenen uygulam $PATH dizininde olmalıdır. Şöyle yaparız.
std::string str("...");
execlp("curl", "-b", "cookie.txt", "-d", str.c_str());

execv
execv metodu yazısına taşıdım.

execve
execve yazısına taşıdım.

execvpe
execvpe Linux üzerinde bulunuyor ancak standart bir metod değil.

execvp - Programı path içinde arar
Şu satırın dahil edilmesi gerekir.
#include <unistd.h>
Metodun imzası şöyle
int execvp(const char *file, char *const argv[]);
argv bir char* array. Son elemanının null olması gerekiyor.  C tarzı programlarda şöyle yaparız. Önce parametre dizisini doldururuz.
// The argument list to pass to the “ls” command.
 char* arg_list[] = { 
   “-l”, 
    “/”,
    NULL // The argument list must end with a NULL
  };
Daha sonra execvp'yi çağırırız.
execvp ("ls", arg_list);
C++ ile parametrelerin büyüklüğünü tam bilmediğimiz bir argv dizisini oluşturmak için bir yöntem şöyle
// given:
std::vector<std::string> args = the_args();
std::vector<char*> argv(args.size() + 1);
std::transform(args.begin(), args.end(), argv.begin(),
  [](std::string& s) { return const_cast<char*>(s.c_str()); });

int error = execvp(path, argv.data());
exec ailesi ve threadler
exec ailesinden bir metod çalıştırılınca eski uygulamadaki exec() metodunu çağıran thread hariç geri kalan tüm threaler kapatılır. man exec sayfasında şu yazıyor.
All threads other than the calling thread are destroyed during an execve().
Bir sınav sorusu şöyle
What happens to the other threads, if one of many threads within a process makes an exec() call? Why?
Cevabı ise şöyle
A call to any exec function from a process with more than one thread shall result in all threads being terminated and the new executable image being loaded and executed. No destructor functions or cleanup handlers shall be called.
Yani exec(...) çağrısından sonra tek bir thread yaşamaya devam ediyor.

exec ailesi ve sinyaller
Set ignored signals to SIG_DFL before execvp() sorusunda da anlatıldığı gibi yeni açılan uygulama eski uygulamadaki dikkate alınmayan sinyalleri dikkate almıyor. Ancak eski uygulamadaki atanmış sinyalleri SIG_DFL olarak atıyor. Yani default handler'ı geri atıyor.


exec ailesi ve programı çıktısı
Bu konudaki en önemli doküman Process sınıfındaki açıklama.Yani çıktısı olan programların çıktılarını okumakta fayda var.


OLSR

Giriş
Link State Routing (LSR) aslında bir başlık veya aile gibi düşünülebilir. Bu başlık altında  OLSR,  IS-IS gibi yönlendirme protokolleri bulunuyor. Tüm bu protokollerin ortak ve kendine mahsus özellikleri var.
LSR protokollerinin ortak özelliklerinden birisi, tüm ağ için bir topoloji oluşturulması ve kendilerini topolojinin başlangıç düğümü olarak görmeleri.
Link-state routing protocols, such as OSPF and IS-IS, create a topology of the network and place themselves at the root of the tree.
Bir başka sınıflandırma şöyle 
Proactive Routing : DSDV, OLSR, STAR
Reactive Routing : AODV, DSR
Hybrid Routing : ZRP

OLSR Nedir?
Optimized Link State Routing (OLSR) Mobile Ad Hoc Network (MANET) veya Mesh ağlarında paket yönlendirmek için kullanılan yönlendirme algoritmalarından bir tanesi.

OLSRv1
OLSRv1 RFC3626 ile tanımlı.
Mobile Ad Hoc Networks RFC2501 ile tanımlı.

OLSRv2
OLSRv2 RFC7181 ile tanımlı.
OLSRv2 için Link Metrics RFC7185 ile tanımlı.

802.11 Ad Hoc Nedir?
MANET denilince kafalarda farklı şeyler oluşuyor. İçinde en çok MANET geçen konulardan bir tanesi de 802.11 ailesi. Bu yüzden bu başlığı yazma lüzumunu duydum.

802.11'de tanımlı olan MANET ağı, bu yazıdaki MANET ağından biraz daha farklı. 802.11'de normalde bir Access Point aracılığıyle iletişim kurulur. Buna Infrastructure Mode denir. Ad Hoc mod'da ise A cihazı B cihazı ile Access Point olmadan konuşabilir.

Ancak routing mantığı yoktur. Eğer A cihazı B cihazının kapsamasına giriyorsa, C ise A'dan uzakta ancak B'nin kapsama alanında ise A ve C iletişim kuramazlar. Yani peer to peer çalışır.

Diğer Ad Hoc Yönlendirme Algoritmaları Nelerdir?
Yönlendirme Algoritmaları şöyle sınıflandırılıyor.


Reactive Yönlendirmek Algoritması Nedir?
Reactive Yönlendirme Algoritmaları belli aralıklarla değil sadece gerekince ağı keşfetmeye çalışırlar.
With reactive protocols, a node discovers routes on-demand and maintains only active ones. Thus, a route is discovered whenever a source node needs to communicate with a destination node for which a route is not available. This discovery mechanism is based on pure flooding [1] in the network. Two reactive protocols are on the standard track or established RFCs: AODV and DSR.

Proactive Yönlendirmek Algoritması Nedir?
OLSR Proactive(table driven) başlığı altına düşüyor. Proactive çalışmak belli aralıklarla veya ağda değişiklik tespit edilince, elindeki bilgileri ağa yayınlamak anlamına gelir.
OLSR (Optimized Link State Routing) is a proactive routing protocol that is now an RFC. It provides the advantage of having routes immediately available in each node for all destinations in the network. Periodic control packets are in charge of monitoring the network topology. This class of protocol is particularly well suited for applications where all nodes can use the topology knowledge discovered by the routing protocol. Moreover, proactive routing protocols can be used without modification in the network protocol stack.
Bir başka a açıklama şöyle 
Proactive routing protocols are based on shortest path algorithns. It maintains and updates information on routing among all nodes of a given network at all times even if the paths are not currently being used. Thus, even if some paths are never used updates regarding such paths are constantly broadcasted among nodes. Route updates are periodically performed regardless of network load,  bandwidth constraints. 

Ben bu algoritmaları biraz ARP'a benzetiyorum. Nasıl ki ARP tam IP için MAC adresi gerektiğinde yapılıyor, bu algoritmalar da aynen routing bilgisi gerektiğinde yapılıyor.

OLSR Gerçekleştirimleri
NRL  olsrd, QOLSR, UniK olsrd gibi birden fazla OLSR gerçekleştirimi var.

OLSR Layer 3'tedir
OLSR forwarding ve  routing yeteneği sağlar. OSI modelinde Layer 3'tedir. OLSR'da Layer 2 kavramı yoktur. OLSR UDP ile çalışır ve Hello paketlerini 255.255.255.255 adresine ve 698 numaralı port'a gönderir.

OLSR ve İşletim Sistemi Yönlendirme Tabloları (Routing Table)
OLSR hesapladığı yolları işletim sistminin yönlendirme tablolarına gönderir. Linux'ta bu iş için Net Link Socket kullanır. Diğer Unix sistemlerinde ioctl ve SIOCADDRT seçeneğini kullanır.

Tüm Katılımcılar Bir Router'dır
OLSR'da tüm katılımcılar aynı zamanda birer router'dır.
Every node is potentially a router in a MANET, while most nodes in traditional wired networks do not route packets. Nodes transmit and receive their own packets and, also, forward packets for other nodes
Next Hop Nedir
Detaylara girmeden önce bazı kavramlara bakalım. Next Hop bir paketin gerçek hedefine (destination) ulaşması için kullanılması gereken katılımcıdır. Her katılımcı, diğer katılımcılara ulaşmak için kullanması gereken next hop katılımcıyı sürekli hesaplar.

Duplicate Detection Nedir
Döngü (Loop) static yönlendirme protokollerinde bile kazara yapılabiliyor. OLSR gibi daha dinamik ortamlarda olması zaten beklenen bir şey.

Döngülerden kaçınmak için OLSR belli bir süre geçmişi hatırlar. Örneğin 1 saniye veya x sayıda paket. Daha önce aldığı bir paket broadcast yüzünden tekrar kendisine gelirse duplicate detection bu paketi yakar ve işlemez.

Duplicate detection yönlendirme algoritmasının ilk adımı olabilir.

OLSR'da 3 Tablo Kullanılır
Bu tablolar mantıksal olarak 3 tanedir. Programlama yaparken belki tek bir veri yapısı bile yeterli olabilir.

1. Neighbor Sensing - One Hop Neigbor List
Her katılımcı kendi one hop komşularını sürekli takip eder. Komşuları symmetric veya asymmetric olarak sınıflandırır ve ağa söyler. Neighbor Sensing için Hello mesajları kullanılır.

The Hello messages are received by all one-hop neighbors, but are not forwarded
Hello mesajları her 2 saniyede bir gönderilir. Eğer bir katılımcıdan 3 Hello gönderim aralığında güncelleme alınmazsa komşuluk bilgisi değişir.

2. Two Hop Neighbor List
OLSR ağlarında tüm katılımcılar 1-hop ve 2-hop komşularını bilirler. Bu tablo MPR seçiminde kullanılır. Two Hop Neighbor List oluşturmak için Hello mesajı ve mesajın içindeki komşuluk bilgileri listesi kullanılır.

A node maintains information (obtained from the HELLO messages) about its one hop neighbors, the status of the link with these neighbors, a list of 2-hop neighbors that these one hop neighbors
give access to, and an associated holding time.
3. Ağ Topolojisi - Topology List
Routing işlemi için bazı bilgilerin katılımcılar arasında paylaşılması gerekir. OLSR Link State Routing mantığını kullanıyor. Bu mantıkta Link State çalıştıran routerlar tüm ağın topoloji bilgisine sahiptirler. Yani Link State routing tabloları herkeste aynıdır. Programlarken her router kendisinin root olduğu bir ağaç veya graph yapısı kullanır.
Each router within the area builds a topology tree of the area, with shortest paths to all other links/routers with itself as the root.
Tüm verinin eşzamanlı olması için proaktif bir yöntem izlenir ve düğümler belli aralıklarla kendi tablolarını yayınlarlar. Her satırda router komşusuna erişim maliyetini belirtir.

Böylece yukarıda bahsedilen gönderilmek istenen paket için Next Hop kolayca bulunur.

Ağ topolojisini oluşturmak için Topology Control (TC) mesajları kullanılır. TC mesajları tüm ağa broadcast edilir. TC mesajını alan bir MPR bu mesajı ileriye doğru forward eder.

Multi Point Relay - MPR
MPR broadcast için. Multi Point Relay yazısına taşıdım.

Multicast
OLSR ile multicast için 3 yöntem var.

1. SMOLSR : Simple Multicast OLSR
2. MOLSR : Multicast OLSR
3. MOST : Multicast Overlay Mininum Spanning Tree






OLSR Multi Point Relay - MPR

Giriş
Açıklaması şöyle
The OLSR backbone for message flooding is composed of Multipoint Relays. Each node must select MPRs from among its symmetric neighbor nodes such that a message emitted by a node and repeated by the MPR nodes will be received by all nodes two hops away. In fact, in order to achieve a network-wide broadcast, a broadcast transmission needs only be repeated by just a subset of the neighbors: this subset is the MPR set of the node. Hence only MPR nodes relay TC, MID, and HNA messages.

OLSR, MANET'lerde broadcast paketlerin flood edilmesini asgariye indirmeye çalışır. Her katılımcı bir MPR - yani kendisi için röle - seçer. MPR'ın görevi broadcast paketleri hem kendisi için işlemek, hem de ağa tekrar göndermektir. Böylece broadcast paketler tüm ağa daha verimli bir şekilde yayılır. Yani flooding yapan katılımcı sayısı azaltılır.

MPR seçimi yapabilmek için Two Hop Neighbor List'in elimizde olması ön koşuldur.


Yöntem 1
MPR seçmek için bir katılımcı 2 hop uzaklıktaki tüm komşularının listesini alır. Sonra bu listedeki her katılımcıya ulaşmak için shortest path'i hesaplar. Shortest path'in ilk düğümü olan kendim komşularımın distinct kümesini alırsak MPR seçimini yapmış oluruz.

2 hop uzaklıktaki komşuları bulmanın kolay bir yolu şöyle. Eğer "routing" hesabı yaparken bir düğüme nasıl gideceğim bilgisi + nereden geldiğim bilgisini saklarsam şöyle yaparız.
foreach node :
  if (nextHop == prevHop) then node is 2 hop neighbor

Yöntem 1 İçin Basit Kodlama
1. Topoloji değişirse MPR hesaplaması en baştan yapılır. Eğer bir katılımcıdan güncelleme alınır ancak topoloji değişmezse MPR hesaplaması yapmaya gerek yoktur.

2. OLSR bir subnette çalışıyorsa en fazla 255 katılımcı olabilir.

3. MPR seçimi için basitçe tüm katılımcılara olan next hop bilgisini bir set'e doldurmak yeterli.

Node isimli bir yapı olsun. Her Node kendi içinde komşularını bilsin. Ayrıca bu düğüme erişmenin maliyet olsun.

Route isimli bir yapı olsun. Bu yapıda Toplam Maliyet, Toplam Hop Sayısı , Next Hop, Previous Hop bilgileri olsun

Önce kendi Node nesnemi gezip- visited - olarak işaretlerim.

Daha sonra erişim maliyeti en küçük olan düğümden başlayarak gezerim. Bu düğüm ilk başta kendi komşum olacaktır. Komşum olduğu icin next hop degeri kendisi olacaktır.

Bir düğümün tüm komşularına olan "toplam " maliyeti hesaplarım. Bu düğümü visited olarak işaretlerim.

A->B->C->D olsun.

A kendisini visited olarak işaretler. B için next hop olarak tabloya B yazar.

Maliyeti en küçük olan B seçilir. B'nin tüm komşuları dolaşılır ve "toplam" maliyet hesaplanır. Ayrıca her komşu için next hop olarak B'ye gidiş next hop değeri yazılır.

Yani C'ye gidiş için next hop B'dir.

Maliyeti en küçük C seçilir. C'nin tüm komşuları dolaşılır ve "toplam" maliyet hesaplanır. Her komşu için next hop olarak C'ye gidiş next hop değeri yazılır. C için next hop B idi. Yani D'ye gidiş için next hop B'dir.

Yöntem 2
2 hop ötedeki komşuluk graph'ını oluştur.
Bottom-up ilerleyerek tek atası olan komşuları MPR seç. Bu komşuları graph'tan sil
Ulaşmak için çok atası olan komşuları seç. Bağlantı sayısı en çok fazla olanı MPR seç.

MPR Örnekleri
Şöyle bir Routing tablomuz şöyle olsun.
6<--> 3<-->Ben >-->1 <--> 4
                                        |->5
4 'e erişmek için  : 1
5 'e erişmek için  : 1
6'ya erişmek için  : 3
kullanırız.
1,1,3 nolu katılımcıların distinct listesi 1 ve 3'tür. Dolayısıyla benim MPR'larım 1 ve 3'tür.

Şimdi şöyle bir ağ düşünelim
           A
           / \
          B   C
         / \
        D   E
           / \
          F   G
A MPR olarak B'yi seçer. B MPR olarak E'yi seçer. A bir paketi broadcast eder. B bu paketi alır. Kendisi işler, sonra tekrar broadcast ederi. E çıkış noktası A olan paketi alır. Kendisi işler ve broadcast eder. F ve G bu paketi alır ve işleler. Böylece tüm ağ broadcast paketini duyar.

Daha karışık bir ağ düşünelim
-------A---------
|    |     |   |    |   |
B  C  D  E  F  G
  \/   \/  \/  \/  \/
  H  I    J   K  L
Bu ağda MPR olarak C,E,F veya C,E,G MPR seçilebilir.