Is it possible to iterate a masked enum in objective c - objective-c

I've seen that this is possible in other languages but need something like this in objective-c
I have an enum similar to this
typedef enum {
option1 = 1 << 0,
option2 = 1 << 1,
option3 = 1 << 2
...
...
} SomePossibleOptions;
and then a user can create a mask of the wanted options
SomePossibleOptions myOptions = option1 | option2;
[self.someObject performOperationsForOptions:myOptions];
-(void)performOperationsForOptions:(SomePossibleOptions)theOptions
{
if (myOptions & option1)
{
// do something
}
if (myOptions & option2
{
// do something
}
//(could use a switch statement)
}
But would much rather use some sort of syntax
foreach (option in myoption)
{
//do something
}

Sometimes I use a last value in my normal enums called "SomeEnumCount", which then has exactly the number of items I have in the enum, so I can make a loop for that.
In your case it would be something like this:
typedef enum {
option1 = 1 << 0,
option2 = 1 << 1,
option3 = 1 << 2,
...
...
optionCount = 1 << n
} SomePossibleOptions;
Or maybe you can call it OptionNone, if you have one like that, and that would be always the last one.
And to make a loop you have to make something like this
NSInteger optionsCount = (int)log2(optionCount);
for (NSInteger i = 0; i < optionsCount; i++) {
SomePossibleOptions option = (SomePossibleOptions)(1 << i);
//handle your options here
}
I hope it helps!
EDIT: Maybe I misunderstood the question. If you want to loop on just the options that are masked together, you should write a function, based on the above. Something like:
- (NSArray *)optionsInMask:(SomePossibleOptions)maskedOptions {
NSMutableArray * options = [NSMutableArray array];
NSInteger optionsCount = (int)log2(optionCount);
for (NSInteger i = 0; i < optionsCount; i++) {
SomePossibleOptions option = (SomePossibleOptions)(1 << i);
if (maskedOptions & option) {
[options addObject:[NSValue valueWithInteger:option]];
}
}
return [NSArray arrayWithArray:options];
}
And then you can loop it like:
for (NSValue * value in [self optionsInMask:myOptions]) {
SomePossibleOption option = (SomePossibleOptions)[value integerValue];
//your code here
}

Related

How to add JSpinner output into array and check for duplicate

I'm working on a code which has a JSpinner, which gives an output and moves it to int = n, I want to save every JSpinner output and add it into an array to check for duplicates and once found the JPanel should close itself or say you lost.
Scanner s = new Scanner(System.in);
int n = (Integer) spinner.getValue();
if (isPrime(n)) {
Input.setText(n + " is a prime number");
score++;
Highscore.setText("Score: " + score);
int[] array = new int[4];
for(int i=0; i<4;i++)
{
array[i]= (Integer) spinner.getValue();
}
for (int i=0; i < 4;i++)
{
System.out.println(array[i]);
}
Spinner output into array but it fills the whole array with one number example: [3,3,3,3].
private <T> boolean duplicate(T... array)
{
for (int i = 0; i < array.length; i++)
{
for (int j = i + 1; j < array.length; j++)
{
if (array[i] != null && array[i].equals(array[j])) {
return true;
dispose();
}
}
}
return false;
}
Duplicate check
I tried adding the user input from the JSpinner into an array and from there to check for duplicates.
Searched Online but could'nt find anything.
This if my first post so if you need anything more you can tell me.

Pset5 (Speller) Weird Valgrind memory errors, no leaks

I have read other threads on pset5 Valgrind memory errors, but that didn't help me. I get 0 leaks, but this instead:
==1917== Conditional jump or move depends on uninitialised value(s)
Looks like you're trying to use a variable that might not have a value? Take a closer look at line 34 of dictionary.c.
The error refers to line 34 which is this: lower[i] = tolower(word[i]);
To supply context, the code below attempts to check if a word exists in the dictionary that has been uploaded to a hash table. I am attempting to convert the wanted word to lowercase because all the dictionary words are also lowercase and so that their hashes would be identical. The program successfully completes all tasks, but then stumbles upon these memory errors.
Any hints as to why Valgrind is mad at me? Thank you!
// Returns true if word is in dictionary else false
bool check(const char *word)
{
char lower[LENGTH + 1];
//Converts word to lower so the hashes of the dictionary entry and searched word would match
for (int i = 0; i < LENGTH + 1; i++)
{
lower[i] = tolower(word[i]);
}
// Creates node from the given bucket
node *tmp = table[hash(lower)];
// Traverses the linked list
while (tmp != NULL)
{
if (strcasecmp(word, tmp->word) == 0)
{
return true;
}
tmp = tmp->next;
}
return false;
}
Below is the whole dictionary.c file:
// Implements a dictionary's functionality
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table 26^3
const unsigned int N = 17576;
// Hash table
node *table[N];
int count = 0;
// Returns true if word is in dictionary else false
bool check(const char *word)
{
char lower[LENGTH + 1];
//Converts word to lower so the hashes of the dictionary entry and searched word would match
for (int i = 0; i < LENGTH + 1; i++)
{
lower[i] = tolower(word[i]);
}
// Creates node from the given bucket
node *tmp = table[hash(lower)];
// Traverses the linked list
while (tmp != NULL)
{
if (strcasecmp(word, tmp->word) == 0)
{
return true;
}
tmp = tmp->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Modified hash function by Dan Berstein taken from http://www.cse.yorku.ca/~oz/hash.html
unsigned int hash = 5381;
int c;
while ((c = *word++))
{
hash = (((hash << 5) + hash) + c) % N; /* hash * 33 + c */
}
return hash;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
FILE *inptr = fopen(dictionary, "r");
if (dictionary == NULL)
{
printf("Could not load %s\n.", dictionary);
return false;
}
// Create a char array to temporarily hold the new word (r stands for read)
char r_word[N+1];
// Until the end of file
while (fscanf(inptr, "%s", r_word) != EOF)
{
// Increments count
count++;
// Create a node
node *new_node = malloc(sizeof(node));
if (new_node == NULL)
{
unload();
return false;
}
strcpy(new_node->word, r_word);
// Hash the node
int index = hash(new_node->word);
// Places the node at the right index
new_node->next = table[index];
table[index] = new_node;
}
fclose(inptr);
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
if (&load == false)
{
return '0';
}
else
{
return count;
}
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
// Interates over the array
for (int i = 0; i < N; i++)
{
node *head = table[i];
while (head != NULL)
{
node *tmp = head;
head = head->next;
free(tmp);
}
}
return true;
}
This loop iterates through the maximum length of word-
for (int i = 0; i < LENGTH + 1; i++)
{
lower[i] = tolower(word[i]);
}
Except if you look at how word is created-
while (fscanf(inptr, "%s", r_word) != EOF)
{
// Increments count
count++;
// Create a node
node *new_node = malloc(sizeof(node));
if (new_node == NULL)
{
unload();
return false;
}
strcpy(new_node->word, r_word);
Notice, the variable r_word, may not be exactly of length LENGTH + 1. So what you really have in word is N number of characters, where N is not necessarily LENGTH + 1, it could be less.
So looping over the entire 0 -> LENGTH + 1 becomes problematic for words that are shorter than LENGTH + 1. You're going over array slots that do not have a value, they have garbage values.
What's the solution? This is precisely why c strings have \0-
for (int i = 0; word[i] != '\0'; i++)
{
lower[i] = tolower(word[i]);
}
This will stop the loop as soon as the NULL character is reached, which, you must have already learnt, marks the end of a string - aka a char array.
There may still be more errors in your code. But for your particular question - reading out of bounds is the answer.

How to check that two format strings are compatible?

Examples:
"Something %d" and "Something else %d" // Compatible
"Something %d" and "Something else %f" // Not Compatible
"Something %d" and "Something %d else %d" // Not Compatible
"Something %d and %f" and "Something %2$f and %1$d" // Compatible
I figured there should be some C function for this, but I'm not getting any relevant search results. I mean the compiler is checking that the format string and the arguments match, so the code for checking this is already written. The only question is how I can call it.
I'm using Objective-C, so if there is an Objective-C specific solution that's fine too.
Checking if 2 printf() format strings are compatible is an exercise in format parsing.
C, at least, has no standard run-time compare function such as:
int format_cmp(const char *f1, const char *f2); // Does not exist
Formats like "%d %f" and "%i %e" are obviously compatible in that both expect an int and float/double. Note: float are promoted to double as short and signed char are promoted to int.
Formats "%*.*f" and "%i %d %e" are compatible, but not obvious: both expect an int,int and float/double.
Formats "%hhd" and "%d" both expect an int, even though the first will have it values cast to signed char before printing.
Formats "%d" and "%u" are not compatible. Even though many systems will behaved as hoped. Note: Typically char will promote to int.
Formats "%d" and "%ld" are not strictly compatible. On a 32-bit system there are equivalent, but not in general. Of course code can be altered to accommodate this. OTOH "%lf" and "%f" are compatible due to the usual argument promotions of float to double.
Formats "%lu" and "%zu" may be compatible, but that depends on the implementation of unsigned long and size_t. Additions to code could allow this or related equivalences.
Some combinations of modifiers and specifiers are not defined like "%zp". The following does not dis-allow such esoteric combinations - but does compare them.
Modifiers like "$" are extensions to standard C and are not implemented in the following.
The compatibility test for printf() differs from scanf().
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
typedef enum {
type_none,
type_int,
type_unsigned,
type_float,
type_charpointer,
type_voidpointer,
type_intpointer,
type_unknown,
type_type_N = 0xFFFFFF
} type_type;
typedef struct {
const char *format;
int int_queue;
type_type type;
} format_T;
static void format_init(format_T *state, const char *format);
static type_type format_get(format_T *state);
static void format_next(format_T *state);
void format_init(format_T *state, const char *format) {
state->format = format;
state->int_queue = 0;
state->type = type_none;
format_next(state);
}
type_type format_get(format_T *state) {
if (state->int_queue > 0) {
return type_int;
}
return state->type;
}
const char *seek_flag(const char *format) {
while (strchr("-+ #0", *format) != NULL)
format++;
return format;
}
const char *seek_width(const char *format, int *int_queue) {
*int_queue = 0;
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char ) *format))
format++;
}
if (*format == '.') {
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char ) *format))
format++;
}
}
return format;
}
const char *seek_mod(const char *format, int *mod) {
*mod = 0;
if (format[0] == 'h' && format[1] == 'h') {
format += 2;
} else if (format[0] == 'l' && format[1] == 'l') {
*mod = ('l' << CHAR_BIT) + 'l';
format += 2;
} else if (strchr("ljztL", *format)) {
*mod = *format;
format++;
} else if (strchr("h", *format)) {
format++;
}
return format;
}
const char *seek_specifier(const char *format, int mod, type_type *type) {
if (strchr("di", *format)) {
*type = type_int;
format++;
} else if (strchr("ouxX", *format)) {
*type = type_unsigned;
format++;
} else if (strchr("fFeEgGaA", *format)) {
if (mod == 'l') mod = 0;
*type = type_float;
format++;
} else if (strchr("c", *format)) {
*type = type_int;
format++;
} else if (strchr("s", *format)) {
*type = type_charpointer;
format++;
} else if (strchr("p", *format)) {
*type = type_voidpointer;
format++;
} else if (strchr("n", *format)) {
*type = type_intpointer;
format++;
} else {
*type = type_unknown;
exit(1);
}
*type |= mod << CHAR_BIT; // Bring in modifier
return format;
}
void format_next(format_T *state) {
if (state->int_queue > 0) {
state->int_queue--;
return;
}
while (*state->format) {
if (state->format[0] == '%') {
state->format++;
if (state->format[0] == '%') {
state->format++;
continue;
}
state->format = seek_flag(state->format);
state->format = seek_width(state->format, &state->int_queue);
int mod;
state->format = seek_mod(state->format, &mod);
state->format = seek_specifier(state->format, mod, &state->type);
return;
} else {
state->format++;
}
}
state->type = type_none;
}
// 0 Compatible
// 1 Not Compatible
// 2 Not Comparable
int format_cmp(const char *f1, const char *f2) {
format_T state1;
format_init(&state1, f1);
format_T state2;
format_init(&state2, f2);
while (format_get(&state1) == format_get(&state2)) {
if (format_get(&state1) == type_none)
return 0;
if (format_get(&state1) == type_unknown)
return 2;
format_next(&state1);
format_next(&state2);
}
if (format_get(&state1) == type_unknown)
return 2;
if (format_get(&state2) == type_unknown)
return 2;
return 1;
}
Note: only minimal testing done. Lots of additional considerations could be added.
Known shortcomings: hh,h,l,ll,j,z,t modifiers with n. l with s,c.
[Edit]
OP comments about security concerns. This changes the nature of the post and the compare from an equality one to a security one. I'd imagine that one of the patterns (A) would be a reference pattern and the next (B) would be the test. The test would be "is B at least as secure as A?". Example A = "%.20s" and B1 = "%.19s", B2 = "%.20s", B3 = "%.21s". B1 and B2 both pass the security test as they do not extract more the 20 char. B3 is a problem as it goes pass the reference limit of 20 char. Further any non-width qualified with %s %[ %c is a security problem - in the reference or test pattern. This answer's code does not address this issue.
As mentioned, code does not yet handle modifiers with "%n".
[2018 edit]
Concerning "Formats "%d" and "%u" are not compatible.": This is for values to be printed in general. For values in the [0..INT_MAX] range, either format may work per C11dr ยง6.5.2.2 6.
My understanding of what you want, is that, you basically want a method which can look at two strings and detect if they both have the same types of values in them. Or something a long those lines.... If so, then try this (or something along the lines of this):
-(int)checkCompatible:(NSString *)string_1 :(NSString *)string_2 {
// Separate the string into single elements.
NSArray *stringArray_1 = [string_1 componentsSeparatedByString:#" "];
NSArray *stringArray_2 = [string_2 componentsSeparatedByString:#" "];
// Store only the numbers for comparison in a new array.
NSMutableArray *numbers_1 = [[NSMutableArray alloc] init];
NSMutableArray *numbers_2 = [[NSMutableArray alloc] init];
// Make sure the for loop below, runs for the appropriate
// number of cycles depending on which array is bigger.
int loopMax = 0;
if ([stringArray_1 count] > [stringArray_2 count]) {
loopMax = (int)[stringArray_1 count];
}
else {
loopMax = (int)[stringArray_2 count];
}
// Now go through the stringArray's and store only the
// numbers in the mutable array's. This will be used
// during the comparison stage.
for (int loop = 0; loop < loopMax; loop++) {
NSCharacterSet *notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if (loop < [stringArray_1 count]) {
if ([[stringArray_1 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) {
// String consists only of the digits 0 through 9.
[numbers_1 addObject:[stringArray_1 objectAtindex:loop]];
}
}
if (loop < [stringArray_2 count]) {
if ([[stringArray_2 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) {
// String consists only of the digits 0 through 9.
[numbers_2 addObject:[stringArray_2 objectAtindex:loop]];
}
}
}
// Now look through the mutable array's
// and perform the type comparison,.
if ([numbers_1 count] != [numbers_2 count]) {
// One of the two strings has more numbers
// than the other, so they are NOT compatible.
return 1;
}
else {
// Both string have the same number of numbers
// numbers so lets go through them to make
// sure the numbers are of the same type.
for (int loop = 0; loop < [numbers_1 count]; loop++) {
// Check to see if the number in the current array index
// is a float or an integer. All the numbers in the array have
// to be the SAME type, in order for the strings to be compatible.
BOOL check_float_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanFloat:nil];
BOOL check_int_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanInt:nil];
BOOL check_float_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanFloat:nil];
BOOL check_int_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanInt:nil];
if (check_float_1 == YES) {
if (check_float_2 == NO) {
return 1;
}
}
else if (check_int_1 == YES) {
if (check_int_2 == NO) {
return 1;
}
}
else {
// Error of some sort......
return 1;
}
}
// All the numbers in the strings are of the same
// type (otherwise we would NOT have reached
// this point). Therefore the strings are compatible.
return 0;
}
}

NSArray -> Find closest value - fastest way

Lets say I've got an ordered NSArray of NSNumbers:
2, 4, 8, 15, 16, 20 // for simplicity let's treat it as array of int's instead of NSNumbers
Now I need to find closest index to let's say value == 19.
searchValue = 19;
minIndex = 0;
maxIndex = array.count - 1;
currentIndex = (int)floorf(maxIndex / 2.f);
while (maxIndex - minIndex == 1) {
if (array[currentIndex] < searchValue) { // go right
minIndex = currentIndex;
} else if (array[currentIndex] > searchValue) { // go left
maxIndex = currentIndex;
} else { // exact value, rather low probability of happening
return currentIndex;
}
currentIndex = (int)floorf((maxIndex - minIndex) / 2.f);
}
// let's check values around, who has smaller difference
int leftDifference = (currentIndex - 1 >= 0) ? abs(array[currentIndex - 1] - searchValue) : INT_MAX;
int rightDifference = (currentIndex + 1 < array.count) ? abs(array[currentIndex + 1] - searchValue) : INT_MAX;
int centralDifference = abs(array[currentIndex] - searchValue);
if (leftDifference < rightDifference && leftDifference < centralDifference) {
return currentIndex - 1;
} else if () {
return currentIndex + 1;
} else {
return currentIndex;
}
This is the fastest way I can imagine, maybe someone has different idea? How can I improve the algorithm?
I've took a look into egSOF question, but it search for value not index and does it by browsing all values. In case of index, we don't have to browse full array.
Lets assume you have an array of NSNumbers:
NSArray *array = #[#(2), #(4), #(8), #(15), #(16), #(20)];
And you are looking for myValue as below:
NSNumber *myValue = #(17);
Use indexOfObject:inSortedRange:options:usingComparator method to find the nearest index of array to you value. Binary search has O(log n) performance so is pretty fast.
NSInteger searchIndex = MIN([array indexOfObject: myValue inSortedRange:NSMakeRange(0, array.count)
options:NSBinarySearchingFirstEqual | NSBinarySearchingInsertionIndex
usingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) {
return [obj1 compare:obj2];
}], [array count] - 1);
Then check if exists a number closest to yours myValue on the searchIndex - 1 index:
if (searchIndex > 0) {
CGFloat leftHandDiff = ABS(((NSNumber *)array[searchIndex - 1]).floatValue - myValue.floatValue);
CGFloat rightHandDiff = ABS(((NSNumber *)array[searchIndex]).floatValue - myValue.floatValue);
if (leftHandDiff == rightHandDiff) {
//here you can add behaviour when your value is in the middle of range
NSLog(#"given value is in the middle");
} else if (leftHandDiff < rightHandDiff) {
searchIndex--;
}
}
NSLog(#"The nearest value to %f is %d in array at index %d", myValue.floatValue, array[searchIndex], searchIndex);
and voila! Now you now the closest value to myValue.
Remember that your array has to be sorted ascending to make this trick.

Mapping elements in an NSArray to their counts and getting the most frequent

I am working on presentation where I am comparing collections and algorithms of standard libraries of different languages. Help me to write effective and readable algorithm of this problem:
Metaprogramming language:
struct Tweet { id, time, text, url, user_id };
struct User { id, name, nick };
array<Tweet> tweets;
map<int, User> userDict;
Problem:
Find the user name who sent the greatest number of tweets with url field not equal to nil.
Also find the count of such tweets from this user.
I have started like this:
NSMutableDictionary * countByUserId = [NSMutableDictionary dictionary];
foreach (Tweet * tweet in tweets)
if (tweet.url != 0)
countByUserId[tweet->user_id] = #(countByUserId[tweet->user_id] + 1);
Now I need to find key-value with max value.
This is my C++ code:
map<int,int> countByUserId;
for (auto it = tweets.begin(); it != tweets.end(); ++it)
if (it->url != 0)
countByUserId[it->user_id]++;
auto para = max_element(countByUserId.begin(),
countByUserId.end(),
[](pair<int,int> a, pair<int,int> b)
{ return a.second < b.second; });
cout << "User = " << userDict[para.first].name << endl
<< "Value = " << para.second << endl;
Now I need to find key-value with max value...
There may be ties, so you should look for all key-value pairs with the max value. You can compute the max in your loop, and then walk through the NSDictionary again, looking for it:
NSMutableDictionary * countByUserId = [NSMutableDictionary dictionary];
NSInteger max = -1;
foreach (Tweet * tweet in tweets) {
if (tweet.url != 0) {
int next = countByUserId[tweet->user_id].intValue + 1;
countByUserId[tweet->user_id] = #(next);
if (next > max) {
max = next;
}
}
}
[countByUserId enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj intValue] == max) {
NSLog(#"Found max for user: %#", key);
}
}