- Operatory logiczne w Javie
- Logiczny operator negacji !
- Logiczne AND - &, a także warunkowe AND - &&
- Logiczne OR jest operatorem |, a warunkowe OR jest operatorem ||
- XOR - wyłączność logiczna OR - operator ^
- Priorytet operacji logicznych
- Złożone wyrażenia logiczne
- Operatory bitowe (bitowe).
- Operatory bitowe &, | i ^
- Dodatkowy kod
- Bitowy operator negacji ~
Operacje logiczne w Javie
Operacje logiczne wykonywane są za pomocą operatorów boolowskich. Przepraszam za tautologię, ale tak właśnie jest. Podstawowe operacje logiczne (w programowaniu i matematyce) można stosować do argumentów logicznych (operandów), a także do tworzenia bardziej złożonych wyrażeń, podobnie jak operacje arytmetyczne na liczbach. Na przykład wyrażenie:
(a | b) | (c < 100) & !(true) ^ (q == 5)
jest złożonym wyrażeniem logicznym posiadającym cztery operandy: (a | b)
, gdzie а
i b
są zmiennymi typu.Z boolean
(c < 100)
(true)
(q == 5)
kolei proste wyrażenie logiczne (a | b)
również składa się z dwóch argumentów operandów. Operand logiczny to wyrażenie, o którym można powiedzieć, że jest prawdziwe lub fałszywe, prawdziwe lub fałszywe . W języku Java operand logiczny jest wyrażeniem typu boolean
lub wartością logiczną, na przykład:
(2 < 1)
— operand logiczny, jego wartość to falsetrue
- operand logiczny, którego wartość jest oczywiście prawdziwaboolean a
- może być także operandem logicznym, np. logicznym aint a = 2
- nie jest operandem logicznym , jest to po prostu zmienna typuint
String a = "true"
nie jest również operandem logicznym . Jest to ciąg znaków, którego wartość tekstowa wynosi"true"
.
- Negacja logiczna , znana również
NOT
jako inwersja. W Javie jest to oznaczone symbolem „!
” przed operandem. Dotyczy jednego operandu. - Logiczne i , to także
AND
spójnik. Wskazane przez&
symbol „ ” pomiędzy dwoma operandami, do których jest zastosowany. - Logicznie lub w Javie , jest to również -
OR
, jest to również alternatywa. W Javie jest to oznaczone symbolem „|
” pomiędzy dwoma operandami. - Ekskluzywne lub ,
XOR
, ścisłe rozłączenie. W Javie jest to oznaczone symbolem „^
” pomiędzy dwoma operandami. - W Javie operatory logiczne obejmują warunek lub , oznaczony jako
||
, a także warunek i -&&
.
==
nie jest uważany za operator logiczny. Uwaga! W Javie operatory logiczne&
,|
a^
także mają zastosowanie do liczb całkowitych. W tym przypadku działają one nieco inaczej i nazywane są bitowymi (lub bitowymi) operatorami logicznymi. O nich - pod koniec artykułu. Przyjrzyjmy się tabeli z krótkim opisem każdego z operatorów logicznych Java, a poniżej opiszemy je bardziej szczegółowo i podamy przykłady kodu.
Operator Java | Nazwa | Typ | Krótki opis | Przykład |
---|---|---|---|---|
! |
Logiczne „nie” (negacja) | Jednoargumentowy | !x oznacza „nie x”. Zwraca wartość true , jeśli operand ma wartość false . Zwraca false , jeśli operand ma wartość true . |
boolean x = true; Następnie // !x == false |
& |
Logiczne AND ( AND , mnożenie) |
Dwójkowy | Zwraca wartość true , jeśli oba operandy mają wartość true . | a = true; b = false; Następnie a & b == false |
| |
Logiczne LUB ( OR , dodanie) |
Dwójkowy | Zwraca wartość true , jeśli przynajmniej jeden z operandów ma wartość true . | a = true; b = false; Następnie a | b == true |
^ |
Logiczne wyłączne LUB ( XOR ) |
Dwójkowy | Zwraca wartość true , jeśli jeden i tylko jeden z operandów ma wartość true . Zwraca wartość false , jeśli oba operandy mają wartość true lub false . Zasadniczo zwraca wartość true , jeśli operandy są różne. | a = true; b = false; Następnie a ^ b == true |
&& |
Warunkowe AND (krótkie logiczne AND) | Dwójkowy | To samo co, & ale jeśli operand po lewej stronie & ma wartość false , ten operator zwraca wartość false bez sprawdzania drugiego operandu. |
|
|| |
Warunkowe LUB (krótkie logiczne LUB) | Dwójkowy | To samo co, | ale jeśli operator po lewej stronie ma wartość true , zwraca wartość true bez sprawdzania drugiego operandu. |
Logiczny operator negacji !
Ten operator jest jednoargumentowy, co oznacza, że ma zastosowanie do pojedynczego wyrażenia logicznego lub argumentu. Jest to bardzo proste do zrozumienia, jak każda negacja: operator po prostu zmienia znaczenie wyrażenia na jego przeciwieństwo. Tablica prawdy lub wyniki wykonania operacji negacji:Wartość A | !A |
FAŁSZ | PRAWDA |
PRAWDA | FAŁSZ |
public class Solution {
public static void main(String[] args) {
boolean a = true;
System.out.println(!a); // tutaj nasze wyrażenie boolowskie odwraca swoją wartość
System.out.println(!false); // wyrażenie niefałszowe, jak można się domyślić, będzie równe... co?
System.out.println(!(2 < 5)); // wyrażenie (2 < 5) jest prawdziwe, więc jego zaprzeczenie jest fałszywe
}
}
Dane wyjściowe programu będą następujące:
false
true
false
Logiczne AND - &, a także warunkowe AND - &&
Do dwóch wyrażeń zostanie zastosowany logiczny AND lub koniunkcja, a jego wynik będzie prawdziwy tylko wtedy, gdy oba operandy będą prawdziwe. Oznacza to, że jeśli jeden z operandówa
or b
ma wartość false , wówczas wyrażenie a & b
będzie fałszywe niezależnie od wartości drugiego operatora. Jeśli wyobrazisz sobie, że prawda to liczba 1, a fałsz to 0, wówczas operator &
działa dokładnie tak samo, jak zwykłe mnożenie. Dlatego logiczne AND jest często nazywane „mnożeniem logicznym”. A tak na marginesie, fakt ten pomaga szybko zapamiętać działanie operatora &
i nie pomylić go z operatorem logicznym lub |
. Tabela prawdy AND, to także wynik pracy operatora&
A | B | a&b |
PRAWDA | PRAWDA | PRAWDA |
PRAWDA | FAŁSZ | FAŁSZ |
FAŁSZ | PRAWDA | FAŁSZ |
FAŁSZ | FAŁSZ | FAŁSZ |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(a & b); // jeśli pomnożymy prawdę przez fałsz, na pewno otrzymamy fałsz
System.out.println(a & c); // prawda dla prawdy będzie prawdą
System.out.println(false & (2 > 5));
System.out.println((2 < 5) & false);
// niezależnie od prawdziwości wyrażenia w nawiasie, w takim przypadku musimy zadowolić się fałszem
}
}
Wynik programu:
false
true
false
false
Operator &&
jest czasami nazywany „krótkim AND”. Daje taki sam wynik podczas pracy z argumentami logicznymi, jak operator &
. Jednak jest różnica w samej jego twórczości. Zatem zauważyłeś już, że jeśli a & b
operand w wyrażeniu () a
jest fałszywy , to sprawdzanie wartości operandu nie ma sensu b
: wynik operacji z pewnością będzie fałszywy . Jeśli więc zasadniczo nie potrzebujemy wartości drugiego operandu, używając go, &&
zmniejszamy zasadniczo liczbę obliczeń w programie. &
Jeśli wszystkie operatory w przykładzie zastąpimy &&
, wynik będzie dokładnie taki sam, ale sam program będzie działał nieco szybciej (choć tego nie zauważymy, bo mówimy o mili-mikro... w skrócie , bardzo małe jednostki czasu).
Logiczne OR jest operatorem |, a warunkowe OR jest operatorem ||
Operator OR w Javie jest reprezentowany przez symbol|
. Do dwóch wyrażeń stosuje się logiczny OR lub alternatywę, a jego wynik będzie fałszywy wtedy i tylko wtedy, gdy oba operandy są fałszywe. Tutaj w pewnym stopniu obserwujemy ten sam obraz, co w przypadku operatora &
, tyle że dokładnie odwrotnie. Oznacza to, że jeśli co najmniej jeden operand ma wartość true , wówczas gwarantuje się, że wyrażenie a | b
będzie prawdziwe niezależnie od wartości drugiego operatora. Jeśli &
zachowuje się jak mnożenie logiczne, to OR jest logicznym dodawaniem, jeśli wyobrazisz sobie, że prawda wynosi 1, a fałsz wynosi 0. Pamiętaj tylko, że dodawanie logiczne działa inaczej niż normalne dodawanie. 1 + 1 w tym przypadku równa się nie 2, ale 1 (liczba 2 w tym systemie po prostu nie istnieje). Czasami przez alternatywę rozumie się maksimum 0 i 1 i w tym przypadku, jeśli przynajmniej jeden operand jest równy 1 ( true ), otrzymamy dokładnie true . Tabela prawdy OR, znana również jako wynik operatora |
:
A | B | | B |
PRAWDA | PRAWDA | PRAWDA |
PRAWDA | FAŁSZ | PRAWDA |
FAŁSZ | PRAWDA | PRAWDA |
FAŁSZ | FAŁSZ | FAŁSZ |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(!a | b); // Skomponuj użycie dwóch operatorów logicznych: a == true, więc !a, jak już wiemy, jest fałszywe.
System.out.println(a | c);
System.out.println((2 < 5) | false); // wyrażenie (2 < 5) jest prawdziwe, co oznacza, że dla dowolnego drugiego operandu otrzymamy wynik prawdziwy
System.out.println((2 > 5) | true);
}
}
Wynik:
false
true
true
true
Jeśli zastosujemy operator warunkowy OR - ||
zamiast |
, otrzymamy dokładnie ten sam wynik, ale podobnie jak w przypadku warunkowego AND &&
, będzie to działać ekonomicznie: jeśli „natrafimy” na pierwszy operand równy true , wartość drugi operand nie jest sprawdzany, ale natychmiast wynik jest prawdziwy .
XOR Java - logiczny wyłączny OR - operator ^
XOR
, dodawanie modulo 2, logiczne XOR, odejmowanie logiczne, ścisłe rozłączenie, uzupełnienie bitowe... operator ^
ma wiele nazw w algebrze Boole'a. Rezultatem zastosowania tego operatora do dwóch operandów będzie prawda , jeśli operandy są różne, i fałsz , jeśli operandy są takie same. Dlatego wygodnie jest to porównać, odejmując zera ( fałsz ) i jedynki ( prawda ). Tabela prawdy XOR
, znana również jako wynik operatora ^
:
Wartość logiczna a | Wartość logiczna b | a^b |
PRAWDA | PRAWDA | FAŁSZ |
PRAWDA | FAŁSZ | PRAWDA |
FAŁSZ | PRAWDA | PRAWDA |
FAŁSZ | FAŁSZ | FAŁSZ |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(!a ^ b); // Skomponuj użycie dwóch operatorów logicznych: a == true, więc !a, jak już wiemy, jest fałszywe.
System.out.println(a ^ c);
System.out.println((2 < 5) ^ false);
System.out.println((2 > 5) ^ true);
}
}
Wynik:
false
false
true
true
Priorytet operacji logicznych
Podobnie jak w matematyce, w programowaniu operatory mają określoną kolejność wykonywania, gdy występują w tym samym wyrażeniu. Operatory jednoargumentowe mają przewagę nad binarnymi, a mnożenie (nawet logiczne) nad dodawaniem. Operatory logiczne umieściliśmy wyżej na liście, im wyższy jest ich priorytet:!
&
^
|
&&
||
&
i |
) mają różne priorytety:
public class Solution {
public static void main(String[] args) {
boolean a = true, b = true, c = false;
System.out.println(a | b & c);
}
Gdybyśmy mieli postępować od lewej do prawej, czyli najpierw zastosować operator, |
a następnie - &
, otrzymalibyśmy wartość false . Ale tak naprawdę, jeśli uruchomisz ten program, będziesz pewien, że wynik będzie prawdziwy , ponieważ operator logiczny AND &
będzie miał wyższy priorytet niż operator logiczny OR |
. Aby uniknąć nieporozumień, należy pamiętać, co &
zachowuje się jak mnożenie, a |
co jak dodawanie. Możesz zmienić kolejność priorytetów. Po prostu używaj nawiasów, tak jak na szkolnej matematyce. Zmieńmy trochę nasz przykładowy kod:
public class Solution {
public static void main(String[] args) {
boolean a = true, b = true, c = false;
System.out.println((a|b)&c);
}
Co słychać? Najpierw stosujemy dodawanie logiczne w nawiasach, a następnie mnożenie. Wynik będzie fałszywy .
Złożone wyrażenia logiczne
Oczywiście możemy łączyć wyrażenia i operatory logiczne. Przypomnijmy sobie wyrażenie z początku artykułu:(a | b) | (c < 100) & !(true) ^ (q == 5)
Teraz nie wygląda to już tak strasznie. Napiszmy program wyświetlający jego wartość, po wcześniejszym ustaleniu wartości a
, i . Przykład obliczenia wartości złożonego wyrażenia logicznego b
с
q
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
int c = 25;
int q = 2;
System.out.println((a|b) | (c < 100) & !(true)^(q == 5));
}
}
Notatka:q
nasza zmienna jest typu int
, ale q == 5
jest to wyrażenie logiczne i jest równe false , ponieważ powyżej zainicjowaliśmy q
liczbą 2. To samo dotyczy zmiennej c
. Ta liczba jest równa 25, ale (c < 100) jest wyrażeniem boolowskim równym true . Wynik tego programu:
true
Złożonych wyrażeń logicznych można używać do testowania bardzo złożonych i rozgałęzionych warunków, ale nie należy ich nadużywać: utrudniają one odczytanie kodu.
Operatory bitowe (bitowe).
Na początku artykułu wspomnieliśmy, że operatory i&
mogą być stosowane w odniesieniu do typów całkowitych Java. W tym przypadku są to operatory bitowe. Nazywa się je również bitowymi, ponieważ jedna cyfra to jeden bit, a te operacje działają w szczególności na bitach. Oczywiście działają one nieco inaczej niż operatory logiczne i aby dokładnie zrozumieć, jak to działa, trzeba wiedzieć, czym jest system liczb binarnych. Jeśli nic o tym nie wiesz lub zupełnie zapomniałeś, proponujemy najpierw przeczytać artykuł Java: bity i bajty i przypomnieć wszystkim, że w systemie liczb binarnych są tylko dwie cyfry - 0 i 1, a wszystkie dane w komputerze jest dokładnie reprezentowana za pomocą warunkowych zer i jedynek. Dowolną liczbę, do której jesteśmy przyzwyczajeni (dziesiętna; dla nich jest 10 różnych cyfr od 0 do 9, za pomocą których zapisujemy dowolne liczby) można przedstawić w systemie liczb binarnych. Możesz przekonwertować liczbę dziesiętną na binarną, stosując dzielenie sekwencyjne na kolumnę, korzystając z podstawy systemu liczbowego (2). Reszty dzielenia w każdym kroku, zapisane w odwrotnej kolejności, dadzą nam pożądaną liczbę binarną. Oto na przykład konwersja liczby dziesiętnej 103 na reprezentację binarną: |
^
Binarny system liczbowy w kursie JavaRush Na kursie JavaRush omawiają system liczb binarnych podczas studiowania zadania MultiThreading (poziom 10, wykład 1), po wykładzie kilka zadań do konsolidacji. Jednak ten temat wcale nie jest trudny i nawet jeśli nie doszedłeś jeszcze tak daleko w kursie, prawdopodobnie sobie z tym poradzisz. |
&
, |
i ^
Java używa również operatorów bitowych:
~
bitowy operator negacji>>
bitowe przesunięcie w prawo>>>
bitowe przesunięcie w prawo bez znaku<<
przesunięcie bitowe w lewo
Operatory bitowe &, | i ^
Spójrzmy na przykład działania tych operatorów. Powiedzmy, że mamy dwie liczby całkowite:int a = 25;
int b = 112;
Musimy zastosować na nich trzy operacje i &
wyświetlić wynik na ekranie. Oto kod programu: |
^
public class Solution {
public static void main(String[] args) {
int a = 25;
int b = 112;
int res1 = a & b;
int res2 = a | b;
int res3 = a ^ b;
System.out.println("a & b = " + res1);
System.out.println("a | b = " + res2);
System.out.println("a ^ b = " + res3);
}
}
Wynik programu jest następujący:
a & b = 16
a | b = 121
a ^ b = 105
Jeśli nie rozumiesz, co się dzieje, wynik wygląda bardzo, bardzo tajemniczo. W rzeczywistości wszystko jest prostsze, niż się wydaje. Operatory bitowe „widzą” liczby operandów w ich postaci binarnej. A następnie stosują operatory logiczne &
, |
czyli ^
do odpowiednich cyfr (bitów) obu liczb. Tak więc, ponieważ &
ostatni bit binarnej reprezentacji liczby 25 logicznie sumuje się z ostatnim bitem binarnej reprezentacji liczby 112, przedostatni bit z przedostatnim i tak dalej: Tę samą logikę można prześledzić w przypadek |
i ^
.
Przesunięcie bitowe w lewo lub w prawo
W Javie istnieje kilka operatorów przesunięcia bitowego. Najczęściej używanymi operatorami<<
są i >>
. Przesuwają binarną reprezentację liczby odpowiednio w lewo lub w prawo, a w przypadku przesunięcia w prawo, zachowując znak (na czym polega zachowanie znaku, wyjaśnimy poniżej). Istnieje inny operator przesunięcia w prawo >>>
. Robi to samo, ale >>
nie zapisuje znaku. Przyjrzyjmy się więc ich pracy na przykładzie. int a = 13
a << 1
przesuwa wszystkie bity binarnej reprezentacji liczby a w lewo o 1 bit. Dla uproszczenia wyobraźmy sobie liczbę 13 w formacie binarnym jako 0000 1101. W rzeczywistości liczba ta wygląda następująco: 00000000 00000000 00000000 00001101, ponieważ Java int
przydziela liczbom 4 bajty, czyli 32 bity. Nie odgrywa to jednak roli w przykładzie, więc w tym przykładzie uznamy, że nasza liczba jest jednobajtowa. Zwolniony bit po prawej stronie jest wypełniany zerami. W wyniku tej operacji otrzymujemy liczbę 26. a << 2
Przesuwa ona wszystkie bity binarnej reprezentacji liczby a
w lewo o 2 bity, a dwa wolne bity po prawej stronie są wypełniane zerami. W efekcie otrzymamy liczbę 52. a << 3
Wynikiem będzie 104... Zauważyliście wzór? Przesunięcie bitowe a
w lewo o n pozycji działa jak pomnożenie liczby a
przez 2 do potęgi n. To samo dotyczy liczb ujemnych. To -13 << 3
da wynik -104. a >> n
przesuwa binarną reprezentację liczby n pozycji w prawo. Na przykład 13 >> 1
przekształca liczbę 1101 w liczbę 0110, czyli 6. 13 >> 2
Wynik wyniesie 3. Oznacza to, że tutaj dzielimy liczbę przez 2 do potęgi n, gdzie n jest liczbą przesunięć w prawo, ale z jednym zastrzeżeniem: jeśli liczba jest nieparzysta, podczas tej operacji wydaje się, że resetujemy ostatni bit liczby. Ale w przypadku negatywnych sytuacja jest nieco inna. Powiedzmy, że spróbujemy sprawdzić, co wyświetli program, jeśli poprosimy go o wykonanie operacji -13 >> 1
. Zobaczysz liczbę -7, a nie -6, jak mogłoby się wydawać. Dzieje się tak ze względu na sposób przechowywania liczb ujemnych w Javie i innych językach programowania. Są one przechowywane w tak zwanym kodzie uzupełniającym. W tym przypadku najbardziej znacząca cyfra (ta po lewej stronie) podawana jest pod znakiem. W przypadku liczby ujemnej najbardziej znaczącą cyfrą jest 1.
Dodatkowy kod
Rozważmy liczbęint a = 13
. Jeśli w programie wypiszesz na konsolę jego binarną reprezentację za pomocą polecenia System.out.println(Integer.toBinaryString(a));
, otrzymamy 1101. W rzeczywistości jest to zapis skrócony, ponieważ numer typu int
zajmuje 4 bajty w pamięci, więc komputer „widzi” go więcej lubię to:
00000000 00000000 00000000 00001101
Najbardziej znaczącą cyfrą jest zero, co oznacza, że mamy liczbę dodatnią. Aby przekonwertować na dodatkowy kod:
-
Liczbę -13 zapisujemy w tzw. „kodzie bezpośrednim”. Aby to zrobić, zmień najbardziej znaczącą cyfrę liczby na 1.
Wynik akcji:10000000 0000000 0000000 00001101
-
Następnie odwracamy wszystkie bity (zmieniamy 0 na 1 i 1 na 0) z wyjątkiem bitu znaku. Właściwie to już to zmieniliśmy.
Wynik akcji:11111111 11111111 11111111 11110010
(tak, kroki 1 i 2 można połączyć, ale lepiej myśleć o tym w ten sposób)
- Do otrzymanej liczby dodaj 1.
Wynik akcji:11111111 11111111 11111111 11110011
-13 >> 1
. Ponieważ nasz operator >>
zachowuje znak, w tej operacji wszystkie wolne bity po lewej stronie są wypełnione nie zerami, ale jedynkami. Zatem zmiana numeru
11111111 11111111 11111111 11110011
jeden bit w prawo, co daje następującą sekwencję bitów:
11111111 11111111 11111111 11111001
Jeśli przekonwertujemy tę liczbę na kod bezpośredni (to znaczy najpierw odejmiemy 1, a następnie odwrócimy wszystkie bity z wyjątkiem pierwszego), otrzymamy liczbę:
10000000 00000000 00000000 00000111
lub -7. Teraz, gdy zrozumieliśmy operator przesunięcia w prawo zachowujący znak, stanie się jasne, czym różni się on od operatora >>>
. a >>> n
— ta operacja jest przesunięciem bez znaku, to znaczy przesuwa binarną reprezentację liczby a
w prawo o n bitów, ale n pustych bitów po lewej stronie wypełnia nie jedynkami, jak w przypadku operatora >>
, ale zerami. Zróbmy operację -13 >>> 1
. Mamy już liczbę -13
w uzupełnieniu do dwóch:
11111111 11111111 11111111 11110011
Przesuwając się w prawo o 1 bit i wypełniając wolny bit zerem, otrzymujemy następującą liczbę:
01111111 11111111 11111111 11111001
Co daje liczbę w zapisie dziesiętnym 2147483641
.
Bitowy operator negacji ~
Ten operator jednoargumentowy działa bardzo prosto: odwraca każdy bit binarnej reprezentacji liczby całkowitej. Weźmy liczbę-13
:
11111111 11111111 11111111 11110011
Operacja negacji bitowej ~13
po prostu odwróci wartość każdego bitu. W rezultacie otrzymujemy:
00000000 00000000 00000000 00001100
Lub 12
w formie dziesiętnej.
Krótkie wnioski
- Wszystkie operatory logiczne mają zastosowanie do wyrażeń boolowskich, czyli takich, o których można powiedzieć, że są prawdziwe lub fałszywe .
- Jeśli operatory
&
lub są stosowane do liczb, nie mówimy już o operacjach logicznych, ale o operacjach bitowych|
.^
Oznacza to, że obie liczby są konwertowane do systemu binarnego, a operacje logicznego dodawania, mnożenia lub odejmowania są stosowane do tych liczb krok po kroku. - W logice matematycznej operatory
&
i|
odpowiadają koniunkcji i alternatywie. - Logiczne AND jest podobne do mnożenia 1 ( prawda ) i 0 ( fałsz ).
- Logiczne LUB jest podobne do znajdowania maksimum spośród 1 ( prawda ) i 0 ( fałsz ).
- Do bitowej negacji liczby całkowitej a używany jest operator
~a
. - Aby logicznie zanegować wyrażenie logiczne a, użyj operatora
!a
. - Liczby ujemne są przechowywane i przetwarzane w kodzie uzupełnienia do dwóch.
- Bitowe przesunięcie w prawo może,
>>
ale nie musi, zachować znak (>>>
).
GO TO FULL VERSION