purebasic.info

PureBasic forum
Текущее время: Ср сен 19, 2018 4:51 am

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 12:42 am 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
Функция как то криво сканирует или сравнивает строки.
Достаточно медленно если искомая строка увеличивается в размере! :roll:


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 3:45 am 
Не в сети
профессор
Аватар пользователя

Зарегистрирован: Чт июн 25, 2009 1:02 pm
Сообщений: 2285
Откуда: Russia — Belarus
Благодарил (а): 6 раз.
Поблагодарили: 72 раз.
Пункты репутации: 11
Пример?

_________________
EnableExplicit User


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 7:23 pm 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
Вот к примеру если искомая строка состоит из 250символов.
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#size=50000000
Global stroca${#size*2}
 
For i=@stroca$ To @stroca$+(#size*2) Step 2
  PokeS(i,"5")
Next
PokeS(@stroca$+(#size*2),"5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666")
 
Procedure Poisktexta(string.s,string1.s,poziciy.i);если найдено возвращает начальный адрес искомых символов или позицию
Protected r.i=MemoryStringLength(@string1)*2  
Protected p.i=(@string+MemoryStringLength(@string)*2)-r
Protected j.i
For j=@string+(poziciy*2) To p Step 2
If CompareMemory(j,@string1,r):ProcedureReturn (j-@string)/2+1:EndIf;adfes pamyti [j] ili poziziy nahala stroki (j-@string)/2+1  
Next
ProcedureReturn 0
EndProcedure
MessageRequester("", "Начать поиск!")
time = GetTickCount_()
poziciy = Poisktexta(stroca$,"5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666",1)
t.d = (GetTickCount_() - time)/1000
MessageRequester("", "Количество символов=" + Str(Len(stroca$)) + #CR$ + "poziciy = " + Str(poziciy) + #CR$ + "Время поиска = " +t)
 
MessageRequester("", "Начать поиск!")
time = GetTickCount_()
poziciy = FindString(stroca$,"5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666",1)
t.d = (GetTickCount_() - time)/1000
MessageRequester("", "Количество символов=" + Str(Len(stroca$)) + #CR$ + "poziciy = " + Str(poziciy) + #CR$ + "Время поиска = " + t)



Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 7:26 pm 
Не в сети
PureBasic Coder
Аватар пользователя

Зарегистрирован: Чт ноя 10, 2011 10:50 am
Сообщений: 4049
Откуда: Ростов-на-Дону
Благодарил (а): 70 раз.
Поблагодарили: 81 раз.
Пункты репутации: 24
PureBasic 5.20.

Всё время ошибка - "Отлаживаемое приложение неожиданно завершило работу". Библиотека Onerror не работает.

P.S. Пошагово выполнил программу - падает в 7 строке

Вот так не падает:
Код:
1
2
3
4
For i=@stroca$ To @stroca$+(#size*2)-500 Step 2
  PokeS(i,"5")
Next
PokeS(@stroca$+(#size*2)-500,"555555555555.....")


_________________
Пурик - лучший язык программирования


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 7:39 pm 
Не в сети
PureBasic Coder
Аватар пользователя

Зарегистрирован: Чт ноя 10, 2011 10:50 am
Сообщений: 4049
Откуда: Ростов-на-Дону
Благодарил (а): 70 раз.
Поблагодарили: 81 раз.
Пункты репутации: 24
И вообще, функция очень быстрая! Пример:
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Procedure$ FillString(Symbol$, Lenght)
  Protected String$
  ProcedureReturn RSet(String$, Lenght, Symbol$)
EndProcedure
 
str1$ = FillString("5", 500000000) ;Строка 500000000 символов (первоначальная)
str2$ = FillString("5", 5000) ;Строка 5000 символов (искомая)
 
time = GetTickCount_()
pos = FindString(str1$, str2$)
time = GetTickCount_() - time
 
Debug "Позиция " + Str(pos)
Debug "Время " + Str(time) + " мс"


Ищет строку размеров 5000 символов в строке 500 000 000 (500 млн) символов. У меня результаты:
Цитата:
Позиция 1
Время 328 мс
Достаточно быстро 8)

_________________
Пурик - лучший язык программирования


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 7:49 pm 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
Никита Однороб писал(а):
PureBasic 5.20.

Всё время ошибка - "Отлаживаемое приложение неожиданно завершило работу". Библиотека Onerror не работает.

P.S. Пошагово выполнил программу - падает в 7 строке

Вот так не падает:
Код:
1
2
3
4
For i=@stroca$ To @stroca$+(#size*2)-500 Step 2
  PokeS(i,"5")
Next
PokeS(@stroca$+(#size*2)-500,"555555555555.....")



У меня пурик 5.11 а в этой строке нет никаких -500 а просто добавляются символы в канец строки для различия и 5 там ненужны просто оставь пять 6-ок.получается что у меня дабавка в конце за пределлами выделенного $stroca{}
Вцелом думаю понятно что с увеличением символов findstring отстаёт в поиске Вот ещё пример
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
Procedure Poisktexta(string.s,string1.s,poziciy.i);если найдено возвращает начальный адрес искомых символов или позицию
Protected r.i=MemoryStringLength(@string1)*2  
Protected p.i=(@string+MemoryStringLength(@string)*2)-r
Protected j.i
For j=@string+(poziciy*2) To p Step 2
If CompareMemory(j,@string1,r):ProcedureReturn (j-@string)/2+1:EndIf;adfes pamyti [j] ili poziziy nahala stroki (j-@string)/2+1  
Next
ProcedureReturn 0
EndProcedure
 
Procedure main()
OpenWindow(0, 0, 0, 1000, 700, "поиск",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
EditorGadget(11,0,26,1000,680)
EditorGadget(904,0,0,300,25)
EditorGadget(907,350,0,300,25)
CreateMenu(0,WindowID(0))
MenuItem(1,"загрузка из буфера")
MenuItem(2,"ptexta")
MenuItem(3,"findstring")
EndProcedure
main()
 
#size=50000000
Global stroca${#size*2}
 
For i=@stroca$ To @stroca$+(#size*2) Step 2
 PokeS(i,"5")
Next
PokeS(@stroca$+(#size*2),"66666")
SetGadgetText(11,stroca$)
stroca$=""
For i=0 To 255
stroca$+"5"
Next
stroca$+"66666"
SetGadgetText(904,stroca$)
 
Repeat
  Event= WaitWindowEvent()
  If Event=#PB_Event_CloseWindow
      End  
  EndIf
 If Event=#PB_Event_Menu
      Menu=EventMenu()
         Select Menu
           Case 1
           time = GetTickCount_()
           SetGadgetText(11,GetClipboardText())
           t.d  = (GetTickCount_() - time)/1000
           MessageRequester("загрузка","загрузка"+": "+t+" секунд" ,#MB_ICONASTERISK)
             
              Case 2
               time = GetTickCount_()  
               SetGadgetText(907,Str(Poisktexta(GetGadgetText(11),GetGadgetText(904),1)))  
               t.d  = (GetTickCount_() - time)/1000
               MessageRequester("загрузка","загрузка"+": "+t+" секунд" ,#MB_ICONASTERISK)
               
               Case 3
               time = GetTickCount_()
               SetGadgetText(907,Str(FindString(GetGadgetText(11),GetGadgetText(904))))
               t.d  = (GetTickCount_() - time)/1000
               MessageRequester("загрузка","загрузка"+": "+t+" секунд" ,#MB_ICONASTERISK)
         EndSelect
  EndIf        
ForEver
End
 



Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Ср дек 04, 2013 7:59 pm 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
Никита Однороб писал(а):
И вообще, функция очень быстрая! Пример:
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Procedure$ FillString(Symbol$, Lenght)
  Protected String$
  ProcedureReturn RSet(String$, Lenght, Symbol$)
EndProcedure
 
str1$ = FillString("5", 500000000) ;Строка 500000000 символов (первоначальная)
str2$ = FillString("5", 5000) ;Строка 5000 символов (искомая)
 
time = GetTickCount_()
pos = FindString(str1$, str2$)
time = GetTickCount_() - time
 
Debug "Позиция " + Str(pos)
Debug "Время " + Str(time) + " мс"


Ищет строку размеров 5000 символов в строке 500 000 000 (500 млн) символов. У меня результаты:
Цитата:
Позиция 1
Время 328 мс
Достаточно быстро 8)


Попробуй не тупо "5"-ки искать а в конце добавить пару 6-ок чтобы поиск шёл до конца файла!


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Чт апр 17, 2014 8:50 pm 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
На fasm по моему алгоритму который выше в примере :D
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
Global r.s="588888888888888888888888888888888888888888888888888888888888888888888888888123456789088888888888888888888888888888855.555555555555555555555555555555555555555555555555555555555555555555555999999999999999999999999999995555555555555555555555"
Procedure poisktexta(s.i,s1.s)
!mov ax,0h
!mov edx,1
!mov esi,[p.v_s]
!mov edi,[p.v_s1]
!mov bx,[edi]
;!cld;счётчик в 0
!Ls45:
!CMP ax,[esi];проверка на конец строки
!je exit3
!CMP bx,[esi]
!je prosmotr
!
!vozvrat:
!add esi,2
!add edx,1
!jmp Ls45
!exit3:
ProcedureReturn 0
!prosmotr:
!push esi
!push edi
!zikl:
!add esi,2
!add edi,2
!CMP ax,[esi];проверка на конец строки=искомая не найдена
!je exit5
!CMP ax,[edi];это конец искомой строки,значит найдена
!je naidena
!mov bx,[edi]
!CMP bx,[esi]
!je zikl;символ равeн смотрим следующий
;
!POP edi
!POP esi
!mov bx,[edi];загружаем начаоьное символ искомой строки
!jmp vozvrat;не равен возвращаемся и смотрим с следующего слова
!exit5:
!POP edi
!POP esi
!jmp exit3
;
!naidena:
!POP edi
!POP esi
!mov eax,edx
ProcedureReturn
Debug poisktexta(@r,"1234567890")
Debug FindString(r,"1234567890",10)
   time = GetTickCount_()
   For q=0 To 1000000
   FindString(r,"1234567890",10)
   Next
   t.d = (GetTickCount_() - time)/1000
   MessageRequester("", "Количество  циклов=" + q + #CR$ + "Время теста= " + t)
   ;
   time = GetTickCount_()
   For q=0 To 1000000
   poisktexta(@r,"1234567890")
   Next
   t.d = (GetTickCount_() - time)/1000
   MessageRequester("", "Количество  циклов=" + q + #CR$ + "Время теста= " + t)
 



Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Пт апр 18, 2014 6:13 pm 
Не в сети
профессор
Аватар пользователя

Зарегистрирован: Пт янв 27, 2012 7:12 pm
Сообщений: 880
Откуда: Кустанай
Благодарил (а): 27 раз.
Поблагодарили: 13 раз.
Пункты репутации: 7
Я как то писал парсер, он собирал адреса сайтов, чекал их на повторы "FindString", когда сайтов в списке становилось много, то проверка нового адреса начинала занимать пол секунды. Если речь шла о проверке одного адреса, то это не критично, но в моём случае надо было добавлять десятки тысяч новых сайтов, что занимало несколько часов/суток. Я не стал заморачиваться, просто стал чекать не "FindString", а перешёл на регулярные выражения, там был стабильный результат в 32 млс (я могу и путать время, но было намного быстрее).

_________________
Детям нельзя в интернет. От детей интернет тупеет.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Пт апр 18, 2014 9:08 pm 
Не в сети
МОДЕРАТОР
Аватар пользователя

Зарегистрирован: Пн апр 09, 2007 4:53 pm
Сообщений: 11255
Благодарил (а): 4 раз.
Поблагодарили: 429 раз.
Тут написано в чем причина медленной работы. http://www.purebasic.fr/english/viewtop ... =3&t=58892

_________________
Компьютер позволяет решать все те проблемы, которые до его изобретения не существовали. :) :)


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Пт апр 18, 2014 11:43 pm 
Не в сети
профессор

Зарегистрирован: Пн июл 22, 2013 11:00 pm
Сообщений: 571
Благодарил (а): 2 раз.
Поблагодарили: 32 раз.
Пункты репутации: 9
С выражениями пока не пробывал вы бы пример какой показали
Так там вроде тоже пишут что медленно работает.
Переделал не много.
Может как плюс,ненужно указывать длинну или возвращать адрес памяти будет ещё побыстрее:roll:
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
Global r.s="588888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888881234567890"
Procedure poisktexta3(s.i,s1.s)
!mov eax,1
!mov esi,[p.v_s]
!mov edi,[p.v_s1]
!mov word bx,[edi]
!hj6:
!CMP word [esi],0h;проверка на конец строки
!je exitt4
!CMP bx,word [esi]
!je prosmottr4
!vozvratt4:
!add esi,2
!add eax,1
!jmp hj6
!exitt4:
ProcedureReturn 0
!prosmottr4:
!push esi
!push edi
!zikt4:
!add esi,2
!add edi,2
!CMP word [edi],0h;это конец искомой строки,значит найдена
!je naidenta4
!CMP word [esi],0h;проверка на конец строки=искомая не найдена
!je exitt45
!mov word bx,[edi]
!CMP bx,word [esi]
!je zikt4;символ равeн смотрим следующий
!POP edi
!POP esi
!mov bx,[edi];загружаем начальный символ искомой строки
!jmp vozvratt4;не равен возвращаемся и смотрим с следующего символа
!exitt45:
!POP edi
!POP esi
!jmp exitt4
!naidenta4:
!POP edi
!POP esi
ProcedureReturn
EndProcedure
Debug poisktexta3(@r,"1234567890")
Debug FindString(r,"1234567890",10)
   time = GetTickCount_()
   For q=0 To 100000
   poisktexta3(@r,"1234567890")  
   Next
   t.d = (GetTickCount_() - time)/1000
   MessageRequester("", "Количество  циклов=" + q + #CR$ + "Время теста= " + t)
   ;
   time = GetTickCount_()
   For q=0 To 100000
   FindString(r,"1234567890",10)
   Next
   t.d = (GetTickCount_() - time)/1000
   MessageRequester("", "Количество  циклов=" + q + #CR$ + "Время теста= " + t)
 
 



Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Сб апр 19, 2014 12:32 pm 
Не в сети
профессор
Аватар пользователя

Зарегистрирован: Пт янв 27, 2012 7:12 pm
Сообщений: 880
Откуда: Кустанай
Благодарил (а): 27 раз.
Поблагодарили: 13 раз.
Пункты репутации: 7
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
text.s = "1234567890"
;собираем длинную строку 1200000 символов включая перенос
For i=1 To 25
  text.s = text.s+text.s+#CRLF$
Next i
 
Debug Len(text.s)
 
start = ElapsedMilliseconds()
FindString(text.s, "1234567890", 0)
result.s = "Findstring с стартовой позицией  -  "+Str(ElapsedMilliseconds()-start)+#CRLF$
 
start = ElapsedMilliseconds()
FindString(text.s, "1234567890")
result.s = result.s + "FindString без стартовой позиции  -  "+Str(ElapsedMilliseconds()-start)+#CRLF$
 
regxt = CreateRegularExpression(#PB_Any, "ssssssssss")
If IsRegularExpression(regxt)=0
  Debug "облом"
EndIf
start = ElapsedMilliseconds()
MatchRegularExpression(regxt, text.s)
result.s = result.s + "Регулярное выражение - "+Str(ElapsedMilliseconds()-start)+#CRLF$
 
Dim res.s(0)
start = ElapsedMilliseconds()
ExtractRegularExpression(regxt, text.s, res())
result.s = result.s + "Регулярное выражение - "+Str(ElapsedMilliseconds()-start)+#CRLF$
 
MessageRequester("", result.s)
 



Я так юмора и не понял, теперь у меня регулярные выражения работают дольше, чем FindString, точно помню, что раньше было наоборот, но после этого я сменил комп на более мощный и версия пурика изменилась на 5,11, а вот какая была, когда писал парсер, уже не помню.

_________________
Детям нельзя в интернет. От детей интернет тупеет.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Сб апр 19, 2014 1:27 pm 
Не в сети
лентяй ужасный
Аватар пользователя

Зарегистрирован: Вс фев 27, 2011 4:23 pm
Сообщений: 646
Благодарил (а): 4 раз.
Поблагодарили: 5 раз.
На дворе уже 2014, а люди все еще используют ElapsedMilliseconds/GetTickCount :)
Если и до этого так мерил, то результат надо писать не как 32 мкс, а 32 ±15 мкс, ибо погрешность..


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Сб апр 19, 2014 1:52 pm 
Не в сети
профессор
Аватар пользователя

Зарегистрирован: Пт янв 27, 2012 7:12 pm
Сообщений: 880
Откуда: Кустанай
Благодарил (а): 27 раз.
Поблагодарили: 13 раз.
Пункты репутации: 7
newww писал(а):
На дворе уже 2014, а люди все еще используют ElapsedMilliseconds/GetTickCount :)
Если и до этого так мерил, то результат надо писать не как 32 мкс, а 32 ±15 мкс, ибо погрешность..

Ну а что вы предлагаете?
Я как то отстранился от программирования, справку переводить лень, что я упустил в этом плане?

_________________
Детям нельзя в интернет. От детей интернет тупеет.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Алгоритм Функции FindString
СообщениеДобавлено: Сб апр 19, 2014 2:19 pm 
Не в сети
лентяй ужасный
Аватар пользователя

Зарегистрирован: Вс фев 27, 2011 4:23 pm
Сообщений: 646
Благодарил (а): 4 раз.
Поблагодарили: 5 раз.
Ну если с QueryPerformanceCounter не охото возиться, то хотя бы timeGetTime_()

Проверьте эти два кода, думаю разница не только у меня видна..
Код:
1
2
3
4
5
6
7
 
For i=0 To 30
  Time = ElapsedMilliseconds()
  Delay(39)
  Debug ElapsedMilliseconds()-Time
Next
 



Код:
1
2
3
4
5
6
7
 
For i=0 To 30
  Time = timeGetTime_()
  Delay(39)
  Debug timeGetTime_()-Time
Next
 



Вернуться наверх
 Профиль  
 
Показать сообщения за:  Сортировать по:  
Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group (блог о phpBB)
Сборка создана CMSart Studio
Русская поддержка phpBB